Final Platform Layer 0.9.9-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/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 or C++11 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++ project 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- Initialize 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)) {
78 // ...
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-2025 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
127
128// ----------------------------------------------------------------------------
129// > CHANGELOG
130// ----------------------------------------------------------------------------
1590
1643
1644// ****************************************************************************
1645//
1646// > HEADER
1647//
1648// ****************************************************************************
1649#ifndef FPL_HEADER_H
1650#define FPL_HEADER_H
1651
1652//
1653// C++/C99 detection
1654//
1655// https://en.wikipedia.org/wiki/C99#Version_detection
1656//
1657// @note C99 is supported since MSVC 2015.
1658// @note C11 is supported since MSVC 2019.
1659//
1660#if defined(__cplusplus)
1665# define FPL_IS_CPP
1666
1667# if (__cplusplus >= 201103L)
1672# define FPL_IS_CPP11
1673# endif
1674
1675# if (__cplusplus >= 201402L)
1680# define FPL_IS_CPP14
1681# endif
1682
1683# if (__cplusplus >= 201703L)
1688# define FPL_IS_CPP17
1689# endif
1690
1691# if (__cplusplus >= 202004L)
1696# define FPL_IS_CPP20
1697# endif
1698#elif defined(_MSC_VER)
1699# if (_MSC_VER >= 1900)
1704# define FPL_IS_C99
1705
1706# if (_MSC_VER >= 1920)
1711# define FPL_IS_C11
1712# endif
1713
1714# else
1715 #error "This MSVC compiler does not support C99 or higher!"
1716# endif
1717#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
1722# define FPL_IS_C99
1723
1724# if __STDC_VERSION__ >= 201112L
1729# define FPL_IS_C11
1730# endif
1731#else
1732 #error "This C compiler is not supported!"
1733#endif
1734
1735//
1736// Architecture detection (x86, x64, arm32, arm64, apple arm64, riscv, powerpc, mips, sparc)
1737//
1738// https://sourceforge.net/p/predef/wiki/Architectures/
1739//
1740#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
1745# define FPL_ARCH_X64
1746#elif defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_)
1751# define FPL_ARCH_X86
1752#elif defined(__aarch64__) || defined(_M_ARM64)
1753# if defined(__APPLE__)
1758# define FPL_ARCH_APPLE_ARM64
1759# else
1764# define FPL_ARCH_ARM64
1765# endif
1766#elif defined(__arm__) || defined(_M_ARM) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_7__) || defined(__armv7__)
1771# define FPL_ARCH_ARM32
1772#elif defined(__riscv) || defined(__riscv_xlen)
1777# define FPL_ARCH_RISCV
1778#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || defined(_ARCH_PPC64)
1783# define FPL_ARCH_POWERPC64
1784#elif defined(__powerpc__) || defined(__POWERPC__) || defined(__ppc__) || defined(__PPC__) || defined(_ARCH_PPC)
1789# define FPL_ARCH_POWERPC32
1790#elif defined(__mips__) || defined(__mips64) || defined(_MIPS_ARCH)
1795# define FPL_ARCH_MIPS
1796#elif defined(__sparc__) || defined(__sparcv9)
1801# define FPL_ARCH_SPARC
1802#else
1803# error "This architecture is not supported!"
1804#endif // FPL_ARCH
1805
1806//
1807// 32-bit or 64-bit
1808//
1809#if defined(_WIN32)
1810# if defined(_WIN64)
1811# define FPL__M_CPU_64BIT // 64-bit system
1812# else
1813# define FPL__M_CPU_32BIT // 32-bit system
1814# endif
1815#elif defined(__GNUC__) || defined(__clang__)
1816# if defined(__LP64__) || defined(_LP64)
1817# define FPL__M_CPU_64BIT // 64-bit system
1818# else
1819# define FPL__M_CPU_32BIT // 32-bit system
1820# endif
1821#elif defined(_MSC_VER)
1822# if defined(_WIN64) || defined(_M_X64) || defined(__x86_64__)
1823# define FPL__M_CPU_64BIT // 64-bit system
1824# else
1825# define FPL__M_CPU_32BIT // 32-bit system
1826# endif
1827#elif defined(__APPLE__) || defined(__linux__) || defined(__unix__)
1828# if defined(__x86_64__) || defined(__aarch64__) || defined(__ppc64__)
1829# define FPL__M_CPU_64BIT // 64-bit system
1830# else
1831# define FPL__M_CPU_32BIT // 32-bit system
1832# endif
1833#else
1834# if (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8) || (sizeof(void *) == 8)
1835# define FPL__M_CPU_64BIT // 64-bit system
1836# else
1837# define FPL__M_CPU_32BIT // 32-bit system
1838# endif
1839#endif
1840
1841#if defined(FPL__M_CPU_64BIT)
1846# define FPL_CPU_64BIT
1847#elif defined(FPL__M_CPU_32BIT)
1852# define FPL_CPU_32BIT
1853#endif
1854
1855//
1856// Compiler detection
1857//
1858// http://beefchunk.com/documentation/lang/c/pre-defined-c/precomp.html
1859// http://nadeausoftware.com/articles/2012/10/c_c_tip_how_detect_compiler_name_and_version_using_compiler_predefined_macros
1860//
1861#if defined(__clang__)
1866# define FPL_COMPILER_CLANG
1867#elif defined(__INTEL_COMPILER)
1872# define FPL_COMPILER_INTEL
1873#elif defined(__MINGW32__) || defined(__MINGW64__)
1878# define FPL_COMPILER_MINGW
1879#elif defined(__CC_ARM)
1884# define FPL_COMPILER_ARM
1885#elif defined(__GNUC__)
1890# define FPL_COMPILER_GCC
1891#elif defined(_MSC_VER)
1896# define FPL_COMPILER_MSVC
1897#elif defined(__APPLE__)
1902# define FPL_COMPILER_APPLE_CLANG
1903#elif defined(__BORLANDC__)
1908# define FPL_COMPILER_BORLAND
1909#elif defined(__TCC__)
1914# define FPL_COMPILER_TCC
1915#elif defined(__DMC__)
1920# define FPL_COMPILER_DMC
1921#elif defined(__CSMC__)
1926# define FPL_COMPILER_CSMC
1927#elif defined(__LINARO__)
1932# define FPL_COMPILER_LINARO
1933#else
1934 #error "This compiler is not supported!"
1935#endif // FPL_COMPILER
1936
1937//
1938// Platform detection
1939//
1940// https://sourceforge.net/p/predef/wiki/OperatingSystems/
1941//
1942// Only platforms that are supported are listed
1943//
1944#if defined(_WIN32) || defined(_WIN64)
1945# define FPL_PLATFORM_WINDOWS
1946# define FPL_PLATFORM_NAME "Windows"
1947#elif defined(__ANDROID__)
1948# define FPL_PLATFORM_ANDROID
1949# define FPL_PLATFORM_NAME "Android"
1950# define FPL_SUBPLATFORM_POSIX
1951# define FPL_SUBPLATFORM_STD_STRINGS
1952# define FPL_SUBPLATFORM_STD_CONSOLE
1953#elif defined(__linux__) || defined(__gnu_linux__)
1954# define FPL_PLATFORM_LINUX
1955# define FPL_PLATFORM_NAME "Linux"
1956# define FPL_SUBPLATFORM_POSIX
1957# define FPL_SUBPLATFORM_X11
1958# define FPL_SUBPLATFORM_STD_STRINGS
1959# define FPL_SUBPLATFORM_STD_CONSOLE
1960#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__bsdi__)
1961 // @NOTE(final): BSD is treated as a subplatform for now
1962# define FPL_PLATFORM_UNIX
1963# define FPL_PLATFORM_NAME "BSD"
1964# define FPL_SUBPLATFORM_BSD
1965# define FPL_SUBPLATFORM_POSIX
1966# define FPL_SUBPLATFORM_X11
1967# define FPL_SUBPLATFORM_STD_STRINGS
1968# define FPL_SUBPLATFORM_STD_CONSOLE
1969#elif defined(unix) || defined(__unix) || defined(__unix__)
1970# define FPL_PLATFORM_UNIX
1971# define FPL_PLATFORM_NAME "Unix"
1972# define FPL_SUBPLATFORM_POSIX
1973# define FPL_SUBPLATFORM_X11
1974# define FPL_SUBPLATFORM_STD_STRINGS
1975# define FPL_SUBPLATFORM_STD_CONSOLE
1976#else
1977# error "This platform is not supported!"
1978#endif // FPL_PLATFORM
1979
1980//
1981// Assembler keyword is compiler specific
1982//
1983#if defined(FPL_COMPILER_CLANG) || defined(FPL_COMPILER_GCC)
1984# define fpl__m_Asm __asm__
1985#elif defined(FPL_COMPILER_MSVC)
1986# define fpl__m_Asm __asm
1987#else
1988# define fpl__m_Asm asm
1989#endif
1990
1995#define fplAsm fpl__m_Asm
1996
1997//
1998// Minimum alignment
1999//
2000#if defined(FPL_COMPILER_MSVC)
2001# define fpl__MinAlignment 8
2002#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
2003# if defined(FPL_CPU_64BIT)
2004# define fpl__MinAlignment 8
2005# else
2006# define fpl__MinAlignment 4
2007# endif
2008#else
2009# define fpl__MinAlignment 8
2010#endif
2011
2017#define fplMinAlignment fpl__MinAlignment
2018
2019//
2020// Alignment keyword
2021//
2022#if defined(FPL_IS_CPP11)
2023# define fpl__m_AlignAs(N) alignas(N)
2024#elif defined(FPL_COMPILER_MSVC)
2025# define fpl__m_AlignAs(N) __declspec(align(N))
2026#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
2027# define fpl__m_AlignAs(N) __attribute__((aligned(N)))
2028#else
2029# define fpl__m_AlignAs(N)
2030#endif
2031
2038#define fplAlignAs(N) fpl__m_AlignAs(((N) < fplMinAlignment ? fplMinAlignment : (N)))
2039//
2040// Defines required for POSIX (mmap, 64-bit file io, etc.)
2041//
2042#if defined(FPL_SUBPLATFORM_POSIX)
2043# if !defined(_XOPEN_SOURCE)
2044# define _XOPEN_SOURCE 600
2045# endif
2046# if !defined(_DEFAULT_SOURCE)
2047# define _DEFAULT_SOURCE 1
2048# endif
2049# if !defined(__STDC_FORMAT_MACROS)
2050# define __STDC_FORMAT_MACROS
2051# endif
2052# if !defined(__STDC_LIMIT_MACROS)
2053# define __STDC_LIMIT_MACROS
2054# endif
2055# if !defined(_LARGEFILE_SOURCE)
2056# define _LARGEFILE_SOURCE
2057# endif
2058# if !defined(_LARGEFILE64_SOURCE)
2059# define _LARGEFILE64_SOURCE
2060# endif
2061# if !defined(_FILE_OFFSET_BITS)
2062# define _FILE_OFFSET_BITS 64
2063# endif
2064#endif
2065
2066#if defined(FPL_PLATFORM_LINUX)
2067# define FPL__INCLUDE_ALLOCA
2068#else
2069# define FPL__INCLUDE_MALLOC
2070#endif
2071
2072// MingW compiler hack
2073#if defined(FPL_PLATFORM_WINDOWS) && defined(FPL_COMPILER_MINGW)
2074# if !defined(_WIN32_WINNT)
2075# define _WIN32_WINNT 0x0600
2076# endif
2077#endif // FPL_COMPILER_MINGW
2078
2079//
2080// Storage class identifiers
2081//
2082
2088
2090#define fpl_globalvar static
2092#define fpl_localvar static
2094#define fpl_internal static
2096#define fpl_inline inline
2098#define fpl_internal_inline inline
2100#if defined(FPL_IS_CPP)
2101# define fpl_extern
2102#else
2103# define fpl_extern extern
2104#endif
2105
2106//
2107// DLL Export/Import definition
2108//
2109#if defined(_WIN32) || defined(__CYGWIN__)
2110# ifdef __GNUC__
2111# define fpl__m_dllexport __attribute__ ((dllexport))
2112# define fpl__m_dllimport __attribute__ ((dllimport))
2113# else
2114# define fpl__m_dllexport __declspec(dllexport)
2115# define fpl__m_dllimport __declspec(dllimport)
2116# endif
2117# define fpl__m_dlllocal
2118#else
2119# if __GNUC__ >= 4
2120# define fpl__m_dllimport __attribute__((visibility("default")))
2121# define fpl__m_dllexport __attribute__((visibility("default")))
2122# define fpl__m_dlllocal __attribute__((visibility("hidden")))
2123# else
2124# define fpl__m_dllimport
2125# define fpl__m_dllexport
2126# define fpl__m_dlllocal
2127# endif
2128#endif
2129
2134#define fpl_dllimport fpl__m_dllimport
2139#define fpl_dllexport fpl__m_dllexport
2144#define fpl_dlllocal fpl__m_dlllocal
2145
2146//
2147// API Call
2148//
2149#if defined(FPL_API_AS_PRIVATE)
2150# define fpl__m_api static
2151#elif defined(FPL_DLLEXPORT)
2152# define fpl__m_api fpl_dllexport
2153#elif defined(FPL_DLLIMPORT)
2154# define fpl__m_api fpl_dllimport
2155#else
2156# define fpl__m_api fpl_extern
2157#endif // FPL_API_AS_PRIVATE
2158
2163#define fpl_api fpl__m_api
2164
2169#define fpl_main
2170
2171#if defined(FPL_IS_CPP)
2172# define fpl__m_platform_api extern "C" fpl_api
2173# define fpl__m_common_api extern "C" fpl_api
2174#else
2175# define fpl__m_platform_api fpl_api
2176# define fpl__m_common_api fpl_api
2177#endif
2178
2183#define fpl_platform_api fpl__m_platform_api
2188#define fpl_common_api fpl__m_common_api
2189
2190//
2191// Inlining
2192//
2193#if defined(FPL_COMPILER_MSVC)
2194# define fpl__m_force_inline __forceinline
2195# define fpl__m_no_inline __declspec(noinline)
2196#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
2197# define fpl__m_force_inline __attribute__((__always_inline__)) inline
2198# define fpl__m_no_inline __attribute__((noinline))
2199#else
2200# define fpl__m_force_inline inline
2201# define fpl__m_no_inline
2202#endif
2203
2208#define fpl_force_inline fpl__m_force_inline
2213#define fpl_no_inline fpl__m_no_inline
2214
2216
2217//
2218// When C-Runtime is disabled we cannot use any function from the C-Standard Library <stdio.h> or <stdlib.h>
2219//
2220#if defined(FPL_NO_CRT)
2221# if defined(FPL_SUBPLATFORM_STD_CONSOLE)
2222# undef FPL_SUBPLATFORM_STD_CONSOLE
2223# endif
2224# if defined(FPL_SUBPLATFORM_STD_STRINGS)
2225# undef FPL_SUBPLATFORM_STD_STRINGS
2226# endif
2227# if !defined(FPL_USERFUNC_vsnprintf)
2228# error "You need to provide a replacement for vsnprintf() by defining FPL_USERFUNC_vsnprintf!"
2229# endif
2230#endif
2231
2232//
2233// Application type detection
2234// - Can be disabled by FPL_NO_APPTYPE
2235// - Must be explicitly set for No-CRT on Win32
2236//
2237#if defined(FPL_APPTYPE_CONSOLE) && defined(FPL_APPTYPE_WINDOW)
2238# error "Its now allowed to define both FPL_APPTYPE_CONSOLE and FPL_APPTYPE_WINDOW!"
2239#endif
2240#if defined(FPL_NO_CRT)
2241# if !defined(FPL_APPTYPE_CONSOLE) && !defined(FPL_APPTYPE_WINDOW)
2242# error "In 'No-CRT' mode you need to define either FPL_APPTYPE_CONSOLE or FPL_APPTYPE_WINDOW manually!"
2243# endif
2244#elif !defined(FPL_NO_APPTYPE) && !(defined(FPL_APPTYPE_CONSOLE) || defined(FPL_APPTYPE_WINDOW))
2245# if !defined(FPL_NO_WINDOW)
2247# define FPL_APPTYPE_WINDOW
2248# else
2250# define FPL_APPTYPE_CONSOLE
2251# endif
2252#endif
2253
2254//
2255// Include entry points always when its not disabled and implementation block is compiled in
2256//
2257#if defined(FPL_IMPLEMENTATION) && !defined(FPL_NO_ENTRYPOINT)
2258# define FPL_ENTRYPOINT
2259#endif
2260
2261//
2262// Debug/Release detection
2263//
2264#if defined(FPL_DEBUG)
2265# define FPL__ENABLE_DEBUG
2266#elif defined(FPL_RELEASE)
2267# define FPL__ENABLE_RELEASE
2268#endif
2269
2270//
2271// Compiler settings
2272//
2273#if defined(FPL_COMPILER_MSVC)
2274 // Debug/Release detection
2275# if !defined(FPL__ENABLE_DEBUG) && !defined(FPL__ENABLE_RELEASE)
2276# if defined(_DEBUG) || (!defined(NDEBUG))
2277# define FPL__ENABLE_DEBUG
2278# else
2279# define FPL__ENABLE_RELEASE
2280# endif
2281# endif
2282
2283 // Function name macro (Win32)
2284# define FPL__M_FUNCTION_NAME __FUNCTION__
2285
2286 // Setup MSVC subsystem hints
2287# if defined(FPL_APPTYPE_WINDOW)
2288# pragma comment(linker, "/SUBSYSTEM:WINDOWS")
2289# elif defined(FPL_APPTYPE_CONSOLE)
2290# pragma comment(linker, "/SUBSYSTEM:CONSOLE")
2291# endif
2292
2293 // Setup MSVC linker hints
2294# pragma comment(lib, "kernel32.lib")
2295#else
2296 // Function name macro (Other compilers)
2297# define FPL__M_FUNCTION_NAME __FUNCTION__
2298#endif // FPL_COMPILER
2299
2300// Debug Release fallback
2301#if !defined(FPL__ENABLE_DEBUG) && !defined(FPL__ENABLE_RELEASE)
2302# define FPL__ENABLE_DEBUG
2303#endif
2304
2306#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
2307# define FPL__M_IS_IDE 1
2308#else
2309# define FPL__M_IS_IDE 0
2310#endif
2311
2317#define FPL_IS_IDE FPL__M_IS_IDE
2318
2323#define FPL_FUNCTION_NAME FPL__M_FUNCTION_NAME
2324
2325//
2326// Options & Feature detection
2327//
2328
2329//
2330// CPU Instruction Set Detection based on compiler settings
2331//
2332
2361
2362#if defined(__AVX512F__)
2363# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_AVX512
2364#elif defined(__AVX2__)
2365# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_AVX2
2366#elif defined(__AVX__)
2367# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_AVX
2368#elif defined(__SSE4_2__)
2369# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_SSE4_2
2370#elif defined(__SSE4_1__)
2371# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_SSE4_1
2372#elif defined(__SSSE3__)
2373# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_SSE3
2374#elif defined(__SSE3__)
2375# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_SSSE3
2376#elif defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) || defined(FPL_ARCH_X64)
2377# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_SSE2
2378#elif defined(__SSE__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 1)
2379# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_SSE
2380#elif defined(FPL_ARCH_X86)
2381# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_IA32
2382#else
2383# define FPL__M_X86_CPU_INSTR_SET_LEVEL fplX86InstructionSetLevel_None
2384#endif
2385
2390#define FPL_X86_CPU_INSTR_SET_LEVEL FPL__M_X86_CPU_INSTR_SET_LEVEL
2391
2392//
2393// Assertions
2394//
2395#if !defined(FPL_NO_ASSERTIONS)
2396# if !defined(FPL_FORCE_ASSERTIONS)
2397# if defined(FPL__ENABLE_DEBUG)
2398# define FPL__ENABLE_ASSERTIONS
2399# endif
2400# else
2401# define FPL__ENABLE_ASSERTIONS
2402# endif
2403#endif // !FPL_NO_ASSERTIONS
2404#if defined(FPL__ENABLE_ASSERTIONS)
2405# if !defined(FPL_NO_C_ASSERT) && !defined(FPL_NO_CRT)
2406# define FPL__ENABLE_C_ASSERT
2407# endif
2408#endif // FPL__ENABLE_ASSERTIONS
2409
2410//
2411// HasInclude
2412//
2413#if defined(__has_include)
2414# define fpl__m_HasInclude(inc) __has_include(inc)
2415#else
2416# define fpl__m_HasInclude(inc) (1)
2417#endif
2418
2425#define fplHasInclude(inc) fpl__m_HasInclude(inc)
2426
2427//
2428// Window
2429//
2430#if !defined(FPL_NO_WINDOW) && !defined(FPL_APPTYPE_CONSOLE)
2431# define FPL__SUPPORT_WINDOW
2432#endif
2433
2434//
2435// Video
2436//
2437#if !defined(FPL_NO_VIDEO)
2438# define FPL__SUPPORT_VIDEO
2439#endif
2440#if defined(FPL__SUPPORT_VIDEO)
2441# if !defined(FPL_NO_VIDEO_OPENGL)
2442# define FPL__SUPPORT_VIDEO_OPENGL
2443# endif
2444# if !defined(FPL_NO_VIDEO_VULKAN)
2445# define FPL__SUPPORT_VIDEO_VULKAN
2446# endif
2447# if !defined(FPL_NO_VIDEO_SOFTWARE)
2448# define FPL__SUPPORT_VIDEO_SOFTWARE
2449# endif
2450#endif // FPL__SUPPORT_VIDEO
2451
2452//
2453// Audio
2454//
2455#if !defined(FPL_NO_AUDIO)
2456 // Audio support
2457# define FPL__SUPPORT_AUDIO
2458#endif
2459#if defined(FPL__SUPPORT_AUDIO)
2460# if !defined(FPL_NO_AUDIO_DIRECTSOUND) && defined(FPL_PLATFORM_WINDOWS)
2461# define FPL__SUPPORT_AUDIO_DIRECTSOUND // <dsound.h> is always present on windows
2462# endif
2463# if !defined(FPL_NO_AUDIO_ALSA) && defined(FPL_PLATFORM_LINUX)
2464# if fplHasInclude(<alsa/asoundlib.h>)
2465# define FPL__SUPPORT_AUDIO_ALSA
2466# else
2467# warning "FPL-Warning: ALSA audio development library is missing. Please install 'libasound2-dev' and try again!"
2468# endif
2469# endif
2470#endif // FPL__SUPPORT_AUDIO
2471
2472//
2473// Remove video support when the window is disabled
2474//
2475#if !defined(FPL__SUPPORT_WINDOW)
2476# if defined(FPL_SUBPLATFORM_X11)
2477# undef FPL_SUBPLATFORM_X11
2478# endif
2479
2480# if defined(FPL__SUPPORT_VIDEO)
2481# undef FPL__SUPPORT_VIDEO
2482# endif
2483# if defined(FPL__SUPPORT_VIDEO_OPENGL)
2484# undef FPL__SUPPORT_VIDEO_OPENGL
2485# endif
2486# if defined(FPL__SUPPORT_VIDEO_VULKAN)
2487# undef FPL__SUPPORT_VIDEO_VULKAN
2488# endif
2489# if defined(FPL__SUPPORT_VIDEO_SOFTWARE)
2490# undef FPL__SUPPORT_VIDEO_SOFTWARE
2491# endif
2492#endif // !FPL__SUPPORT_WINDOW
2493
2494//
2495// Enable supports (FPL uses _ENABLE_ internally only)
2496//
2497#if defined(FPL__SUPPORT_WINDOW)
2498# define FPL__ENABLE_WINDOW
2499#endif
2500
2501#if defined(FPL__SUPPORT_VIDEO)
2502# define FPL__ENABLE_VIDEO
2503# if defined(FPL__SUPPORT_VIDEO_OPENGL)
2504# define FPL__ENABLE_VIDEO_OPENGL
2505# endif
2506# if defined(FPL__SUPPORT_VIDEO_VULKAN)
2507# define FPL__ENABLE_VIDEO_VULKAN
2508# endif
2509# if defined(FPL__SUPPORT_VIDEO_SOFTWARE)
2510# define FPL__ENABLE_VIDEO_SOFTWARE
2511# endif
2512#endif // FPL__SUPPORT_VIDEO
2513
2514#if defined(FPL__SUPPORT_AUDIO)
2515# define FPL__ENABLE_AUDIO
2516# if defined(FPL__SUPPORT_AUDIO_DIRECTSOUND)
2517# define FPL__ENABLE_AUDIO_DIRECTSOUND
2518# endif
2519# if defined(FPL__SUPPORT_AUDIO_ALSA)
2520# define FPL__ENABLE_AUDIO_ALSA
2521# endif
2522#endif // FPL__SUPPORT_AUDIO
2523
2524#if defined(FPL_LOGGING)
2525# define FPL__ENABLE_LOGGING
2526# if defined(FPL_LOG_MULTIPLE_WRITERS)
2527# define FPL__ENABLE_LOG_MULTIPLE_WRITERS
2528# endif
2529#endif
2530
2531//
2532// Assertions & Debug
2533//
2534
2540
2541#if defined(FPL__ENABLE_ASSERTIONS)
2542# if defined(FPL__ENABLE_C_ASSERT) && !defined(FPL_FORCE_ASSERTIONS)
2543# define FPL__INCLUDE_ASSERT
2544# define fpl__m_Assert(exp) assert(exp)
2545# if defined(__cplusplus)
2546# define fpl__m_StaticAssert(exp) static_assert(exp, "fpl_static_assert")
2547# endif
2548# else
2549# define fpl__m_Assert(exp) if(!(exp)) {*(int *)0 = 0;}
2550# endif // FPL__ENABLE_C_ASSERT
2551# if !defined(fpl__m_StaticAssert)
2552# define FPL__M_STATICASSERT_0(exp, line, counter) \
2553 int fpl__ct_assert_##line_##counter(int ct_assert_failed[(exp)?1:-1])
2554# define fpl__m_StaticAssert(exp) \
2555 FPL__M_STATICASSERT_0(exp, __LINE__, __COUNTER__)
2556# endif
2557#else
2558# define fpl__m_Assert(exp)
2559# define fpl__m_StaticAssert(exp)
2560#endif // FPL__ENABLE_ASSERTIONS
2561
2567#define fplAssert(exp) fpl__m_Assert(exp)
2568
2574#define fplStaticAssert(exp) fpl__m_StaticAssert(exp)
2575
2581#define fplAlwaysAssert(exp) if(!(exp)) {*(int *)0 = 0;}
2582
2588#define fplAssertPtr(ptr) fpl__m_Assert((ptr) != fpl_null)
2589
2590//
2591// Debug-Break
2592// Based on: https://stackoverflow.com/questions/173618/is-there-a-portable-equivalent-to-debugbreak-debugbreak
2593//
2594#if defined(__has_builtin)
2595# if __has_builtin(__builtin_debugtrap)
2596# define fpl__m_DebugBreak() __builtin_debugtrap()
2597# elif __has_builtin(__debugbreak)
2598# define fpl__m_DebugBreak() __debugbreak()
2599# endif
2600#endif
2601#if !defined(fpl__m_DebugBreak)
2602# if defined(FPL_COMPILER_MSVC) || defined(FPL_COMPILER_INTEL)
2603# define fpl__m_DebugBreak() __debugbreak()
2604# elif defined(FPL_COMPILER_ARM)
2605# define fpl__m_DebugBreak() __breakpoint(42)
2606# elif defined(FPL_ARCH_X86) || defined(FPL_ARCH_X64)
2607fpl_internal fpl_force_inline void fpl__m_DebugBreak(void) { __asm__ __volatile__("int $03"); }
2608# elif defined(__thumb__)
2609fpl_internal fpl_force_inline void fpl__m_DebugBreak(void) { __asm__ __volatile__(".inst 0xde01"); }
2610# elif defined(FPL_ARCH_ARM64)
2611fpl_internal fpl_force_inline void fpl__m_DebugBreak(void) { __asm__ __volatile__(".inst 0xd4200000"); }
2612# elif defined(FPL_ARCH_ARM32)
2613fpl_internal fpl_force_inline void fpl__m_DebugBreak(void) { __asm__ __volatile__(".inst 0xe7f001f0"); }
2614# elif defined(FPL_COMPILER_GCC)
2615# define fpl__m_DebugBreak() __builtin_trap()
2616# else
2617# define FPL__INCLUDE_SIGNAL
2618# if defined(SIGTRAP)
2619# define fpl__m_DebugBreak() raise(SIGTRAP)
2620# else
2621# define fpl__m_DebugBreak() raise(SIGABRT)
2622# endif
2623# endif
2624#endif
2625
2630#define fplDebugBreak() fpl__m_DebugBreak()
2631
2633
2634//
2635// Memory macros
2636//
2637
2643
2644#if !defined(FPL_NO_MEMORY_MACROS) || defined(FPL_FORCE_MEMORY_MACROS)
2645# define FPL__ENABLE_MEMORY_MACROS
2646#endif
2647
2649
2650//
2651// Types & Limits
2652//
2653#include <stdint.h> // uint32_t, ...
2654#include <stddef.h> // size_t
2655#include <stdbool.h> // bool
2656#include <stdarg.h> // va_start, va_end, va_list, va_arg
2657#include <limits.h> // UINT32_MAX, ...
2658#if defined(FPL__INCLUDE_ASSERT)
2659# include <assert.h> // assert
2660#endif
2661#if defined(FPL__INCLUDE_SIGNAL)
2662# include <signal.h>
2663#endif
2664#if defined(FPL__INCLUDE_MALLOC)
2665# include <malloc.h> // malloc/free/realloc/_alloca
2666#endif
2667#if defined(FPL__INCLUDE_ALLOCA)
2668# include <alloca.h> // alloca
2669#endif
2670#if !defined(FPL_NO_CRT)
2671# include <stdlib.h> // _countof
2672#endif
2673
2675#if !defined(UINT32_MAX)
2676 // On android or older posix versions there is no UINT32_MAX
2677# define UINT32_MAX (0xFFFFFFFFU)
2678#endif
2680
2681#if defined(FPL_IS_CPP11)
2682# define fpl__m_null nullptr
2683#elif defined(NULL)
2684# define fpl__m_null NULL
2685#else
2686# define fpl__m_null 0
2687#endif
2688
2693#define fpl_null fpl__m_null
2694
2699typedef int32_t fpl_b32;
2700
2701//
2702// Test sizes
2703//
2704
2706
2707#if defined(FPL_CPU_64BIT)
2708fplStaticAssert(sizeof(uintptr_t) >= sizeof(uint64_t));
2709fplStaticAssert(sizeof(size_t) >= sizeof(uint64_t));
2710#elif defined(FPL_CPU_32BIT)
2711fplStaticAssert(sizeof(uintptr_t) >= sizeof(uint32_t));
2712fplStaticAssert(sizeof(size_t) >= sizeof(uint32_t));
2713#endif
2714
2715#if CHAR_BIT != 8
2716#error "Unsupported Char Size, expect 8 bits"
2717#endif
2718
2720
2721//
2722// Macro functions
2723//
2724
2730
2732#define FPL_NOT_IMPLEMENTED {*(int *)0 = 0xBAD;}
2733
2734#if defined(FPL_IS_C99)
2735# define fpl__m_ZeroInit {0}
2736# define fpl__m_StructSet(ptr, type, value) *(ptr) = (type)value
2737# define fpl__m_StructInit(type, ...) (type){__VA_ARGS__}
2738# define fpl__m_StructField(type, name, ...) .name = __VA_ARGS__
2739#else
2740# define fpl__m_ZeroInit {}
2741# define fpl__m_StructSet(ptr, type, value) *(ptr) = value
2742# define fpl__m_StructInit(type, ...) {__VA_ARGS__}
2743# define fpl__m_StructField(type, name, ...) __VA_ARGS__
2744#endif
2745
2751#define fplZeroInit fpl__m_ZeroInit
2752
2760#define fplStructSet fpl__m_StructSet
2761
2769#define fplStructInit fpl__m_StructInit
2770
2779#define fplStructField fpl__m_StructField
2780
2788#define fplGetAlignmentOffset(value, alignment) ( (((alignment) > 1) && (((value) & ((alignment) - 1)) != 0)) ? ((alignment) - ((value) & (alignment - 1))) : 0)
2789
2797#define fplGetAlignedSize(size, alignment) (((size) > 0 && (alignment) > 0) ? ((size) + fplGetAlignmentOffset(size, alignment)) : (size))
2798
2806#define fplIsAligned(ptr, alignment) (((uintptr_t)(const void *)(ptr)) % (alignment) == 0)
2807
2814#define fplIsPowerOfTwo(value) (((value) != 0) && (((value) & (~(value) + 1)) == (value)))
2815
2823#define fplIsBitSet(value, bit) (((value) >> (bit)) & 0x1)
2824
2831#define fplKiloBytes(value) (((value) * 1024ull))
2832
2839#define fplMegaBytes(value) ((fplKiloBytes(value) * 1024ull))
2840
2847#define fplGigaBytes(value) ((fplMegaBytes(value) * 1024ull))
2848
2855#define fplTeraBytes(value) ((fplGigaBytes(value) * 1024ull))
2856
2857//
2858// Endianess
2859//
2860
2862
2863typedef enum fplEndianessType {
2864 // Little-endian type
2865 fplEndianessType_Little = 0x04030201,
2866 // Big-endian type
2867 fplEndianessType_Big = 0x01020304,
2868} fplEndianessType;
2869
2870typedef union {
2871 // Value as 4 unsigned chars
2872 unsigned char bytes[4];
2873 // Value as 32-bit unsigned integer
2874 uint32_t value;
2875} fplEndianess;
2876
2877// The current endianess value
2878fpl_globalvar const fplEndianess fpl__global_endianessOrder = { 1, 2, 3, 4 };
2879
2881
2887#define fplIsBigEndian() (fpl__global_endianessOrder.value == fplEndianessType_Big)
2888
2894#define fplIsLittleEndian() (fpl__global_endianessOrder.value == fplEndianessType_Little)
2895
2901#define fplGetEndianess32() (fpl__global_endianessOrder.value)
2902
2908#define fplClearStruct(ptr) fplMemoryClear((void *)(ptr), sizeof(*(ptr)))
2909
2916#define fplCopyStruct(src, dst) fplMemoryCopy(src, sizeof(*(src)), dst)
2917
2918//
2919// Array count
2920//
2921#if defined(_countof)
2922# define fpl__m_ArrayCount(arr) _countof(arr)
2923#elif defined(ARRAY_SIZE)
2924# define fpl__m_ArrayCount(arr) ARRAY_SIZE(arr)
2925#else
2926# define fpl__m_ArrayCount(arr) (sizeof(arr) / sizeof((arr)[0]))
2927#endif
2928
2935#define fplArrayCount(arr) fpl__m_ArrayCount(arr)
2936
2944#define fplOffsetOf(type, field) ((size_t)(&(((type*)(0))->field)))
2945
2953#define fplMin(a, b) ((a) < (b) ? (a) : (b))
2954
2962#define fplMax(a, b) ((a) > (b) ? (a) : (b))
2963
2964#if defined(FPL_PLATFORM_WINDOWS)
2965# define fpl__m_StackAllocate(size) _alloca(size)
2966#else
2967# define fpl__m_StackAllocate(size) alloca(size)
2968#endif
2969
2977#define fplStackAllocate(size) fpl__m_StackAllocate(size)
2978
2980
2981#if defined(FPL_IS_CPP)
2982# define FPL__M_ENUM_AS_FLAGS_OPERATORS(etype) \
2983 inline etype operator | (etype a, etype b) { \
2984 return static_cast<etype>(static_cast<int>(a) | static_cast<int>(b)); \
2985 } \
2986 inline etype& operator |= (etype &a, etype b) { \
2987 return a = a | b; \
2988 } \
2989 inline etype operator & (etype a, etype b) { \
2990 return static_cast<etype>(static_cast<int>(a) & static_cast<int>(b)); \
2991 } \
2992 inline etype& operator &= (etype &a, etype b) { \
2993 return a = a & b; \
2994 } \
2995 inline etype operator ~ (etype a) { \
2996 return static_cast<etype>(~static_cast<int>(a)); \
2997 } \
2998 inline etype operator ^ (etype a, etype b) { \
2999 return static_cast<etype>(static_cast<int>(a) ^ static_cast<int>(b)); \
3000 } \
3001 inline etype& operator ^= (etype &a, etype b) { \
3002 return a = a ^ b; \
3003 }
3004#else
3005# define FPL__M_ENUM_AS_FLAGS_OPERATORS(etype)
3006#endif
3007
3013#define FPL_ENUM_AS_FLAGS_OPERATORS(type) FPL__M_ENUM_AS_FLAGS_OPERATORS(type)
3014
3015// ****************************************************************************
3016//
3017// Platform Includes
3018//
3019// ****************************************************************************
3020#if !defined(FPL_NO_PLATFORM_INCLUDES) && !defined(FPL__HAS_PLATFORM_INCLUDES)
3021# define FPL__HAS_PLATFORM_INCLUDES
3022
3023# if defined(FPL_PLATFORM_WINDOWS)
3024 // @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!
3025# if !defined(NOMINMAX)
3026# define NOMINMAX
3027# endif
3028 // @NOTE(final): For now we dont want any network, com or gdi stuff at all, maybe later who knows.
3029# if !defined(WIN32_LEAN_AND_MEAN)
3030# define WIN32_LEAN_AND_MEAN 1
3031# endif
3032 // @STUPID(final): Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here"
3033struct IUnknown;
3034# include <windows.h> // Win32 api
3035# if _WIN32_WINNT < 0x0600
3036# error "Windows Vista or higher required!"
3037# endif
3038# endif // FPL_PLATFORM_WINDOWS
3039
3040# if defined(FPL_SUBPLATFORM_POSIX)
3041# include <pthread.h> // pthread_t, pthread_mutex_, pthread_cond_, pthread_barrier_
3042# include <sched.h> // sched_param, sched_get_priority_max, SCHED_FIFO
3043# include <semaphore.h> // sem_t
3044# include <dirent.h> // DIR, dirent
3045# endif // FPL_SUBPLATFORM_POSIX
3046
3047# if defined(FPL_SUBPLATFORM_X11)
3048# include <X11/X.h> // Window
3049# include <X11/Xlib.h> // Display
3050# include <X11/Xutil.h> // XVisualInfo
3051# include <X11/Xatom.h> // XA_CARDINAL
3052# endif // FPL_SUBPLATFORM_X11
3053
3054#endif // !FPL_NO_PLATFORM_INCLUDES
3055
3056//
3057// Platform handles
3058//
3059#if !defined(FPL__HAS_PLATFORM_INCLUDES) || defined(FPL_OPAQUE_HANDLES)
3060
3061# if defined(FPL_PLATFORM_WINDOWS)
3062
3064typedef struct fpl__Win32Guid {
3065 uint32_t Data1;
3066 uint16_t Data2;
3067 uint16_t Data3;
3068 uint8_t Data4[ 8 ];
3069} fpl__Win32Guid;
3071typedef void *fpl__Win32Handle;
3073typedef fpl__Win32Handle fpl__Win32InstanceHandle;
3075typedef fpl__Win32Handle fpl__Win32LibraryHandle;
3077typedef fpl__Win32Handle fpl__Win32FileHandle;
3079typedef fpl__Win32Handle fpl__Win32ThreadHandle;
3081typedef uint64_t fpl__Win32MutexHandle[16];
3083typedef fpl__Win32Handle fpl__Win32SignalHandle;
3085typedef void *fpl__Win32ConditionVariable;
3087typedef fpl__Win32Handle fpl__Win32SemaphoreHandle;
3089typedef fpl__Win32Handle fpl__Win32WindowHandle;
3091typedef fpl__Win32Handle fpl__Win32DeviceContext;
3093typedef fpl__Win32Handle fpl__Win32RenderingContext;
3095typedef union fpl__Win32LargeInteger {
3097 int64_t QuadPart;
3098 struct {
3100 int32_t LowPart;
3102 int32_t HighPart;
3103 };
3104} fpl__Win32LargeInteger;
3105
3106# endif // FPL_PLATFORM_WINDOWS
3107
3108# if defined(FPL_SUBPLATFORM_POSIX)
3109
3111typedef void *fpl__POSIXLibraryHandle;
3113typedef int fpl__POSIXFileHandle;
3115typedef void *fpl__POSIXDirHandle;
3117typedef uint64_t fpl__POSIXThreadHandle;
3119typedef uint64_t fpl__POSIXMutexHandle[16];
3121typedef uint64_t fpl__POSIXSemaphoreHandle[8];
3123typedef uint64_t fpl__POSIXConditionVariable[16];
3124
3125# endif // FPL_SUBPLATFORM_POSIX
3126
3127# if defined(FPL_SUBPLATFORM_X11)
3128
3129// @TODO(final): Opaque X11 Display is not correct, to not assume void ptr - its a full structure, that is really large
3130
3132typedef void *fpl__X11Display;
3133
3135typedef int fpl__X11Window;
3137typedef void *fpl__X11Visual;
3139typedef void *fpl__X11GC;
3141typedef void *fpl__X11Image;
3143typedef void *fpl__GLXContext;
3144
3145# endif // FPL_SUBPLATFORM_X11
3146
3147# if defined(FPL_PLATFORM_LINUX)
3148
3150typedef int fpl__LinuxSignalHandle;
3151
3152# endif // FPL_PLATFORM_LINUX
3153
3154#else
3155
3156# if defined(FPL_PLATFORM_WINDOWS)
3157
3159typedef GUID fpl__Win32Guid;
3161typedef HANDLE fpl__Win32Handle;
3163typedef HINSTANCE fpl__Win32InstanceHandle;
3165typedef HMODULE fpl__Win32LibraryHandle;
3167typedef HANDLE fpl__Win32ThreadHandle;
3169typedef HANDLE fpl__Win32FileHandle;
3171typedef CRITICAL_SECTION fpl__Win32MutexHandle;
3173typedef HANDLE fpl__Win32SignalHandle;
3175typedef CONDITION_VARIABLE fpl__Win32ConditionVariable;
3177typedef HANDLE fpl__Win32SemaphoreHandle;
3179typedef HWND fpl__Win32WindowHandle;
3181typedef HDC fpl__Win32DeviceContext;
3183typedef HGLRC fpl__Win32RenderingContext;
3185typedef LARGE_INTEGER fpl__Win32LargeInteger;
3186
3187# endif // FPL_PLATFORM_WINDOWS
3188
3189# if defined(FPL_SUBPLATFORM_POSIX)
3190
3192typedef void *fpl__POSIXLibraryHandle;
3194typedef int fpl__POSIXFileHandle;
3196typedef DIR *fpl__POSIXDirHandle;
3198typedef pthread_t fpl__POSIXThreadHandle;
3200typedef pthread_mutex_t fpl__POSIXMutexHandle;
3202typedef sem_t fpl__POSIXSemaphoreHandle;
3204typedef pthread_cond_t fpl__POSIXConditionVariable;
3205
3206# endif // FPL_SUBPLATFORM_POSIX
3207
3208# if defined(FPL_SUBPLATFORM_X11)
3209
3211typedef Display *fpl__X11Display;
3213typedef Window fpl__X11Window;
3215typedef Visual *fpl__X11Visual;
3217typedef GC fpl__X11GC;
3219typedef XImage *fpl__X11Image;
3221typedef void *fpl__GLXContext;
3222
3223# endif // FPL_SUBPLATFORM_X11
3224
3225
3226# if defined(FPL_PLATFORM_LINUX)
3227
3229typedef int fpl__LinuxSignalHandle;
3230
3231# endif // FPL_PLATFORM_LINUX
3232
3233
3234#endif
3235
3236//
3237// Constants
3238//
3239
3245#if defined(FPL_PLATFORM_WINDOWS)
3246# if defined(MAX_PATH)
3247# define FPL__M_MAX_FILENAME_LENGTH (MAX_PATH)
3248# define FPL__M_MAX_PATH_LENGTH (MAX_PATH * 2)
3249# else
3250# define FPL__M_MAX_FILENAME_LENGTH (260)
3251# define FPL__M_MAX_PATH_LENGTH (260 * 2)
3252# endif
3253# define FPL__M_PATH_SEPARATOR '\\'
3254# define FPL__M_FILE_EXT_SEPARATOR '.'
3255#else
3256# define FPL__M_MAX_FILENAME_LENGTH (512)
3257# define FPL__M_MAX_PATH_LENGTH (2048)
3258# define FPL__M_PATH_SEPARATOR '/'
3259# define FPL__M_FILE_EXT_SEPARATOR '.'
3260#endif
3261
3266#define FPL_MAX_FILENAME_LENGTH FPL__M_MAX_FILENAME_LENGTH
3267
3272#define FPL_MAX_PATH_LENGTH FPL__M_MAX_PATH_LENGTH
3273
3278#define FPL_PATH_SEPARATOR FPL__M_PATH_SEPARATOR
3279
3284#define FPL_FILE_EXT_SEPARATOR FPL__M_FILE_EXT_SEPARATOR
3285
3290#define FPL_MAX_NAME_LENGTH (256)
3291
3296#define FPL_MAX_BUFFER_LENGTH (2048)
3297
3299
3300// ****************************************************************************
3301//
3302// > API
3303//
3304// ****************************************************************************
3305
3306// ----------------------------------------------------------------------------
3313// ----------------------------------------------------------------------------
3314
3315//
3316// Barrier/Fence
3317//
3318
3337
3338//
3339// Exchange
3340//
3341
3350fpl_platform_api uint32_t fplAtomicExchangeU32(volatile uint32_t *target, const uint32_t value);
3359fpl_platform_api uint64_t fplAtomicExchangeU64(volatile uint64_t *target, const uint64_t value);
3368fpl_platform_api int32_t fplAtomicExchangeS32(volatile int32_t *target, const int32_t value);
3377fpl_platform_api int64_t fplAtomicExchangeS64(volatile int64_t *target, const int64_t value);
3386fpl_common_api void *fplAtomicExchangePtr(volatile void **target, const void *value);
3395fpl_common_api size_t fplAtomicExchangeSize(volatile size_t *target, const size_t value);
3396
3397//
3398// Fetch and Add
3399//
3400
3409fpl_platform_api uint32_t fplAtomicFetchAndAddU32(volatile uint32_t *value, const uint32_t addend);
3418fpl_platform_api uint64_t fplAtomicFetchAndAddU64(volatile uint64_t *value, const uint64_t addend);
3427fpl_platform_api int32_t fplAtomicFetchAndAddS32(volatile int32_t *value, const int32_t addend);
3436fpl_platform_api int64_t fplAtomicFetchAndAddS64(volatile int64_t *value, const int64_t addend);
3445fpl_common_api size_t fplAtomicFetchAndAddSize(volatile size_t *dest, const size_t addend);
3454fpl_common_api void *fplAtomicFetchAndAddPtr(volatile void **dest, const intptr_t addend);
3455
3456//
3457// Add and Fetch
3458//
3459
3468fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *dest, const uint32_t addend);
3477fpl_platform_api uint64_t fplAtomicAddAndFetchU64(volatile uint64_t *dest, const uint64_t addend);
3486fpl_platform_api int32_t fplAtomicAddAndFetchS32(volatile int32_t *dest, const int32_t addend);
3495fpl_platform_api int64_t fplAtomicAddAndFetchS64(volatile int64_t *dest, const int64_t addend);
3504fpl_common_api size_t fplAtomicAddAndFetchSize(volatile size_t *dest, const size_t addend);
3513fpl_common_api void *fplAtomicAddAndFetchPtr(volatile void **dest, const intptr_t addend);
3514
3515//
3516// Increment
3517//
3518
3526fpl_platform_api uint32_t fplAtomicIncrementU32(volatile uint32_t *dest);
3534fpl_platform_api uint64_t fplAtomicIncrementU64(volatile uint64_t *dest);
3542fpl_platform_api int32_t fplAtomicIncrementS32(volatile int32_t *dest);
3550fpl_platform_api int64_t fplAtomicIncrementS64(volatile int64_t *dest);
3558fpl_common_api size_t fplAtomicIncrementSize(volatile size_t *dest);
3566fpl_common_api void *fplAtomicIncrementPtr(volatile void **dest);
3567
3568//
3569// CAS
3570//
3571
3582fpl_platform_api uint32_t fplAtomicCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange);
3593fpl_platform_api uint64_t fplAtomicCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange);
3604fpl_platform_api int32_t fplAtomicCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange);
3615fpl_platform_api int64_t fplAtomicCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange);
3626fpl_common_api size_t fplAtomicCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange);
3637fpl_common_api void *fplAtomicCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange);
3638
3648fpl_platform_api bool fplAtomicIsCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange);
3658fpl_platform_api bool fplAtomicIsCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange);
3668fpl_platform_api bool fplAtomicIsCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange);
3678fpl_platform_api bool fplAtomicIsCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange);
3688fpl_common_api bool fplAtomicIsCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange);
3698fpl_common_api bool fplAtomicIsCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange);
3699
3700//
3701// Load
3702//
3703
3712fpl_platform_api uint32_t fplAtomicLoadU32(volatile uint32_t *source);
3721fpl_platform_api uint64_t fplAtomicLoadU64(volatile uint64_t *source);
3730fpl_platform_api int32_t fplAtomicLoadS32(volatile int32_t *source);
3739fpl_platform_api int64_t fplAtomicLoadS64(volatile int64_t *source);
3748fpl_common_api size_t fplAtomicLoadSize(volatile size_t *source);
3757fpl_common_api void *fplAtomicLoadPtr(volatile void **source);
3758
3759//
3760// Store
3761//
3762
3770fpl_platform_api void fplAtomicStoreU32(volatile uint32_t *dest, const uint32_t value);
3778fpl_platform_api void fplAtomicStoreU64(volatile uint64_t *dest, const uint64_t value);
3786fpl_platform_api void fplAtomicStoreS32(volatile int32_t *dest, const int32_t value);
3794fpl_platform_api void fplAtomicStoreS64(volatile int64_t *dest, const int64_t value);
3802fpl_common_api void fplAtomicStoreSize(volatile size_t *dest, const size_t value);
3810fpl_common_api void fplAtomicStorePtr(volatile void **dest, const void *value);
3811
3813
3814// ----------------------------------------------------------------------------
3820// ----------------------------------------------------------------------------
3821
3826typedef struct fplMemoryBlock {
3828 void *base;
3830 size_t size;
3832
3855
3862fpl_common_api void fplMemoryClear(void *mem, const size_t size);
3863
3871fpl_common_api void fplMemorySet(void *mem, const uint8_t value, const size_t size);
3872
3880fpl_common_api void fplMemoryCopy(const void *sourceMem, const size_t sourceSize, void *targetMem);
3881
3891fpl_platform_api void *fplMemoryAllocate(const size_t size);
3892
3901
3911fpl_common_api void *fplMemoryAlignedAllocate(const size_t size, const size_t alignment);
3912
3921
3929
3931
3932// ----------------------------------------------------------------------------
3938// ----------------------------------------------------------------------------
3939
3944typedef char fplVersionNumberPart[4 + 1];
3945
3970
3985
3994
3996
3997// ----------------------------------------------------------------------------
4003// ----------------------------------------------------------------------------
4004
4012fpl_platform_api size_t fplSessionGetUsername(char *nameBuffer, const size_t maxNameBufferLen);
4013
4015
4016// ----------------------------------------------------------------------------
4023// ----------------------------------------------------------------------------
4024
4048
4066
4073
4117
4137
4154fplStaticAssert(sizeof(fplCPUCapabilities) == 32);
4155
4160typedef union fplCPUIDLeaf {
4161 struct {
4163 uint32_t eax;
4165 uint32_t ebx;
4167 uint32_t ecx;
4169 uint32_t edx;
4170 };
4172 uint32_t raw[4];
4173} fplCPUIDLeaf;
4174fplStaticAssert(sizeof(fplCPUIDLeaf) == 16);
4175
4183fpl_common_api bool fplCPUID(const uint32_t functionId, fplCPUIDLeaf *outLeaf);
4184
4191
4198
4206
4213
4221fpl_common_api size_t fplCPUGetName(char *destBuffer, const size_t maxDestBufferLen);
4222
4230
4237
4239
4240// ----------------------------------------------------------------------------
4246// ----------------------------------------------------------------------------
4247
4268
4270
4290
4318
4326
4346
4347#if defined(FPL__ENABLE_VIDEO_OPENGL)
4362
4379#endif // FPL__ENABLE_VIDEO_OPENGL
4380
4381#if defined(FPL__ENABLE_VIDEO_VULKAN)
4382
4391typedef void (fplVulkanValidationLayerCallback)(void *userData, const char *message, const uint32_t messageSeverity, const uint32_t messageType, const void *debugUtilsMessengerCallbackData);
4392
4405
4424
4455#endif // FPL__ENABLE_VIDEO_VULKAN
4456
4462#if defined(FPL__ENABLE_VIDEO_OPENGL)
4465#endif
4466#if defined(FPL__ENABLE_VIDEO_VULKAN)
4469#endif
4473
4488
4496
4518
4546
4567
4569
4580
4591
4606
4643
4725
4727
4734
4739#define FPL_MAX_AUDIO_CHANNEL_COUNT 32
4740
4749
4774
4779typedef union fplAudioDeviceID {
4780#if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
4782 fpl__Win32Guid dshow;
4783#endif
4784#if defined(FPL__ENABLE_AUDIO_ALSA)
4786 char alsa[256];
4787#endif
4789 uint8_t dummy[256];
4791
4796typedef uint64_t fplAudioFormatU64;
4797
4810
4823
4824#if defined(FPL__ENABLE_AUDIO_ALSA)
4829typedef struct fplAlsaAudioSettings {
4831 fpl_b32 noMMap;
4832} fplAlsaAudioSettings;
4833#endif
4834
4840#if defined(FPL__ENABLE_AUDIO_ALSA)
4842 fplAlsaAudioSettings alsa;
4843#endif
4847
4857typedef uint32_t(fpl_audio_client_read_callback)(const fplAudioFormat *deviceFormat, const uint32_t frameCount, void *outputSamples, void *userData);
4858
4883
4891
4902
4907typedef struct fplImageSource {
4909 const uint8_t *data;
4911 uint32_t width;
4913 uint32_t height;
4917
4926typedef bool (fpl_window_event_callback)(const fplPlatformType platformType, void *windowState, void *rawEventData, void *userData);
4927
4937
4952
4957typedef struct fplWindowSize {
4959 uint32_t width;
4961 uint32_t height;
4963
4968typedef struct fplWindowPosition {
4970 int32_t left;
4972 int32_t top;
4974
4979typedef union fplColor32 {
4981 struct {
4983 uint8_t b;
4985 uint8_t g;
4987 uint8_t r;
4989 uint8_t a;
4992 uint32_t value;
4994 uint8_t m[4];
4995} fplColor32;
4996
5005fpl_common_api fplColor32 fplCreateColorRGBA(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a);
5006
5039
5047
5056
5063
5074
5082
5090typedef void *(fpl_memory_allocate_callback)(void *userData, const size_t size, const size_t alignment);
5091
5097typedef void (fpl_memory_release_callback)(void *userData, void *ptr);
5098
5109
5124
5135
5154
5162
5169
5176
5178
5179// ----------------------------------------------------------------------------
5185// ----------------------------------------------------------------------------
5186
5193
5201
5210fpl_common_api bool fplPlatformInit(const fplInitFlags initFlags, const fplSettings *initSettings);
5211
5218
5225
5231
5233
5234// ----------------------------------------------------------------------------
5240// ----------------------------------------------------------------------------
5241
5269
5270#if defined(FPL__ENABLE_LOGGING)
5271
5280typedef void (fpl_log_func_callback)(const char *funcName, const int lineNumber, const fplLogLevel level, const char *message);
5281
5298
5300
5309
5318
5331
5336typedef struct fplLogSettings {
5337#if defined(FPL__ENABLE_LOG_MULTIPLE_WRITERS)
5338 union {
5341 struct {
5343 fplLogWriter criticalWriter;
5345 fplLogWriter errorWriter;
5347 fplLogWriter warningWriter;
5349 fplLogWriter infoWriter;
5351 fplLogWriter verboseWriter;
5353 fplLogWriter debugWriter;
5354 };
5355 };
5356#else
5359#endif // FPL_USE_LOG_SIMPLE
5361 fplLogLevel maxLevel;
5365
5373
5381
5389
5397
5398#endif // FPL__ENABLE_LOGGING
5399
5401
5402// ----------------------------------------------------------------------------
5408// ----------------------------------------------------------------------------
5409
5417
5425fpl_common_api const char *fplGetErrorByIndex(const size_t index);
5426
5434
5441
5443
5444// ----------------------------------------------------------------------------
5450// ----------------------------------------------------------------------------
5451
5457#if defined(FPL_PLATFORM_WINDOWS)
5459 fpl__Win32LibraryHandle win32LibraryHandle;
5460#elif defined(FPL_SUBPLATFORM_POSIX)
5462 fpl__POSIXLibraryHandle posixLibraryHandle;
5463#endif
5465
5476
5484fpl_platform_api bool fplDynamicLibraryLoad(const char *libraryFilePath, fplDynamicLibraryHandle *outHandle);
5485
5494
5501
5503
5504// ----------------------------------------------------------------------------
5509// ----------------------------------------------------------------------------
5510
5517fpl_platform_api void fplDebugOut(const char *text);
5518
5526fpl_common_api void fplDebugFormatOut(const char *format, ...);
5527
5529
5530// ----------------------------------------------------------------------------
5536// ----------------------------------------------------------------------------
5537
5543fpl_platform_api void fplConsoleOut(const char *text);
5544
5550fpl_platform_api void fplConsoleError(const char *text);
5551
5558
5565fpl_common_api void fplConsoleFormatOut(const char *format, ...);
5566
5573fpl_common_api void fplConsoleFormatError(const char *format, ...);
5574
5576
5577// ----------------------------------------------------------------------------
5583// ----------------------------------------------------------------------------
5584
5589typedef union fplTimestamp {
5590#if defined(FPL_PLATFORM_WINDOWS)
5592 struct {
5594 fpl__Win32LargeInteger qpc;
5596 uint64_t ticks;
5597 } win32;
5598#elif defined(FPL_SUBPLATFORM_POSIX)
5600 struct {
5602 uint64_t seconds;
5604 int64_t nanoSeconds;
5605 } posix;
5606#endif
5608 uint64_t unused;
5609} fplTimestamp;
5610
5615typedef uint32_t fplTimeoutValue;
5616
5621#define FPL_TIMEOUT_INFINITE UINT32_MAX
5622
5627typedef double fplSeconds;
5628
5633typedef uint64_t fplMilliseconds;
5634
5641
5647
5655
5657
5658// ----------------------------------------------------------------------------
5664// ----------------------------------------------------------------------------
5665
5680
5685typedef uint32_t fplThreadState;
5686
5716
5722
5728typedef void (fpl_run_thread_callback)(const fplThreadHandle *thread, void *data);
5729
5735#if defined(FPL_PLATFORM_WINDOWS)
5737 fpl__Win32ThreadHandle win32ThreadHandle;
5738#elif defined(FPL_SUBPLATFORM_POSIX)
5740 fpl__POSIXThreadHandle posixThread;
5741#endif
5743
5758
5777
5778#if defined(FPL_PLATFORM_WINDOWS)
5783typedef struct fpl__Win32InternalSemaphore {
5785 fpl__Win32SemaphoreHandle handle;
5787 volatile int32_t value;
5788} fpl__Win32InternalSemaphore;
5789#endif
5790
5796#if defined(FPL_PLATFORM_WINDOWS)
5798 fpl__Win32InternalSemaphore win32;
5799#elif defined(FPL_SUBPLATFORM_POSIX)
5801 fpl__POSIXSemaphoreHandle posixHandle;
5802#endif
5804
5815
5821#if defined(FPL_PLATFORM_WINDOWS)
5823 fpl__Win32MutexHandle win32CriticalSection;
5824#elif defined(FPL_SUBPLATFORM_POSIX)
5826 fpl__POSIXMutexHandle posixMutex;
5827#endif
5829
5840
5846#if defined(FPL_PLATFORM_WINDOWS)
5848 fpl__Win32SignalHandle win32EventHandle;
5849#elif defined(FPL_PLATFORM_LINUX)
5851 fpl__LinuxSignalHandle linuxEventHandle;
5852#endif
5854
5865
5876
5882#if defined(FPL_PLATFORM_WINDOWS)
5884 fpl__Win32ConditionVariable win32Condition;
5885#elif defined(FPL_SUBPLATFORM_POSIX)
5887 fpl__POSIXConditionVariable posixCondition;
5888#endif
5892
5903
5911
5917
5923
5929
5935
5946
5955
5962
5970
5976fpl_platform_api void fplThreadSleep(const uint32_t milliseconds);
5977
5983
5993
6002
6012fpl_platform_api bool fplThreadWaitForAll(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout);
6013
6023fpl_platform_api bool fplThreadWaitForAny(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout);
6032
6039
6047
6055
6063
6073
6080
6089
6099fpl_platform_api bool fplSignalWaitForAll(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout);
6100
6110fpl_platform_api bool fplSignalWaitForAny(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout);
6111
6119
6127
6136
6143
6153
6161
6169
6177fpl_platform_api bool fplSemaphoreInit(fplSemaphoreHandle *semaphore, const uint32_t initialValue);
6178
6186
6196
6205
6213
6221
6223
6224// ----------------------------------------------------------------------------
6230// ----------------------------------------------------------------------------
6231
6238fpl_common_api bool fplIsStringMatchWildcard(const char *source, const char *wildcard);
6239
6249fpl_common_api bool fplIsStringEqualLen(const char *a, const size_t aLen, const char *b, const size_t bLen);
6250
6257fpl_common_api bool fplIsStringEqual(const char *a, const char *b);
6258
6265fpl_common_api char *fplEnforcePathSeparatorLen(char *path, size_t maxPathLen);
6266
6274
6283fpl_common_api char *fplStringAppendLen(const char *appended, const size_t appendedLen, char *buffer, size_t maxBufferLen);
6284
6292fpl_common_api char *fplStringAppend(const char *appended, char *buffer, size_t maxBufferLen);
6293
6299fpl_common_api size_t fplGetStringLength(const char *str);
6300
6310fpl_common_api char *fplCopyStringLen(const char *source, const size_t sourceLen, char *dest, const size_t maxDestLen);
6311
6320fpl_common_api char *fplCopyString(const char *source, char *dest, const size_t maxDestLen);
6321
6331fpl_platform_api size_t fplWideStringToUTF8String(const wchar_t *wideSource, const size_t wideSourceLen, char *utf8Dest, const size_t maxUtf8DestLen);
6332
6342fpl_platform_api size_t fplUTF8StringToWideString(const char *utf8Source, const size_t utf8SourceLen, wchar_t *wideDest, const size_t maxWideDestLen);
6343
6353fpl_common_api size_t fplStringFormat(char *destBuffer, const size_t maxDestBufferLen, const char *format, ...);
6354
6364fpl_common_api size_t fplStringFormatArgs(char *destBuffer, const size_t maxDestBufferLen, const char *format, va_list argList);
6365
6372fpl_common_api int32_t fplStringToS32Len(const char *str, const size_t len);
6373
6379fpl_common_api int32_t fplStringToS32(const char *str);
6380
6388fpl_common_api size_t fplS32ToString(const int32_t value, char *buffer, const size_t maxBufferLen);
6389
6391
6392// ----------------------------------------------------------------------------
6398// ----------------------------------------------------------------------------
6399
6405#if defined(FPL_PLATFORM_WINDOWS)
6407 fpl__Win32FileHandle win32FileHandle;
6408#elif defined(FPL_SUBPLATFORM_POSIX)
6410 fpl__POSIXFileHandle posixFileHandle;
6411#endif
6413
6424
6437
6450
6465
6467
6482
6484
6489typedef union fplFilePermissions {
6490 struct {
6492 uint8_t user;
6494 uint8_t group;
6496 uint8_t owner;
6498 uint8_t unused;
6499 };
6501 uint32_t umask;
6503
6520
6522
6528#if defined(FPL_PLATFORM_WINDOWS)
6530 fpl__Win32FileHandle win32FileHandle;
6531#elif defined(FPL_SUBPLATFORM_POSIX)
6533 fpl__POSIXDirHandle posixDirHandle;
6534#endif
6536
6543 const char *rootPath;
6545 const char *filter;
6547
6549typedef uint64_t fplFileTimeStamp;
6550
6563
6586
6594fpl_platform_api bool fplFileOpenBinary(const char *filePath, fplFileHandle *outHandle);
6595
6603fpl_platform_api bool fplFileCreateBinary(const char *filePath, fplFileHandle *outHandle);
6604
6615fpl_platform_api uint32_t fplFileReadBlock32(const fplFileHandle *fileHandle, const uint32_t sizeToRead, void *targetBuffer, const uint32_t maxTargetBufferSize);
6616
6627fpl_platform_api uint64_t fplFileReadBlock64(const fplFileHandle *fileHandle, const uint64_t sizeToRead, void *targetBuffer, const uint64_t maxTargetBufferSize);
6628
6639fpl_platform_api size_t fplFileReadBlock(const fplFileHandle *fileHandle, const size_t sizeToRead, void *targetBuffer, const size_t maxTargetBufferSize);
6640
6650fpl_platform_api uint32_t fplFileWriteBlock32(const fplFileHandle *fileHandle, void *sourceBuffer, const uint32_t sourceSize);
6651
6661fpl_platform_api uint64_t fplFileWriteBlock64(const fplFileHandle *fileHandle, void *sourceBuffer, const uint64_t sourceSize);
6662
6672fpl_common_api size_t fplFileWriteBlock(const fplFileHandle *fileHandle, void *sourceBuffer, const size_t sourceSize);
6673
6682fpl_platform_api uint32_t fplFileSetPosition32(const fplFileHandle *fileHandle, const int32_t position, const fplFilePositionMode mode);
6683
6692fpl_platform_api uint64_t fplFileSetPosition64(const fplFileHandle *fileHandle, const int64_t position, const fplFilePositionMode mode);
6693
6702fpl_common_api size_t fplFileSetPosition(const fplFileHandle *fileHandle, const intptr_t position, const fplFilePositionMode mode);
6703
6712
6721
6730
6737
6744
6751fpl_platform_api uint32_t fplFileGetSizeFromPath32(const char *filePath);
6752
6759fpl_platform_api uint64_t fplFileGetSizeFromPath64(const char *filePath);
6760
6767fpl_platform_api size_t fplFileGetSizeFromPath(const char *filePath);
6768
6776
6784
6792
6800
6808
6814fpl_platform_api bool fplFileExists(const char *filePath);
6815
6823fpl_platform_api bool fplFileCopy(const char *sourceFilePath, const char *targetFilePath, const bool overwrite);
6824
6831fpl_platform_api bool fplFileMove(const char *sourceFilePath, const char *targetFilePath);
6832
6838fpl_platform_api bool fplFileDelete(const char *filePath);
6839
6846
6853
6860
6871fpl_platform_api bool fplDirectoryListBegin(const char *path, const char *filter, fplFileEntry *entry);
6872
6882
6890
6892
6893// ----------------------------------------------------------------------------
6899// ----------------------------------------------------------------------------
6900
6908fpl_platform_api size_t fplGetExecutableFilePath(char *destPath, const size_t maxDestLen);
6909
6917fpl_platform_api size_t fplGetHomePath(char *destPath, const size_t maxDestLen);
6918
6927fpl_common_api size_t fplExtractFilePath(const char *sourcePath, char *destPath, const size_t maxDestLen);
6928
6935fpl_common_api const char *fplExtractFileExtension(const char *sourcePath);
6936
6943fpl_common_api const char *fplExtractFileName(const char *sourcePath);
6944
6954fpl_common_api size_t fplChangeFileExtension(const char *filePath, const char *newFileExtension, char *destPath, const size_t maxDestLen);
6955
6965fpl_common_api size_t fplPathCombine(char *destPath, const size_t maxDestPathLen, const size_t pathCount, ...);
6966
6968
6969#if defined(FPL__ENABLE_WINDOW)
6970// ----------------------------------------------------------------------------
6976// ----------------------------------------------------------------------------
6977
6982typedef enum fplKey {
6983 fplKey_None = 0,
6984
6985 // 0x0-0x07: Undefined
6986
6991
6992 // 0x0A-0x0B: Reserved
6993
6998
6999 // 0x0E-0x0F: Undefined
7000
7011
7012 // 0x15: IME-Keys
7013 // 0x16: Undefined
7014 // 0x17-0x19 IME-Keys
7015 // 0x1A: Undefined
7016
7019
7020 // 0x1C-0x1F: IME-Keys
7021
7054
7056 fplKey_0 = 0x30,
7058 fplKey_1 = 0x31,
7060 fplKey_2 = 0x32,
7062 fplKey_3 = 0x33,
7064 fplKey_4 = 0x34,
7066 fplKey_5 = 0x35,
7068 fplKey_6 = 0x36,
7070 fplKey_7 = 0x37,
7072 fplKey_8 = 0x38,
7074 fplKey_9 = 0x39,
7075
7076 // 0x3A-0x40: Undefined
7077
7079 fplKey_A = 0x41,
7081 fplKey_B = 0x42,
7083 fplKey_C = 0x43,
7085 fplKey_D = 0x44,
7087 fplKey_E = 0x45,
7089 fplKey_F = 0x46,
7091 fplKey_G = 0x47,
7093 fplKey_H = 0x48,
7095 fplKey_I = 0x49,
7097 fplKey_J = 0x4A,
7099 fplKey_K = 0x4B,
7101 fplKey_L = 0x4C,
7103 fplKey_M = 0x4D,
7105 fplKey_N = 0x4E,
7107 fplKey_O = 0x4F,
7109 fplKey_P = 0x50,
7111 fplKey_Q = 0x51,
7113 fplKey_R = 0x52,
7115 fplKey_S = 0x53,
7117 fplKey_T = 0x54,
7119 fplKey_U = 0x55,
7121 fplKey_V = 0x56,
7123 fplKey_W = 0x57,
7125 fplKey_X = 0x58,
7127 fplKey_Y = 0x59,
7129 fplKey_Z = 0x5A,
7130
7137
7138 // 0x5E: Reserved
7139
7222
7223 // 0x88-8F: Unassigned
7224
7229
7230 // 0x92-0x96: OEM specific
7231 // 0x97-0x9F: Unassigned
7232
7245
7246 // 0xA6-0xAC: Don't care
7247
7262
7263 // 0xB4-0xB9: Don't care
7264
7279
7280 // 0xC1-0xD7: Reserved
7281 // 0xD8-0xDA: Unassigned
7282
7293
7294 // 0xE0-0xFE: Don't care
7295} fplKey;
7296
7329
7342
7359
7372
7385
7416
7418
7435
7450
7467
7486
7501
7510
7549
7616
7631
7648
7667
7675
7682
7684
7685// ----------------------------------------------------------------------------
7691// ----------------------------------------------------------------------------
7692
7697#define FPL_MAX_KEYBOARD_STATE_COUNT 256
7698
7711
7716#define FPL_MAX_GAMEPAD_STATE_COUNT 4
7717
7726
7739
7746
7753
7760
7766fpl_platform_api bool fplQueryCursorPosition(int32_t *outX, int32_t *outY);
7767
7769
7770// ----------------------------------------------------------------------------
7776// ----------------------------------------------------------------------------
7777
7794
7807
7813
7818
7824
7830
7837
7843fpl_platform_api void fplSetWindowSize(const uint32_t width, const uint32_t height);
7844
7850
7856
7862
7868
7874
7880
7890fpl_platform_api bool fplSetWindowFullscreenSize(const bool value, const uint32_t fullscreenWidth, const uint32_t fullscreenHeight, const uint32_t refreshRate);
7891
7902fpl_platform_api bool fplSetWindowFullscreenRect(const bool value, const int32_t x, const int32_t y, const int32_t width, const int32_t height);
7903
7910
7917
7923
7930
7936fpl_platform_api void fplSetWindowPosition(const int32_t left, const int32_t top);
7937
7942fpl_platform_api void fplSetWindowTitle(const char *title);
7943
7950fpl_common_api char *fplGetWindowTitle(char *outTitle, const size_t maxOutTitleLength);
7951
7957
7964
7971
7973
7974// ----------------------------------------------------------------------------
7980// ----------------------------------------------------------------------------
7981
7998
8003typedef struct fplDisplayMode {
8005 uint32_t width;
8007 uint32_t height;
8009 uint32_t colorBits;
8011 uint32_t refreshRate;
8013
8019
8026fpl_platform_api size_t fplGetDisplays(fplDisplayInfo *outDisplays, const size_t maxDisplayCount);
8027
8034
8041
8049fpl_platform_api bool fplGetDisplayFromPosition(const int32_t x, const int32_t y, fplDisplayInfo *outInfo);
8050
8058fpl_platform_api size_t fplGetDisplayModes(const char *id, fplDisplayMode *outModes, const size_t maxDisplayModeCount);
8059
8061
8062// ----------------------------------------------------------------------------
8068// ----------------------------------------------------------------------------
8069
8076fpl_platform_api bool fplGetClipboardText(char *dest, const uint32_t maxDestLen);
8077
8084
8086#endif // FPL__ENABLE_WINDOW
8087
8088#if defined(FPL__ENABLE_VIDEO)
8089// ----------------------------------------------------------------------------
8095// ----------------------------------------------------------------------------
8096
8101typedef struct fplVideoRect {
8103 int32_t x;
8105 int32_t y;
8107 int32_t width;
8109 int32_t height;
8110} fplVideoRect;
8111
8120fpl_common_api fplVideoRect fplCreateVideoRectFromLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom);
8121
8142
8143#if defined(FPL__ENABLE_VIDEO_VULKAN)
8154#endif
8155
8156#if defined(FPL__ENABLE_VIDEO_OPENGL)
8165#endif
8166
8167#if defined(FPL_PLATFORM_WINDOWS)
8172typedef struct fplVideoWindowWin32 {
8174 fpl__Win32WindowHandle windowHandle;
8176 fpl__Win32DeviceContext deviceContext;
8177} fplVideoWindowWin32;
8178#endif
8179
8180#if defined(FPL_SUBPLATFORM_X11)
8185typedef struct fplVideoWindowX11 {
8187 fpl__X11Window window;
8189 fpl__X11Display display;
8191 fpl__X11Visual visual;
8193 int screen;
8194} fplVideoWindowX11;
8195#endif // FPL_SUBPLATFORM_X11
8196
8201typedef union fplVideoWindow {
8202#if defined(FPL_PLATFORM_WINDOWS)
8203 fplVideoWindowWin32 win32;
8204#elif defined(FPL_SUBPLATFORM_X11)
8205 fplVideoWindowX11 x11;
8206#endif
8210
8215typedef struct fplVideoSurface {
8218
8219#if defined(FPL__ENABLE_VIDEO_VULKAN)
8222#endif
8223
8224#if defined(FPL__ENABLE_VIDEO_OPENGL)
8227#endif
8228
8232
8233#if defined(FPL__ENABLE_VIDEO_VULKAN)
8244#endif // FPL__ENABLE_VIDEO_VULKAN
8245
8251#if defined(FPL__ENABLE_VIDEO_VULKAN)
8254#endif // FPL__ENABLE_VIDEO_VULKAN
8256 int dummy;
8258
8264
8271
8278
8285fpl_common_api bool fplResizeVideoBackBuffer(const uint32_t width, const uint32_t height);
8286
8291
8297fpl_common_api const void *fplGetVideoProcedure(const char *procName);
8298
8304
8312
8314#endif // FPL__ENABLE_VIDEO
8315
8316#if defined(FPL__ENABLE_AUDIO)
8317// ----------------------------------------------------------------------------
8323// ----------------------------------------------------------------------------
8324
8382
8388
8394
8400
8407
8413
8420
8427
8433
8440
8449
8457fpl_common_api uint32_t fplGetAudioDevices(const uint32_t maxDeviceCount, const uint32_t deviceInfoSize, fplAudioDeviceInfo *outDevices);
8458
8466
8473
8480
8487
8494fpl_common_api uint32_t fplGetAudioBufferSizeInFrames(const uint32_t sampleRate, const uint32_t bufferSizeInMilliSeconds);
8495
8502fpl_common_api uint32_t fplGetAudioBufferSizeInMilliseconds(const uint32_t sampleRate, const uint32_t frameCount);
8503
8510fpl_common_api uint32_t fplGetAudioFrameSizeInBytes(const fplAudioFormatType format, const uint16_t channelCount);
8511
8519fpl_common_api uint32_t fplGetAudioBufferSizeInBytes(const fplAudioFormatType format, const uint16_t channelCount, const uint32_t frameCount);
8520
8528fpl_common_api uint32_t fplGetTargetAudioFrameCount(const uint32_t inputFrameCount, const uint32_t inputSampleRate, const uint32_t outputSampleRate);
8529
8536
8543
8550
8557
8565
8573fpl_common_api fplAudioFormatU64 fplEncodeAudioFormatU64(const uint32_t sampleRate, const uint16_t channels, const fplAudioFormatType type);
8574
8583fpl_common_api bool fplDecodeAudioFormatU64(const fplAudioFormatU64 format64, uint32_t *outSampleRate, uint16_t *outChannels, fplAudioFormatType *outType);
8584
8586#endif // FPL__ENABLE_AUDIO
8587
8588// ----------------------------------------------------------------------------
8594// ----------------------------------------------------------------------------
8595
8606
8614fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen);
8615
8623fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen);
8624
8632fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen);
8633
8635
8636// Ignore any doxygen documentation from here
8638
8639// ****************************************************************************
8640//
8641// > EXPORT
8642//
8643// Internal functions for static library
8644// Entry-Point forward declaration
8645//
8646// ****************************************************************************
8647#if defined(FPL_PLATFORM_WINDOWS)
8648# if defined(FPL_ENTRYPOINT)
8649// @NOTE(final): Required for access "main" from the actual win32 entry point
8650fpl_main int main(int argc, char **args);
8651# endif
8652#endif // FPL_PLATFORM_WINDOWS
8653
8654#endif // FPL_HEADER_H
8655
8656// ****************************************************************************
8657//
8658// > IMPLEMENTATION
8659//
8660// FPL uses several implementation blocks to structure things in categories.
8661// Each block has its own ifdef definition to collapse it down if needed.
8662// But the baseline structure is the following:
8663//
8664// - Compiler settings (Disable warnings, etc.)
8665// - Platform Constants & Types (All required structs, Constants, Global variables, etc.)
8666// - Common implementations
8667// - Actual platform implementations (Win32, Linux)
8668// - Sub platform implementations (X11, POSIX, STD)
8669// - Backend implementations (Video: OpenGL/Software/Vulkan, Audio: DirectSound/Alsa)
8670// - Systems (Audio, Video, Window systems)
8671// - Core (Init & Release of the specific platform by selection)
8672//
8673// You can use the following strings to search for implementation blocks - including the > prefix:
8674//
8675// > COMPILER_CONFIG
8676// > PLATFORM_INCLUDES
8677//
8678// > INTERNAL_TOP
8679// > INTERNAL_LOGGING
8680//
8681// > PLATFORM_CONSTANTS
8682// > UTILITY_FUNCTIONS
8683//
8684// > TYPES
8685// > TYPES_WIN32
8686// > TYPES_POSIX
8687// > TYPES_LINUX
8688// > TYPES_X11
8689//
8690// > PLATFORM_STATES
8691//
8692// > COMMON
8693//
8694// > WIN32_PLATFORM
8695// > POSIX_SUBPLATFORM (Linux, Unix)
8696// > STD_STRINGS_SUBPLATFORM
8697// > STD_CONSOLE_SUBPLATFORM
8698// > X11_SUBPLATFORM
8699// > LINUX_PLATFORM
8700// > UNIX_PLATFORM
8701//
8702// > VIDEO_BACKENDS
8703// > VIDEO_BACKEND_OPENGL_WIN32
8704// > VIDEO_BACKEND_OPENGL_X11
8705// > VIDEO_BACKEND_SOFTWARE_WIN32
8706// > VIDEO_BACKEND_SOFTWARE_X11
8707// > VIDEO_BACKEND_VULKAN
8708//
8709// > AUDIO_BACKENDS
8710// > AUDIO_BACKEND_DIRECTSOUND
8711// > AUDIO_BACKEND_ALSA
8712//
8713// > SYSTEM_AUDIO_L1
8714// > SYSTEM_VIDEO_L1
8715// > SYSTEM_WINDOW
8716// > SYSTEM_AUDIO_L2
8717// > SYSTEM_VIDEO_L2 (Video backbuffer access and present of the frame)
8718// > SYSTEM_INIT (Init & Release of the Platform)
8719//
8720// ****************************************************************************
8721#if (defined(FPL_IMPLEMENTATION) || FPL_IS_IDE) && !defined(FPL__IMPLEMENTED)
8722#define FPL__IMPLEMENTED
8723
8724// ############################################################################
8725//
8726// > COMPILER_CONFIG
8727//
8728// ############################################################################
8729#if !defined(FPL__COMPILER_CONFIG_DEFINED)
8730#define FPL__COMPILER_CONFIG_DEFINED
8731
8732//
8733// Compiler warnings
8734//
8735#if defined(FPL_COMPILER_MSVC)
8736
8737 // Start to overwrite warning settings (MSVC)
8738# pragma warning( push )
8739
8740 // Disable noexcept compiler warning for C++
8741# pragma warning( disable : 4577 )
8742 // Disable "switch statement contains 'default' but no 'case' labels" compiler warning for C++
8743# pragma warning( disable : 4065 )
8744 // Disable "conditional expression is constant" warning
8745# pragma warning( disable : 4127 )
8746 // Disable "unreferenced formal parameter" warning
8747# pragma warning( disable : 4100 )
8748 // Disable "nonstandard extension used: nameless struct/union" warning
8749# pragma warning( disable : 4201 )
8750 // Disable "local variable is initialized but not referenced" warning
8751# pragma warning( disable : 4189 )
8752 // Disable "nonstandard extension used: non-constant aggregate initializer" warning
8753# pragma warning( disable : 4204 )
8754
8755#elif defined(FPL_COMPILER_GCC)
8756
8757 // Start to overwrite warning settings (GCC)
8758# pragma GCC diagnostic push
8759// Disable warning -Wunused-variable
8760# pragma GCC diagnostic ignored "-Wunused-variable"
8761// Disable warning -Wunused-function
8762# pragma GCC diagnostic ignored "-Wunused-function"
8763
8764#elif defined(FPL_COMPILER_CLANG)
8765
8766 // Start to overwrite warning settings (Clang)
8767# pragma clang diagnostic push
8768
8769// Disable warning -Wunused-variable
8770# pragma clang diagnostic ignored "-Wunused-variable"
8771// Disable warning -Wunused-function
8772# pragma clang diagnostic ignored "-Wunused-function"
8773
8774#endif // FPL_COMPILER
8775
8776#endif // FPL__COMPILER_CONFIG_DEFINED
8777
8778// ############################################################################
8779//
8780// > PLATFORM_INCLUDES
8781//
8782// ############################################################################
8783#if !defined(FPL__PLATFORM_INCLUDES_DEFINED)
8784#define FPL__PLATFORM_INCLUDES_DEFINED
8785
8786#if !defined(FPL__HAS_PLATFORM_INCLUDES)
8787# define FPL__HAS_PLATFORM_INCLUDES
8788
8789# if defined(FPL_PLATFORM_WINDOWS)
8790 // @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!
8791# if !defined(NOMINMAX)
8792# define NOMINMAX
8793# endif
8794 // @NOTE(final): For now we dont want any network, com or gdi stuff at all, maybe later who knows.
8795# if !defined(WIN32_LEAN_AND_MEAN)
8796# define WIN32_LEAN_AND_MEAN 1
8797# endif
8798 // @STUPID(final): Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here"
8799struct IUnknown;
8800# include <windows.h> // Win32 api
8801# if _WIN32_WINNT < 0x0600
8802# error "Windows Vista or higher required!"
8803# endif
8804# endif // FPL_PLATFORM_WINDOWS
8805
8806# if defined(FPL_SUBPLATFORM_POSIX)
8807# include <pthread.h> // pthread_t, pthread_mutex_, pthread_cond_, pthread_barrier_
8808# include <sched.h> // sched_param, sched_get_priority_max, SCHED_FIFO
8809# include <semaphore.h> // sem_t
8810# include <dirent.h> // DIR, dirent
8811# endif // FPL_SUBPLATFORM_POSIX
8812
8813# if defined(FPL_SUBPLATFORM_X11)
8814# include <X11/X.h> // Window
8815# include <X11/Xlib.h> // Display
8816# include <X11/Xutil.h> // XVisualInfo
8817# include <X11/Xatom.h> // XA_CARDINAL
8818# endif // FPL_SUBPLATFORM_X11
8819
8820#endif // !FPL__HAS_PLATFORM_INCLUDES
8821
8822//
8823// Test OS handles
8824//
8825#if defined(FPL_PLATFORM_WINDOWS)
8826fplStaticAssert(sizeof(fpl__Win32Handle) >= sizeof(HANDLE));
8827fplStaticAssert(sizeof(fpl__Win32LibraryHandle) >= sizeof(HANDLE));
8828fplStaticAssert(sizeof(fpl__Win32FileHandle) >= sizeof(HANDLE));
8829fplStaticAssert(sizeof(fpl__Win32ThreadHandle) >= sizeof(HANDLE));
8830fplStaticAssert(sizeof(fpl__Win32MutexHandle) >= sizeof(CRITICAL_SECTION));
8831fplStaticAssert(sizeof(fpl__Win32SemaphoreHandle) >= sizeof(HANDLE));
8832fplStaticAssert(sizeof(fpl__Win32ConditionVariable) >= sizeof(CONDITION_VARIABLE));
8833#elif defined(FPL_SUBPLATFORM_POSIX)
8834fplStaticAssert(sizeof(fpl__POSIXLibraryHandle) >= sizeof(void *));
8835fplStaticAssert(sizeof(fpl__POSIXFileHandle) >= sizeof(int));
8836fplStaticAssert(sizeof(fpl__POSIXDirHandle) >= sizeof(DIR *));
8837fplStaticAssert(sizeof(fpl__POSIXThreadHandle) >= sizeof(pthread_t));
8838fplStaticAssert(sizeof(fpl__POSIXMutexHandle) >= sizeof(pthread_mutex_t));
8839fplStaticAssert(sizeof(fpl__POSIXSemaphoreHandle) >= sizeof(sem_t));
8840fplStaticAssert(sizeof(fpl__POSIXConditionVariable) >= sizeof(pthread_cond_t));
8841#endif // FPL_PLATFORM_WINDOWS / FPL_SUBPLATFORM_POSIX
8842#if defined(FPL_PLATFORM_LINUX)
8843fplStaticAssert(sizeof(fpl__LinuxSignalHandle) >= sizeof(int));
8844#endif // FPL_PLATFORM_LINUX
8845
8846//
8847// Compiler Includes
8848//
8849#if defined(FPL_COMPILER_MSVC)
8850# include <intrin.h> // __cpuid, _Interlocked*
8851#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
8852# if defined(FPL_ARCH_X86) || defined(FPL_ARCH_X64)
8853# include <cpuid.h> // __cpuid_count
8854# endif // X86 or X64
8855#endif
8856
8857// Only include C-Runtime functions when CRT is enabled
8858#if !defined(FPL_NO_CRT)
8859# include <stdio.h> // stdin, stdout, stderr, fprintf, vfprintf, vsnprintf, getchar
8860# include <stdlib.h> // wcstombs, mbstowcs, getenv
8861# include <locale.h> // setlocale, struct lconv, localeconv
8862#endif
8863
8864#endif // FPL__PLATFORM_INCLUDES_DEFINED
8865
8866// ############################################################################
8867//
8868// > INTERNAL_TOP
8869//
8870// ############################################################################
8871#if !defined(FPL__INTERNAL_TOP_DEFINED)
8872#define FPL__INTERNAL_TOP_DEFINED
8873
8874// Module constants used for logging
8875#define FPL__MODULE_CORE "Core"
8876#define FPL__MODULE_FILES "Files"
8877#define FPL__MODULE_THREADING "Threading"
8878#define FPL__MODULE_MEMORY "Memory"
8879#define FPL__MODULE_WINDOW "Window"
8880#define FPL__MODULE_LIBRARIES "Libraries"
8881#define FPL__MODULE_OS "OS"
8882#define FPL__MODULE_HARDWARE "Hardware"
8883#define FPL__MODULE_STRINGS "Strings"
8884#define FPL__MODULE_PATHS "Paths"
8885#define FPL__MODULE_ARGS "Arguments"
8886
8887#define FPL__MODULE_AUDIO "Audio"
8888#define FPL__MODULE_AUDIO_DIRECTSOUND "DirectSound"
8889#define FPL__MODULE_AUDIO_ALSA "ALSA"
8890
8891#define FPL__MODULE_VIDEO "Video"
8892#define FPL__MODULE_VIDEO_OPENGL "OpenGL"
8893#define FPL__MODULE_VIDEO_VULKAN "Vulkan"
8894#define FPL__MODULE_VIDEO_SOFTWARE "Software"
8895
8896#define FPL__MODULE_WIN32 "Win32"
8897#define FPL__MODULE_XINPUT "XInput"
8898
8899#define FPL__MODULE_LINUX "Linux"
8900#define FPL__MODULE_UNIX "Unix"
8901#define FPL__MODULE_POSIX "POSIX"
8902#define FPL__MODULE_PTHREAD "pthread"
8903#define FPL__MODULE_X11 "X11"
8904#define FPL__MODULE_GLX "GLX"
8905
8906//
8907// Enum macros
8908//
8909
8910// Get number of enum values from the specifie first and last enum value
8911#define FPL__ENUM_COUNT(first, last) ((last) - (first) + 1)
8912
8913// Get array index for the specified enum value with the first and last enum value
8914#define FPL__ENUM_VALUE_TO_ARRAY_INDEX(value, first, last) (((value) >= (first) && (value) <= (last)) ? ((value) - (first)) : 0)
8915
8916// Define the name for a enum value in a mapping table
8917#define FPL__ENUM_NAME(str, enumValue) str
8918
8919//
8920// Internal memory
8921//
8922fpl_internal void *fpl__AllocateMemory(const fplMemoryAllocationSettings *allocSettings, const size_t size, const size_t alignment);
8923fpl_internal void fpl__ReleaseMemory(const fplMemoryAllocationSettings *allocSettings, void *ptr);
8924
8925#endif // FPL__INTERNAL_TOP_DEFINED
8926
8927// ############################################################################
8928//
8929// > INTERNAL_LOGGING
8930//
8931// ############################################################################
8932#if !defined(FPL__INTERNAL_LOGGING_DEFINED)
8933#define FPL__INTERNAL_LOGGING_DEFINED
8934
8935#define FPL__MODULE_CONCAT(mod, format) "[" mod "] " format
8936
8937#if defined(FPL__ENABLE_LOGGING)
8938fpl_globalvar fplLogSettings fpl__global__LogSettings = fplZeroInit;
8939
8940#define FPL__LOGLEVEL_COUNT FPL__ENUM_COUNT(fplLogLevel_First, fplLogLevel_Last)
8943fpl_globalvar const char *fpl__LogLevelNameTable[] = {
8944 FPL__ENUM_NAME("All", fplLogLevel_All),
8945 FPL__ENUM_NAME("Critical", fplLogLevel_Critical),
8946 FPL__ENUM_NAME("Error", fplLogLevel_Error),
8947 FPL__ENUM_NAME("Warning", fplLogLevel_Warning),
8948 FPL__ENUM_NAME("Info", fplLogLevel_Info),
8949 FPL__ENUM_NAME("Verbose", fplLogLevel_Verbose),
8950 FPL__ENUM_NAME("Debug", fplLogLevel_Debug),
8951 FPL__ENUM_NAME("Trace", fplLogLevel_Trace),
8952};
8953fplStaticAssert(fplArrayCount(fpl__LogLevelNameTable) == FPL__LOGLEVEL_COUNT);
8954
8955fpl_internal const char *fpl__LogLevelToString(const fplLogLevel level) {
8956 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(level, fplLogLevel_First, fplLogLevel_Last);
8957 const char *result = fpl__LogLevelNameTable[index];
8958 return(result);
8959}
8960
8961fpl_internal void fpl__LogWrite(const char *funcName, const int lineNumber, const fplLogLevel level, const char *message) {
8962 fplLogSettings *settings = &fpl__global__LogSettings;
8963 if (!settings->isInitialized) {
8964#if defined(FPL_LOG_MULTIPLE_WRITERS)
8965 settings->criticalWriter.console.logToError = true;
8966 settings->criticalWriter.flags = fplLogWriterFlags_ErrorConsole | fplLogWriterFlags_DebugOut;
8967 settings->errorWriter = settings->criticalWriter;
8968 settings->warningWriter = settings->criticalWriter;
8970 settings->verboseWriter = settings->infoWriter;
8971 settings->debugWriter.flags = fplLogWriterFlags_DebugOut;
8972#else
8974#endif
8975 settings->maxLevel = fplLogLevel_Warning;
8976 settings->isInitialized = true;
8977 }
8978
8979 if ((settings->maxLevel == -1) || (level <= settings->maxLevel)) {
8980#if defined(FPL_LOG_MULTIPLE_WRITERS)
8981 fplAssert(level < fplArrayCount(settings->writers));
8982 const fplLogWriter *writer = &settings->writers[(int)level];
8983#else
8984 const fplLogWriter *writer = &settings->writers[0];
8985#endif
8986 const char *levelStr = fpl__LogLevelToString(level);
8987
8989 fplConsoleFormatOut("[%s:%d][%s] %s\n", funcName, lineNumber, levelStr, message);
8990 }
8991 if (writer->flags & fplLogWriterFlags_ErrorConsole) {
8992 fplConsoleFormatError("[%s:%d][%s] %s\n", funcName, lineNumber, levelStr, message);
8993 }
8994 if (writer->flags & fplLogWriterFlags_DebugOut) {
8995 fplDebugFormatOut("[%s:%d][%s] %s\n", funcName, lineNumber, levelStr, message);
8996 }
8997 if (writer->flags & fplLogWriterFlags_Custom && writer->custom.callback != fpl_null) {
8998 writer->custom.callback(funcName, lineNumber, level, message);
8999 }
9000 }
9001}
9002fpl_internal void fpl__LogWriteArgs(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, va_list argList) {
9003 va_list listCopy;
9004 va_copy(listCopy, argList);
9005 char buffer[FPL_MAX_BUFFER_LENGTH];
9006 size_t formattedLen = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, listCopy);
9007 if (formattedLen > 0) {
9008 fpl__LogWrite(funcName, lineNumber, level, buffer);
9009 }
9010 va_end(listCopy);
9011}
9012
9013fpl_internal void fpl__LogWriteVarArgs(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, ...) {
9014 va_list argList;
9015 va_start(argList, format);
9016 fpl__LogWriteArgs(funcName, lineNumber, level, format, argList);
9017 va_end(argList);
9018}
9019
9020# define FPL_LOG(lvl, mod, format, ...) fpl__LogWriteVarArgs(FPL_FUNCTION_NAME, __LINE__, lvl, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
9021# define FPL_LOG_CRITICAL(mod, format, ...) FPL_LOG(fplLogLevel_Critical, mod, format, ## __VA_ARGS__)
9022# define FPL_LOG_ERROR(mod, format, ...) FPL_LOG(fplLogLevel_Error, mod, format, ## __VA_ARGS__)
9023# define FPL_LOG_WARN(mod, format, ...) FPL_LOG(fplLogLevel_Warning, mod, format, ## __VA_ARGS__)
9024# define FPL_LOG_INFO(mod, format, ...) FPL_LOG(fplLogLevel_Info, mod, format, ## __VA_ARGS__)
9025# define FPL_LOG_VERBOSE(mod, format, ...) FPL_LOG(fplLogLevel_Verbose, mod, format, ## __VA_ARGS__)
9026# define FPL_LOG_DEBUG(mod, format, ...) FPL_LOG(fplLogLevel_Debug, mod, format, ## __VA_ARGS__)
9027# define FPL_LOG_TRACE(mod, format, ...) FPL_LOG(fplLogLevel_Trace, mod, format, ## __VA_ARGS__)
9028
9029# define FPL__LOG_FUNCTION_N(mod, name) FPL_LOG(fplLogLevel_Debug, mod, "-> %s()", name)
9030# define FPL_LOG_FUNCTION(mod) FPL__LOG_FUNCTION_N(mod, FPL_FUNCTION_NAME)
9031
9032#else
9033
9034# define FPL_LOG(lvl, mod, format, ...)
9035# define FPL_LOG_CRITICAL(mod, format, ...)
9036# define FPL_LOG_ERROR(mod, format, ...)
9037# define FPL_LOG_WARN(mod, format, ...)
9038# define FPL_LOG_INFO(mod, format, ...)
9039# define FPL_LOG_VERBOSE(mod, format, ...)
9040# define FPL_LOG_DEBUG(mod, format, ...)
9041# define FPL_LOG_FUNCTION(mod)
9042
9043#endif
9044
9045//
9046// Error handling
9047//
9048
9049#define FPL__M_CRITICAL(funcName, line, mod, format, ...) fpl__HandleError(funcName, line, fplLogLevel_Critical, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
9050#define FPL__M_ERROR(funcName, line, mod, format, ...) fpl__HandleError(funcName, line, fplLogLevel_Error, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
9051#define FPL__M_WARNING(funcName, line, mod, format, ...) fpl__HandleError(funcName, line, fplLogLevel_Warning, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
9052
9053#define FPL__CRITICAL(mod, format, ...) FPL__M_CRITICAL(FPL_FUNCTION_NAME, __LINE__, mod, format, ## __VA_ARGS__)
9054#define FPL__ERROR(mod, format, ...) FPL__M_ERROR(FPL_FUNCTION_NAME, __LINE__, mod, format, ## __VA_ARGS__)
9055#define FPL__WARNING(mod, format, ...) FPL__M_WARNING(FPL_FUNCTION_NAME, __LINE__, mod, format, ## __VA_ARGS__)
9056
9057#endif // FPL__INTERNAL_LOGGING_DEFINED
9058
9059// ############################################################################
9060//
9061// > PLATFORM_CONSTANTS
9062//
9063// ############################################################################
9064#if !defined(FPL__PLATFORM_CONSTANTS_DEFINED)
9065#define FPL__PLATFORM_CONSTANTS_DEFINED
9066
9067// One cacheline worth of padding
9068#define FPL__ARBITARY_PADDING 64
9069// Small padding to split sections in memory blocks
9070#define FPL__MEMORY_PADDING sizeof(uintptr_t)
9071
9072fpl_globalvar struct fpl__PlatformAppState *fpl__global__AppState = fpl_null;
9073
9074fpl_internal void fpl__HandleError(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, ...);
9075#endif // FPL__PLATFORM_CONSTANTS_DEFINED
9076
9077// ############################################################################
9078//
9079// > UTILITY_FUNCTIONS
9080//
9081// ############################################################################
9082fpl_internal void *fpl__AllocateMemory(const fplMemoryAllocationSettings *allocSettings, const size_t size, const size_t alignment) {
9083 if (allocSettings->mode == fplMemoryAllocationMode_Custom) {
9084 if (allocSettings->allocateCallback != fpl_null && allocSettings->releaseCallback != fpl_null) {
9085 return allocSettings->allocateCallback(allocSettings->userData, size, alignment);
9086 }
9087 }
9088 return fplMemoryAlignedAllocate(size, alignment);
9089}
9090
9091fpl_internal void fpl__ReleaseMemory(const fplMemoryAllocationSettings *allocSettings, void *ptr) {
9092 if (allocSettings->mode == fplMemoryAllocationMode_Custom) {
9093 if (allocSettings->allocateCallback != fpl_null && allocSettings->releaseCallback != fpl_null) {
9094 allocSettings->releaseCallback(allocSettings->userData, ptr);
9095 return;
9096 }
9097 }
9099}
9100
9101// Forward declarations of internal memory
9102fpl_internal void *fpl__AllocateDynamicMemory(const size_t size, const size_t alignment);
9103fpl_internal void fpl__ReleaseDynamicMemory(void *ptr);
9104fpl_internal void *fpl__AllocateTemporaryMemory(const size_t size, const size_t alignment);
9105fpl_internal void fpl__ReleaseTemporaryMemory(void *ptr);
9106
9107fpl_internal uint32_t fpl__NextPowerOfTwo(const uint32_t input) {
9108 uint32_t x = input;
9109 x--;
9110 x |= x >> 1;
9111 x |= x >> 2;
9112 x |= x >> 4;
9113 x |= x >> 8;
9114 x |= x >> 16;
9115 x++;
9116 return(x);
9117}
9118fpl_internal uint32_t fpl__PrevPowerOfTwo(const uint32_t input) {
9119 uint32_t result = fpl__NextPowerOfTwo(input) >> 1;
9120 return(result);
9121}
9122
9123fpl_internal uint32_t fpl__RoundToPowerOfTwo(const uint32_t input) {
9124 uint32_t prev = fpl__PrevPowerOfTwo(input);
9125 uint32_t next = fpl__NextPowerOfTwo(input);
9126 if ((next - input) < (input - prev)) {
9127 return prev;
9128 } else {
9129 return next;
9130 }
9131}
9132
9133fpl_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) {
9134 for (size_t i = 0; i < maxWildcardCount; ++i) {
9135 const char *wildcard = wildcards[i];
9136 if (fplIsStringMatchWildcard(line, wildcard)) {
9137 size_t index = *outCount;
9138 char *target = outLines[index];
9139 fplCopyString(line, target, maxLineSize);
9140 *outCount = index + 1;
9141 break;
9142 }
9143 }
9144 bool result = *outCount < maxLineCount;
9145 return(result);
9146}
9147
9148fpl_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) {
9149 if (filePath == fpl_null || wildcards == fpl_null || maxWildcardCount == 0 || maxLineSize == 0 || maxLineCount == 0 || outLines == fpl_null) {
9150 return(0);
9151 }
9152 // @NOTE(final): Forced Zero-Terminator is not nessecary here, but we do it here to debug it better
9153 // This function supports maxLineSize < fplArrayCount(buffer)
9154 // We allocate the line buffer on the stack because we do not know how large the line will be on compile time
9155 size_t result = 0;
9156 fplFileHandle fileHandle = fplZeroInit;
9157 if (fplFileOpenBinary(filePath, &fileHandle)) {
9158 char *line = (char *)fpl__AllocateTemporaryMemory(maxLineSize, 8);
9159 char buffer[FPL_MAX_BUFFER_LENGTH];
9160 const size_t maxBufferSize = fplArrayCount(buffer) - 1;
9161 size_t bytesRead = 0;
9162 size_t posLineBytes = 0;
9163 bool done = false;
9164 while (!done && ((bytesRead = fplFileReadBlock(&fileHandle, maxBufferSize, &buffer[0], maxBufferSize)) > 0)) {
9165 buffer[bytesRead] = 0;
9166 char *start = &buffer[0];
9167 char *p = start;
9168 size_t readPos = 0;
9169 size_t lineSizeToRead = 0;
9170 while (readPos < bytesRead) {
9171 if (*p == '\n') {
9172 size_t remainingLineBytes = maxLineSize - posLineBytes;
9173 char *lineTargetP = line + posLineBytes;
9174 if (lineSizeToRead < remainingLineBytes) {
9175 fplCopyStringLen(start, lineSizeToRead, lineTargetP, remainingLineBytes);
9176 } else {
9177 fplCopyStringLen(start, remainingLineBytes - 1, lineTargetP, remainingLineBytes);
9178 }
9179 if (!fpl__AddLineWhenAnyMatches(line, wildcards, maxWildcardCount, maxLineSize, maxLineCount, outLines, &result)) {
9180 done = true;
9181 break;
9182 }
9183 start = p + 1;
9184 line[0] = 0;
9185 lineSizeToRead = 0;
9186 posLineBytes = 0;
9187 } else {
9188 ++lineSizeToRead;
9189 }
9190 ++p;
9191 ++readPos;
9192 }
9193 if (done) {
9194 break;
9195 }
9196 if (lineSizeToRead > 0) {
9197 size_t remainingLineBytes = maxLineSize - posLineBytes;
9198 char *lineTargetP = line + posLineBytes;
9199 if (lineSizeToRead < remainingLineBytes) {
9200 fplCopyStringLen(start, lineSizeToRead, lineTargetP, remainingLineBytes);
9201 posLineBytes += lineSizeToRead;
9202 if (bytesRead <= maxBufferSize) {
9203 if (!fpl__AddLineWhenAnyMatches(line, wildcards, maxWildcardCount, maxLineSize, maxLineCount, outLines, &result)) {
9204 done = true;
9205 }
9206 }
9207 } else {
9208 fplCopyStringLen(start, remainingLineBytes - 1, lineTargetP, remainingLineBytes);
9209 line[0] = 0;
9210 lineSizeToRead = 0;
9211 posLineBytes = 0;
9212 if (!fpl__AddLineWhenAnyMatches(line, wildcards, maxWildcardCount, maxLineSize, maxLineCount, outLines, &result)) {
9213 done = true;
9214 }
9215 }
9216 }
9217 }
9218 fpl__ReleaseTemporaryMemory(line);
9219 fplFileClose(&fileHandle);
9220 }
9221 return(result);
9222}
9223
9224fpl_internal void fpl__ParseVersionString(const char *versionStr, fplVersionInfo *versionInfo) {
9225 fplCopyString(versionStr, versionInfo->fullName, fplArrayCount(versionInfo->fullName));
9226 if (versionStr != fpl_null) {
9227 const char *p = versionStr;
9228 for (int i = 0; i < 4; ++i) {
9229 const char *digitStart = p;
9230 while (*p >= '0' && *p <= '9') {
9231 ++p;
9232 }
9233 size_t len = p - digitStart;
9234 if (len <= fplArrayCount(versionInfo->version.values[i])) {
9235 fplCopyStringLen(digitStart, len, versionInfo->version.values[i], fplArrayCount(versionInfo->version.values[i]));
9236 } else {
9237 versionInfo->version.values[i][0] = 0;
9238 }
9239 if (*p != '.' && *p != '-') break;
9240 ++p;
9241 }
9242 }
9243}
9244
9245// ****************************************************************************
9246//
9247// > TYPES
9248//
9249// This implementation block includes all the required platform-specific
9250// header files and defines all the constants, structures and types.
9251//
9252// ****************************************************************************
9253
9254// ############################################################################
9255//
9256// > TYPES_WIN32
9257//
9258// ############################################################################
9259#if defined(FPL_PLATFORM_WINDOWS)
9260# include <windowsx.h> // Macros for window messages
9261# include <shlobj.h> // SHGetFolderPath
9262# include <xinput.h> // XInputGetState
9263# include <shellapi.h> // HDROP
9264
9265# if defined(FPL_IS_CPP)
9266# define fpl__Win32IsEqualGuid(a, b) InlineIsEqualGUID(a, b)
9267# define fpl__Win32CopyGuid(src, dst) fplMemoryCopy(src, sizeof(*(src)), dst)
9268# else
9269# define fpl__Win32IsEqualGuid(a, b) InlineIsEqualGUID(&a, &b)
9270# define fpl__Win32CopyGuid(src, dst) fplMemoryCopy(src, sizeof(*(src)), dst)
9271# endif
9272
9273fpl_globalvar const fpl__Win32Guid FPL__WIN32_GUID_ZERO = { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } };
9274
9275fpl_internal const char *fpl__Win32FormatGuidString(char *buffer, const size_t maxBufferLen, const fpl__Win32Guid *guid) {
9276 fplStringFormat(buffer, maxBufferLen, "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
9277 guid->Data1, guid->Data2, guid->Data3,
9278 guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
9279 guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
9280 return(buffer);
9281}
9282
9283// Little macro to not write 5 lines of code all the time
9284#define FPL__WIN32_LOAD_LIBRARY_BREAK(mod, target, libName) \
9285 (target) = LoadLibraryA(libName); \
9286 if((target) == fpl_null) { \
9287 FPL__WARNING(mod, "Failed loading library '%s'", (libName)); \
9288 break; \
9289 }
9290#define FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK(mod, libHandle, libName, target, type, name) \
9291 (target)->name = (type *)(void *)GetProcAddress(libHandle, #name); \
9292 if ((target)->name == fpl_null) { \
9293 FPL__WARNING(mod, "Failed getting procedure address '%s' from library '%s'", #name, libName); \
9294 break; \
9295 }
9296#if !defined(FPL_NO_RUNTIME_LINKING)
9297# define FPL__WIN32_LOAD_LIBRARY FPL__WIN32_LOAD_LIBRARY_BREAK
9298# define FPL__WIN32_GET_FUNCTION_ADDRESS FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK
9299#else
9300# define FPL__WIN32_LOAD_LIBRARY(mod, target, libName)
9301# define FPL__WIN32_GET_FUNCTION_ADDRESS(mod, libHandle, libName, target, type, name) \
9302 (target)->name = name
9303#endif
9304
9305//
9306// XInput
9307//
9308#define FPL__FUNC_XINPUT_XInputGetState(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
9309typedef FPL__FUNC_XINPUT_XInputGetState(fpl__win32_func_XInputGetState);
9310FPL__FUNC_XINPUT_XInputGetState(fpl__Win32XInputGetStateStub) {
9311 return(ERROR_DEVICE_NOT_CONNECTED);
9312}
9313#define FPL__FUNC_XINPUT_XInputGetCapabilities(name) DWORD WINAPI name(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities)
9314typedef FPL__FUNC_XINPUT_XInputGetCapabilities(fpl__win32_func_XInputGetCapabilities);
9315FPL__FUNC_XINPUT_XInputGetCapabilities(fpl__Win32XInputGetCapabilitiesStub) {
9316 return(ERROR_DEVICE_NOT_CONNECTED);
9317}
9318typedef struct fpl__Win32XInputApi {
9319 HMODULE xinputLibrary;
9320 fpl__win32_func_XInputGetState *XInputGetState;
9321 fpl__win32_func_XInputGetCapabilities *XInputGetCapabilities;
9322} fpl__Win32XInputApi;
9323
9324fpl_internal void fpl__Win32UnloadXInputApi(fpl__Win32XInputApi *xinputApi) {
9325 fplAssert(xinputApi != fpl_null);
9326 if (xinputApi->xinputLibrary) {
9327 FPL_LOG_DEBUG("XInput", "Unload XInput Library");
9328 FreeLibrary(xinputApi->xinputLibrary);
9329 xinputApi->xinputLibrary = fpl_null;
9330 xinputApi->XInputGetState = fpl__Win32XInputGetStateStub;
9331 xinputApi->XInputGetCapabilities = fpl__Win32XInputGetCapabilitiesStub;
9332 }
9333}
9334
9335fpl_internal void fpl__Win32LoadXInputApi(fpl__Win32XInputApi *xinputApi) {
9336 fplAssert(xinputApi != fpl_null);
9337 const char *xinputFileNames[] = {
9338 "xinput1_4.dll",
9339 "xinput1_3.dll",
9340 "xinput9_1_0.dll",
9341 };
9342 bool result = false;
9343 for (uint32_t index = 0; index < fplArrayCount(xinputFileNames); ++index) {
9344 const char *libName = xinputFileNames[index];
9345 fplClearStruct(xinputApi);
9346 do {
9347 HMODULE libHandle = fpl_null;
9348 FPL__WIN32_LOAD_LIBRARY_BREAK(FPL__MODULE_XINPUT, libHandle, libName);
9349 xinputApi->xinputLibrary = libHandle;
9350 FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK(FPL__MODULE_XINPUT, libHandle, libName, xinputApi, fpl__win32_func_XInputGetState, XInputGetState);
9351 FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK(FPL__MODULE_XINPUT, libHandle, libName, xinputApi, fpl__win32_func_XInputGetCapabilities, XInputGetCapabilities);
9352 result = true;
9353 } while (0);
9354 if (result) {
9355 break;
9356 }
9357 fpl__Win32UnloadXInputApi(xinputApi);
9358 }
9359
9360 if (!result) {
9361 xinputApi->XInputGetState = fpl__Win32XInputGetStateStub;
9362 xinputApi->XInputGetCapabilities = fpl__Win32XInputGetCapabilitiesStub;
9363 }
9364}
9365
9366//
9367// WINAPI functions
9368//
9369
9370// GDI32
9371#define FPL__FUNC_WIN32_ChoosePixelFormat(name) int WINAPI name(HDC hdc, CONST PIXELFORMATDESCRIPTOR *ppfd)
9372typedef FPL__FUNC_WIN32_ChoosePixelFormat(fpl__win32_func_ChoosePixelFormat);
9373#define FPL__FUNC_WIN32_SetPixelFormat(name) BOOL WINAPI name(HDC hdc, int format, CONST PIXELFORMATDESCRIPTOR *ppfd)
9374typedef FPL__FUNC_WIN32_SetPixelFormat(fpl__win32_func_SetPixelFormat);
9375#define FPL__FUNC_WIN32_DescribePixelFormat(name) int WINAPI name(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR ppfd)
9376typedef FPL__FUNC_WIN32_DescribePixelFormat(fpl__win32_func_DescribePixelFormat);
9377#define FPL__FUNC_WIN32_GetDeviceCaps(name) int WINAPI name(HDC hdc, int index)
9378typedef FPL__FUNC_WIN32_GetDeviceCaps(fpl__win32_func_GetDeviceCaps);
9379#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)
9380typedef FPL__FUNC_WIN32_StretchDIBits(fpl__win32_func_StretchDIBits);
9381#define FPL__FUNC_WIN32_DeleteObject(name) BOOL WINAPI name( _In_ HGDIOBJ ho)
9382typedef FPL__FUNC_WIN32_DeleteObject(fpl__win32_func_DeleteObject);
9383#define FPL__FUNC_WIN32_SwapBuffers(name) BOOL WINAPI name(HDC)
9384typedef FPL__FUNC_WIN32_SwapBuffers(fpl__win32_func_SwapBuffers);
9385#define FPL__FUNC_WIN32_CreateDIBSection(name) HBITMAP WINAPI name(HDC hdc, CONST BITMAPINFO *pbmi, UINT usage, VOID **ppvBits, HANDLE hSection, DWORD offset)
9386typedef FPL__FUNC_WIN32_CreateDIBSection(fpl__win32_func_CreateDIBSection);
9387#define FPL__FUNC_WIN32_CreateBitmap(name) HBITMAP WINAPI name(int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, CONST VOID *lpBits)
9388typedef FPL__FUNC_WIN32_CreateBitmap(fpl__win32_func_CreateBitmap);
9389#define FPL__FUNC_WIN32_CreateSolidBrush(name) HBRUSH WINAPI name(COLORREF color)
9390typedef FPL__FUNC_WIN32_CreateSolidBrush(fpl__win32_func_CreateSolidBrush);
9391
9392// ShellAPI
9393#define FPL__FUNC_WIN32_SHGetFolderPathW(name) HRESULT WINAPI name(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath)
9394typedef FPL__FUNC_WIN32_SHGetFolderPathW(fpl__win32_func_SHGetFolderPathW);
9395#define FPL__FUNC_WIN32_DragQueryFileW(name) UINT WINAPI name(HDROP hDrop, UINT iFile, LPWSTR lpszFile, UINT cch)
9396typedef FPL__FUNC_WIN32_DragQueryFileW(fpl__win32_func_DragQueryFileW);
9397#define FPL__FUNC_WIN32_DragAcceptFiles(name) void WINAPI name(HWND hWnd, BOOL fAccept)
9398typedef FPL__FUNC_WIN32_DragAcceptFiles(fpl__win32_func_DragAcceptFiles);
9399
9400// User32
9401#define FPL__FUNC_WIN32_RegisterClassExW(name) ATOM WINAPI name(CONST WNDCLASSEXW *)
9402typedef FPL__FUNC_WIN32_RegisterClassExW(fpl__win32_func_RegisterClassExW);
9403#define FPL__FUNC_WIN32_UnregisterClassW(name) BOOL WINAPI name(LPCWSTR lpClassName, HINSTANCE hInstance)
9404typedef FPL__FUNC_WIN32_UnregisterClassW(fpl__win32_func_UnregisterClassW);
9405#define FPL__FUNC_WIN32_ShowWindow(name) BOOL WINAPI name(HWND hWnd, int nCmdShow)
9406typedef FPL__FUNC_WIN32_ShowWindow(fpl__win32_func_ShowWindow);
9407#define FPL__FUNC_WIN32_DestroyWindow(name) BOOL WINAPI name(HWND hWnd)
9408typedef FPL__FUNC_WIN32_DestroyWindow(fpl__win32_func_DestroyWindow);
9409#define FPL__FUNC_WIN32_UpdateWindow(name) BOOL WINAPI name(HWND hWnd)
9410typedef FPL__FUNC_WIN32_UpdateWindow(fpl__win32_func_UpdateWindow);
9411#define FPL__FUNC_WIN32_TranslateMessage(name) BOOL WINAPI name(CONST MSG *lpMsg)
9412typedef FPL__FUNC_WIN32_TranslateMessage(fpl__win32_func_TranslateMessage);
9413#define FPL__FUNC_WIN32_DispatchMessageW(name) LRESULT WINAPI name(CONST MSG *lpMsg)
9414typedef FPL__FUNC_WIN32_DispatchMessageW(fpl__win32_func_DispatchMessageW);
9415#define FPL__FUNC_WIN32_PeekMessageW(name) BOOL WINAPI name(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
9416typedef FPL__FUNC_WIN32_PeekMessageW(fpl__win32_func_PeekMessageW);
9417#define FPL__FUNC_WIN32_DefWindowProcW(name) LRESULT WINAPI name(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
9418typedef FPL__FUNC_WIN32_DefWindowProcW(fpl__win32_func_DefWindowProcW);
9419#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)
9420typedef FPL__FUNC_WIN32_CreateWindowExW(fpl__win32_func_CreateWindowExW);
9421#define FPL__FUNC_WIN32_SetWindowPos(name) BOOL WINAPI name(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags)
9422typedef FPL__FUNC_WIN32_SetWindowPos(fpl__win32_func_SetWindowPos);
9423#define FPL__FUNC_WIN32_GetWindowPlacement(name) BOOL WINAPI name(HWND hWnd, WINDOWPLACEMENT *lpwndpl)
9424typedef FPL__FUNC_WIN32_GetWindowPlacement(fpl__win32_func_GetWindowPlacement);
9425#define FPL__FUNC_WIN32_SetWindowPlacement(name) BOOL WINAPI name(HWND hWnd, CONST WINDOWPLACEMENT *lpwndpl)
9426typedef FPL__FUNC_WIN32_SetWindowPlacement(fpl__win32_func_SetWindowPlacement);
9427#define FPL__FUNC_WIN32_GetClientRect(name) BOOL WINAPI name(HWND hWnd, LPRECT lpRect)
9428typedef FPL__FUNC_WIN32_GetClientRect(fpl__win32_func_GetClientRect);
9429#define FPL__FUNC_WIN32_GetWindowRect(name) BOOL WINAPI name(HWND hWnd, LPRECT lpRect)
9430typedef FPL__FUNC_WIN32_GetWindowRect(fpl__win32_func_GetWindowRect);
9431#define FPL__FUNC_WIN32_AdjustWindowRect(name) BOOL WINAPI name(LPRECT lpRect, DWORD dwStyle, BOOL bMenu)
9432typedef FPL__FUNC_WIN32_AdjustWindowRect(fpl__win32_func_AdjustWindowRect);
9433#define FPL__FUNC_WIN32_ClientToScreen(name) BOOL WINAPI name(HWND hWnd, LPPOINT lpPoint)
9434typedef FPL__FUNC_WIN32_ClientToScreen(fpl__win32_func_ClientToScreen);
9435#define FPL__FUNC_WIN32_GetAsyncKeyState(name) SHORT WINAPI name(int vKey)
9436typedef FPL__FUNC_WIN32_GetAsyncKeyState(fpl__win32_func_GetAsyncKeyState);
9437#define FPL__FUNC_WIN32_GetKeyState(name) SHORT WINAPI name(int vKey)
9438typedef FPL__FUNC_WIN32_GetKeyState(fpl__win32_func_GetKeyState);
9439#define FPL__FUNC_WIN32_MapVirtualKeyW(name) UINT WINAPI name(UINT uCode, UINT uMapType)
9440typedef FPL__FUNC_WIN32_MapVirtualKeyW(fpl__win32_func_MapVirtualKeyW);
9441#define FPL__FUNC_WIN32_SetCursor(name) HCURSOR WINAPI name(HCURSOR hCursor)
9442typedef FPL__FUNC_WIN32_SetCursor(fpl__win32_func_SetCursor);
9443#define FPL__FUNC_WIN32_GetCursor(name) HCURSOR WINAPI name(VOID)
9444typedef FPL__FUNC_WIN32_GetCursor(fpl__win32_func_GetCursor);
9445#define FPL__FUNC_WIN32_GetCursorPos(name) BOOL WINAPI name(LPPOINT lpPoint)
9446typedef FPL__FUNC_WIN32_GetCursorPos(fpl__win32_func_GetCursorPos);
9447#define FPL__FUNC_WIN32_WindowFromPoint(name) HWND WINAPI name(POINT Point)
9448typedef FPL__FUNC_WIN32_WindowFromPoint(fpl__win32_func_WindowFromPoint);
9449#define FPL__FUNC_WIN32_PtInRect(name) BOOL WINAPI name(CONST RECT *lprc, POINT pt)
9450typedef FPL__FUNC_WIN32_PtInRect(fpl__win32_func_PtInRect);
9451#define FPL__FUNC_WIN32_LoadCursorA(name) HCURSOR WINAPI name(HINSTANCE hInstance, LPCSTR lpCursorName)
9452typedef FPL__FUNC_WIN32_LoadCursorA(fpl__win32_func_LoadCursorA);
9453#define FPL__FUNC_WIN32_LoadCursorW(name) HCURSOR WINAPI name(HINSTANCE hInstance, LPCWSTR lpCursorName)
9454typedef FPL__FUNC_WIN32_LoadCursorW(fpl__win32_func_LoadCursorW);
9455#define FPL__FUNC_WIN32_LoadIconA(name) HICON WINAPI name(HINSTANCE hInstance, LPCSTR lpIconName)
9456typedef FPL__FUNC_WIN32_LoadIconA(fpl__win32_func_LoadIconA);
9457#define FPL__FUNC_WIN32_LoadIconW(name) HICON WINAPI name(HINSTANCE hInstance, LPCWSTR lpIconName)
9458typedef FPL__FUNC_WIN32_LoadIconW(fpl__win32_func_LoadIconW);
9459#define FPL__FUNC_WIN32_SetWindowTextW(name) BOOL WINAPI name(HWND hWnd, LPCWSTR lpString)
9460typedef FPL__FUNC_WIN32_SetWindowTextW(fpl__win32_func_SetWindowTextW);
9461#define FPL__FUNC_WIN32_GetWindowTextW(name) int WINAPI name(HWND hWnd, LPWSTR lpString, int nMaxCount)
9462typedef FPL__FUNC_WIN32_GetWindowTextW(fpl__win32_func_GetWindowTextW);
9463#define FPL__FUNC_WIN32_SetWindowLongW(name) LONG WINAPI name(HWND hWnd, int nIndex, LONG dwNewLong)
9464typedef FPL__FUNC_WIN32_SetWindowLongW(fpl__win32_func_SetWindowLongW);
9465#define FPL__FUNC_WIN32_GetWindowLongW(name) LONG WINAPI name(HWND hWnd, int nIndex)
9466typedef FPL__FUNC_WIN32_GetWindowLongW(fpl__win32_func_GetWindowLongW);
9467
9468#if defined(FPL_ARCH_X64)
9469#define FPL__FUNC_WIN32_SetWindowLongPtrW(name) LONG_PTR WINAPI name(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
9470typedef FPL__FUNC_WIN32_SetWindowLongPtrW(fpl__win32_func_SetWindowLongPtrW);
9471#define FPL__FUNC_WIN32_GetWindowLongPtrW(name) LONG_PTR WINAPI name(HWND hWnd, int nIndex)
9472typedef FPL__FUNC_WIN32_GetWindowLongPtrW(fpl__win32_func_GetWindowLongPtrW);
9473#endif
9474
9475#define FPL__FUNC_WIN32_ReleaseDC(name) int WINAPI name(HWND hWnd, HDC hDC)
9476typedef FPL__FUNC_WIN32_ReleaseDC(fpl__win32_func_ReleaseDC);
9477#define FPL__FUNC_WIN32_GetDC(name) HDC WINAPI name(HWND hWnd)
9478typedef FPL__FUNC_WIN32_GetDC(fpl__win32_func_GetDC);
9479#define FPL__FUNC_WIN32_ChangeDisplaySettingsW(name) LONG WINAPI name(DEVMODEW* lpDevMode, DWORD dwFlags)
9480typedef FPL__FUNC_WIN32_ChangeDisplaySettingsW(fpl__win32_func_ChangeDisplaySettingsW);
9481#define FPL__FUNC_WIN32_EnumDisplaySettingsW(name) BOOL WINAPI name(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode)
9482typedef FPL__FUNC_WIN32_EnumDisplaySettingsW(fpl__win32_func_EnumDisplaySettingsW);
9483#define FPL__FUNC_WIN32_OpenClipboard(name) BOOL WINAPI name(HWND hWndNewOwner)
9484typedef FPL__FUNC_WIN32_OpenClipboard(fpl__win32_func_OpenClipboard);
9485#define FPL__FUNC_WIN32_CloseClipboard(name) BOOL WINAPI name(VOID)
9486typedef FPL__FUNC_WIN32_CloseClipboard(fpl__win32_func_CloseClipboard);
9487#define FPL__FUNC_WIN32_EmptyClipboard(name) BOOL WINAPI name(VOID)
9488typedef FPL__FUNC_WIN32_EmptyClipboard(fpl__win32_func_EmptyClipboard);
9489#define FPL__FUNC_WIN32_IsClipboardFormatAvailable(name) BOOL WINAPI name(UINT format)
9490typedef FPL__FUNC_WIN32_IsClipboardFormatAvailable(fpl__win32_func_IsClipboardFormatAvailable);
9491#define FPL__FUNC_WIN32_SetClipboardData(name) HANDLE WINAPI name(UINT uFormat, HANDLE hMem)
9492typedef FPL__FUNC_WIN32_SetClipboardData(fpl__win32_func_SetClipboardData);
9493#define FPL__FUNC_WIN32_GetClipboardData(name) HANDLE WINAPI name(UINT uFormat)
9494typedef FPL__FUNC_WIN32_GetClipboardData(fpl__win32_func_GetClipboardData);
9495#define FPL__FUNC_WIN32_GetDesktopWindow(name) HWND WINAPI name(VOID)
9496typedef FPL__FUNC_WIN32_GetDesktopWindow(fpl__win32_func_GetDesktopWindow);
9497#define FPL__FUNC_WIN32_GetForegroundWindow(name) HWND WINAPI name(VOID)
9498typedef FPL__FUNC_WIN32_GetForegroundWindow(fpl__win32_func_GetForegroundWindow);
9499#define FPL__FUNC_WIN32_IsZoomed(name) BOOL WINAPI name(HWND hWnd)
9500typedef FPL__FUNC_WIN32_IsZoomed(fpl__win32_func_IsZoomed);
9501#define FPL__FUNC_WIN32_IsIconic(name) BOOL WINAPI name(HWND hWnd)
9502typedef FPL__FUNC_WIN32_IsIconic(fpl__win32_func_IsIconic);
9503#define FPL__FUNC_WIN32_SendMessageW(name) LRESULT WINAPI name(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
9504typedef FPL__FUNC_WIN32_SendMessageW(fpl__win32_func_SendMessageW);
9505#define FPL__FUNC_WIN32_GetMonitorInfoW(name) BOOL WINAPI name(HMONITOR hMonitor, LPMONITORINFO lpmi)
9506typedef FPL__FUNC_WIN32_GetMonitorInfoW(fpl__win32_func_GetMonitorInfoW);
9507#define FPL__FUNC_WIN32_EnumDisplayMonitors(name) BOOL WINAPI name(HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum,LPARAM dwData)
9508typedef FPL__FUNC_WIN32_EnumDisplayMonitors(fpl__win32_func_EnumDisplayMonitors);
9509#define FPL__FUNC_WIN32_MonitorFromRect(name) HMONITOR WINAPI name(LPCRECT lprc, DWORD dwFlags)
9510typedef FPL__FUNC_WIN32_MonitorFromRect(fpl__win32_func_MonitorFromRect);
9511#define FPL__FUNC_WIN32_MonitorFromPoint(name) HMONITOR WINAPI name(POINT pt, DWORD dwFlags)
9512typedef FPL__FUNC_WIN32_MonitorFromPoint(fpl__win32_func_MonitorFromPoint);
9513#define FPL__FUNC_WIN32_MonitorFromWindow(name) HMONITOR WINAPI name(HWND hwnd, DWORD dwFlags)
9514typedef FPL__FUNC_WIN32_MonitorFromWindow(fpl__win32_func_MonitorFromWindow);
9515#define FPL__FUNC_WIN32_RegisterRawInputDevices(name) BOOL WINAPI name(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize)
9516typedef FPL__FUNC_WIN32_RegisterRawInputDevices(fpl__win32_func_RegisterRawInputDevices);
9517#define FPL__FUNC_WIN32_ClipCursor(name) BOOL WINAPI name(CONST RECT *lpRect)
9518typedef FPL__FUNC_WIN32_ClipCursor(fpl__win32_func_ClipCursor);
9519#define FPL__FUNC_WIN32_PostQuitMessage(name) VOID WINAPI name(int nExitCode)
9520typedef FPL__FUNC_WIN32_PostQuitMessage(fpl__win32_func_PostQuitMessage);
9521#define FPL__FUNC_WIN32_CreateIconIndirect(name) HICON WINAPI name(PICONINFO piconinfo)
9522typedef FPL__FUNC_WIN32_CreateIconIndirect(fpl__win32_func_CreateIconIndirect);
9523#define FPL__FUNC_WIN32_GetKeyboardLayout(name) HKL WINAPI name(DWORD idThread)
9524typedef FPL__FUNC_WIN32_GetKeyboardLayout(fpl__win32_func_GetKeyboardLayout);
9525#define FPL__FUNC_WIN32_SetCapture(name) HWND WINAPI name(HWND hWnd)
9526typedef FPL__FUNC_WIN32_SetCapture(fpl__win32_func_SetCapture);
9527#define FPL__FUNC_WIN32_ReleaseCapture(name) BOOL WINAPI name(VOID)
9528typedef FPL__FUNC_WIN32_ReleaseCapture(fpl__win32_func_ReleaseCapture);
9529#define FPL__FUNC_WIN32_ScreenToClient(name) BOOL WINAPI name(HWND hWnd, LPPOINT lpPoint)
9530typedef FPL__FUNC_WIN32_ScreenToClient(fpl__win32_func_ScreenToClient);
9531#define FPL__FUNC_WIN32_BeginPaint(name) HDC WINAPI name(_In_ HWND hWnd, _Out_ LPPAINTSTRUCT lpPaint)
9532typedef FPL__FUNC_WIN32_BeginPaint(fpl__win32_func_BeginPaint);
9533#define FPL__FUNC_WIN32_EndPaint(name) BOOL WINAPI name(_In_ HWND hWnd, _In_ CONST PAINTSTRUCT *lpPaint)
9534typedef FPL__FUNC_WIN32_EndPaint(fpl__win32_func_EndPaint);
9535#define FPL__FUNC_WIN32_SetForegroundWindow(name) BOOL WINAPI name(_In_ HWND hWnd)
9536typedef FPL__FUNC_WIN32_SetForegroundWindow(fpl__win32_func_SetForegroundWindow);
9537#define FPL__FUNC_WIN32_SetFocus(name) HWND WINAPI name(_In_opt_ HWND hWnd)
9538typedef FPL__FUNC_WIN32_SetFocus(fpl__win32_func_SetFocus);
9539#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)
9540typedef FPL__FUNC_WIN32_SetTimer(fpl__win32_func_SetTimer);
9541#define FPL__FUNC_WIN32_GetSysColorBrush(name) HBRUSH WINAPI name(_In_ int nIndex)
9542typedef FPL__FUNC_WIN32_GetSysColorBrush(fpl__win32_func_GetSysColorBrush);
9543#define FPL__FUNC_WIN32_GetSysColorBrush(name) HBRUSH WINAPI name(_In_ int nIndex)
9544typedef FPL__FUNC_WIN32_GetSysColorBrush(fpl__win32_func_GetSysColorBrush);
9545
9546// OLE32
9547#define FPL__FUNC_WIN32_CoInitializeEx(name) HRESULT WINAPI name(LPVOID pvReserved, DWORD dwCoInit)
9548typedef FPL__FUNC_WIN32_CoInitializeEx(fpl__win32_func_CoInitializeEx);
9549#define FPL__FUNC_WIN32_CoUninitialize(name) void WINAPI name(void)
9550typedef FPL__FUNC_WIN32_CoUninitialize(fpl__win32_func_CoUninitialize);
9551#define FPL__FUNC_WIN32_CoCreateInstance(name) HRESULT WINAPI name(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv)
9552typedef FPL__FUNC_WIN32_CoCreateInstance(fpl__win32_func_CoCreateInstance);
9553#define FPL__FUNC_WIN32_CoTaskMemFree(name) void WINAPI name(LPVOID pv)
9554typedef FPL__FUNC_WIN32_CoTaskMemFree(fpl__win32_func_CoTaskMemFree);
9555#define FPL__FUNC_WIN32_PropVariantClear(name) HRESULT WINAPI name(PROPVARIANT *pvar)
9556typedef FPL__FUNC_WIN32_PropVariantClear(fpl__win32_func_PropVariantClear);
9557
9558typedef struct fpl__Win32GdiApi {
9559 HMODULE gdiLibrary;
9560 fpl__win32_func_ChoosePixelFormat *ChoosePixelFormat;
9561 fpl__win32_func_SetPixelFormat *SetPixelFormat;
9562 fpl__win32_func_DescribePixelFormat *DescribePixelFormat;
9563 fpl__win32_func_GetDeviceCaps *GetDeviceCaps;
9564 fpl__win32_func_StretchDIBits *StretchDIBits;
9565 fpl__win32_func_DeleteObject *DeleteObject;
9566 fpl__win32_func_SwapBuffers *SwapBuffers;
9567 fpl__win32_func_CreateDIBSection *CreateDIBSection;
9568 fpl__win32_func_CreateBitmap *CreateBitmap;
9569 fpl__win32_func_CreateSolidBrush *CreateSolidBrush;
9570} fpl__Win32GdiApi;
9571
9572typedef struct fpl__Win32ShellApi {
9573 HMODULE shellLibrary;
9574 fpl__win32_func_SHGetFolderPathW *SHGetFolderPathW;
9575 fpl__win32_func_DragQueryFileW *DragQueryFileW;
9576 fpl__win32_func_DragAcceptFiles *DragAcceptFiles;
9577} fpl__Win32ShellApi;
9578
9579typedef struct fpl__Win32UserApi {
9580 HMODULE userLibrary;
9581 fpl__win32_func_RegisterClassExW *RegisterClassExW;
9582 fpl__win32_func_UnregisterClassW *UnregisterClassW;
9583 fpl__win32_func_ShowWindow *ShowWindow;
9584 fpl__win32_func_DestroyWindow *DestroyWindow;
9585 fpl__win32_func_UpdateWindow *UpdateWindow;
9586 fpl__win32_func_TranslateMessage *TranslateMessage;
9587 fpl__win32_func_DispatchMessageW *DispatchMessageW;
9588 fpl__win32_func_PeekMessageW *PeekMessageW;
9589 fpl__win32_func_DefWindowProcW *DefWindowProcW;
9590 fpl__win32_func_CreateWindowExW *CreateWindowExW;
9591 fpl__win32_func_SetWindowPos *SetWindowPos;
9592 fpl__win32_func_GetWindowPlacement *GetWindowPlacement;
9593 fpl__win32_func_SetWindowPlacement *SetWindowPlacement;
9594 fpl__win32_func_GetClientRect *GetClientRect;
9595 fpl__win32_func_GetWindowRect *GetWindowRect;
9596 fpl__win32_func_AdjustWindowRect *AdjustWindowRect;
9597 fpl__win32_func_GetAsyncKeyState *GetAsyncKeyState;
9598 fpl__win32_func_MapVirtualKeyW *MapVirtualKeyW;
9599 fpl__win32_func_SetCursor *SetCursor;
9600 fpl__win32_func_GetCursor *GetCursor;
9601 fpl__win32_func_LoadCursorA *LoadCursorA;
9602 fpl__win32_func_LoadCursorW *LoadCursorW;
9603 fpl__win32_func_LoadIconA *LoadIconA;
9604 fpl__win32_func_LoadIconW *LoadIconW;
9605 fpl__win32_func_SetWindowTextW *SetWindowTextW;
9606 fpl__win32_func_GetWindowTextW *GetWindowTextW;
9607 fpl__win32_func_SetWindowLongW *SetWindowLongW;
9608 fpl__win32_func_GetWindowLongW *GetWindowLongW;
9609#if defined(FPL_ARCH_X64)
9610 fpl__win32_func_SetWindowLongPtrW *SetWindowLongPtrW;
9611 fpl__win32_func_GetWindowLongPtrW *GetWindowLongPtrW;
9612#endif
9613 fpl__win32_func_ReleaseDC *ReleaseDC;
9614 fpl__win32_func_GetDC *GetDC;
9615 fpl__win32_func_ChangeDisplaySettingsW *ChangeDisplaySettingsW;
9616 fpl__win32_func_EnumDisplaySettingsW *EnumDisplaySettingsW;
9617 fpl__win32_func_OpenClipboard *OpenClipboard;
9618 fpl__win32_func_CloseClipboard *CloseClipboard;
9619 fpl__win32_func_EmptyClipboard *EmptyClipboard;
9620 fpl__win32_func_IsClipboardFormatAvailable *IsClipboardFormatAvailable;
9621 fpl__win32_func_SetClipboardData *SetClipboardData;
9622 fpl__win32_func_GetClipboardData *GetClipboardData;
9623 fpl__win32_func_GetDesktopWindow *GetDesktopWindow;
9624 fpl__win32_func_GetForegroundWindow *GetForegroundWindow;
9625 fpl__win32_func_IsZoomed *IsZoomed;
9626 fpl__win32_func_IsIconic *IsIconic;
9627 fpl__win32_func_SendMessageW *SendMessageW;
9628 fpl__win32_func_GetMonitorInfoW *GetMonitorInfoW;
9629 fpl__win32_func_EnumDisplayMonitors *EnumDisplayMonitors;
9630 fpl__win32_func_MonitorFromRect *MonitorFromRect;
9631 fpl__win32_func_MonitorFromPoint *MonitorFromPoint;
9632 fpl__win32_func_MonitorFromWindow *MonitorFromWindow;
9633 fpl__win32_func_GetCursorPos *GetCursorPos;
9634 fpl__win32_func_WindowFromPoint *WindowFromPoint;
9635 fpl__win32_func_ClientToScreen *ClientToScreen;
9636 fpl__win32_func_PtInRect *PtInRect;
9637 fpl__win32_func_RegisterRawInputDevices *RegisterRawInputDevices;
9638 fpl__win32_func_ClipCursor *ClipCursor;
9639 fpl__win32_func_PostQuitMessage *PostQuitMessage;
9640 fpl__win32_func_CreateIconIndirect *CreateIconIndirect;
9641 fpl__win32_func_GetKeyboardLayout *GetKeyboardLayout;
9642 fpl__win32_func_GetKeyState *GetKeyState;
9643 fpl__win32_func_SetCapture *SetCapture;
9644 fpl__win32_func_ReleaseCapture *ReleaseCapture;
9645 fpl__win32_func_ScreenToClient *ScreenToClient;
9646 fpl__win32_func_BeginPaint *BeginPaint;
9647 fpl__win32_func_EndPaint *EndPaint;
9648 fpl__win32_func_SetForegroundWindow *SetForegroundWindow;
9649 fpl__win32_func_SetFocus *SetFocus;
9650 fpl__win32_func_SetTimer *SetTimer;
9651 fpl__win32_func_GetSysColorBrush *GetSysColorBrush;
9652} fpl__Win32UserApi;
9653
9654typedef struct fpl__Win32OleApi {
9655 HMODULE oleLibrary;
9656 fpl__win32_func_CoInitializeEx *CoInitializeEx;
9657 fpl__win32_func_CoUninitialize *CoUninitialize;
9658 fpl__win32_func_CoCreateInstance *CoCreateInstance;
9659 fpl__win32_func_CoTaskMemFree *CoTaskMemFree;
9660 fpl__win32_func_PropVariantClear *PropVariantClear;
9661} fpl__Win32OleApi;
9662
9663typedef struct fpl__Win32Api {
9664 fpl__Win32GdiApi gdi;
9665 fpl__Win32ShellApi shell;
9666 fpl__Win32UserApi user;
9667 fpl__Win32OleApi ole;
9668 fpl_b32 isValid;
9669} fpl__Win32Api;
9670
9671fpl_internal void fpl__Win32UnloadApi(fpl__Win32Api *wapi) {
9672 fplAssert(wapi != fpl_null);
9673 if (wapi->ole.oleLibrary != fpl_null) {
9674 FreeLibrary(wapi->ole.oleLibrary);
9675 }
9676 fplClearStruct(&wapi->ole);
9677 if (wapi->gdi.gdiLibrary != fpl_null) {
9678 FreeLibrary(wapi->gdi.gdiLibrary);
9679 }
9680 fplClearStruct(&wapi->gdi);
9681 if (wapi->user.userLibrary != fpl_null) {
9682 FreeLibrary(wapi->user.userLibrary);
9683 }
9684 fplClearStruct(&wapi->user);
9685 if (wapi->shell.shellLibrary != fpl_null) {
9686 FreeLibrary(wapi->shell.shellLibrary);
9687 }
9688 fplClearStruct(&wapi->shell);
9689 wapi->isValid = false;
9690}
9691
9692fpl_internal bool fpl__Win32LoadApi(fpl__Win32Api *wapi) {
9693 fplAssert(wapi != fpl_null);
9694 bool result = false;
9695 fplClearStruct(wapi);
9696 do {
9697 // Shell32
9698 const char *shellLibraryName = "shell32.dll";
9699 HMODULE shellLibrary = fpl_null;
9700 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, shellLibrary, shellLibraryName);
9701 wapi->shell.shellLibrary = shellLibrary;
9702 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, shellLibrary, shellLibraryName, &wapi->shell, fpl__win32_func_SHGetFolderPathW, SHGetFolderPathW);
9703 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, shellLibrary, shellLibraryName, &wapi->shell, fpl__win32_func_DragQueryFileW, DragQueryFileW);
9704 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, shellLibrary, shellLibraryName, &wapi->shell, fpl__win32_func_DragAcceptFiles, DragAcceptFiles);
9705
9706 // User32
9707 const char *userLibraryName = "user32.dll";
9708 HMODULE userLibrary = fpl_null;
9709 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, userLibrary, userLibraryName);
9710 wapi->user.userLibrary = userLibrary;
9711 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_RegisterClassExW, RegisterClassExW);
9712 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_UnregisterClassW, UnregisterClassW);
9713 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ShowWindow, ShowWindow);
9714 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_DestroyWindow, DestroyWindow);
9715 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_UpdateWindow, UpdateWindow);
9716 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_TranslateMessage, TranslateMessage);
9717 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_DispatchMessageW, DispatchMessageW);
9718 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_PeekMessageW, PeekMessageW);
9719 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_DefWindowProcW, DefWindowProcW);
9720 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_CreateWindowExW, CreateWindowExW);
9721 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowPos, SetWindowPos);
9722 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowPlacement, GetWindowPlacement);
9723 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowPlacement, SetWindowPlacement);
9724 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetClientRect, GetClientRect);
9725 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowRect, GetWindowRect);
9726 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_AdjustWindowRect, AdjustWindowRect);
9727 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetAsyncKeyState, GetAsyncKeyState);
9728 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MapVirtualKeyW, MapVirtualKeyW);
9729 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetCursor, SetCursor);
9730 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetCursor, GetCursor);
9731 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadCursorA, LoadCursorA);
9732 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadCursorW, LoadCursorW);
9733 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetCursorPos, GetCursorPos);
9734 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_WindowFromPoint, WindowFromPoint);
9735 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadIconA, LoadIconA);
9736 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadIconW, LoadIconW);
9737 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowTextW, SetWindowTextW);
9738 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowLongW, SetWindowLongW);
9739 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowLongW, GetWindowLongW);
9740
9741# if defined(FPL_ARCH_X64)
9742 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowLongPtrW, SetWindowLongPtrW);
9743 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowLongPtrW, GetWindowLongPtrW);
9744# endif
9745
9746 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ReleaseDC, ReleaseDC);
9747 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetDC, GetDC);
9748 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ChangeDisplaySettingsW, ChangeDisplaySettingsW);
9749 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EnumDisplaySettingsW, EnumDisplaySettingsW);
9750 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_IsClipboardFormatAvailable, IsClipboardFormatAvailable);
9751 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_OpenClipboard, OpenClipboard);
9752 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_CloseClipboard, CloseClipboard);
9753 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EmptyClipboard, EmptyClipboard);
9754 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetClipboardData, SetClipboardData);
9755 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetClipboardData, GetClipboardData);
9756 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetDesktopWindow, GetDesktopWindow);
9757 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetForegroundWindow, GetForegroundWindow);
9758 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_IsZoomed, IsZoomed);
9759 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_IsIconic, IsIconic);
9760 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SendMessageW, SendMessageW);
9761 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetMonitorInfoW, GetMonitorInfoW);
9762 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EnumDisplayMonitors, EnumDisplayMonitors);
9763 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MonitorFromRect, MonitorFromRect);
9764 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MonitorFromPoint, MonitorFromPoint);
9765 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MonitorFromWindow, MonitorFromWindow);
9766 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ClientToScreen, ClientToScreen);
9767 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_PtInRect, PtInRect);
9768 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_RegisterRawInputDevices, RegisterRawInputDevices);
9769 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ClipCursor, ClipCursor);
9770 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_PostQuitMessage, PostQuitMessage);
9771 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_CreateIconIndirect, CreateIconIndirect);
9772 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetKeyboardLayout, GetKeyboardLayout);
9773 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetKeyState, GetKeyState);
9774 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetCapture, SetCapture);
9775 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ReleaseCapture, ReleaseCapture);
9776 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ScreenToClient, ScreenToClient);
9777 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_BeginPaint, BeginPaint);
9778 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EndPaint, EndPaint);
9779 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetForegroundWindow, SetForegroundWindow);
9780 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetFocus, SetFocus);
9781 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetTimer, SetTimer);
9782 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetSysColorBrush, GetSysColorBrush);
9783
9784 // GDI32
9785 const char *gdiLibraryName = "gdi32.dll";
9786 HMODULE gdiLibrary = fpl_null;
9787 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName);
9788 wapi->gdi.gdiLibrary = gdiLibrary;
9789 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_ChoosePixelFormat, ChoosePixelFormat);
9790 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_SetPixelFormat, SetPixelFormat);
9791 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_DescribePixelFormat, DescribePixelFormat);
9792 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_StretchDIBits, StretchDIBits);
9793 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_DeleteObject, DeleteObject);
9794 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_SwapBuffers, SwapBuffers);
9795 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_GetDeviceCaps, GetDeviceCaps);
9796 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_CreateDIBSection, CreateDIBSection);
9797 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_CreateBitmap, CreateBitmap);
9798 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_CreateSolidBrush, CreateSolidBrush);
9799
9800 // OLE32
9801 const char *oleLibraryName = "ole32.dll";
9802 HMODULE oleLibrary = fpl_null;
9803 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, oleLibrary, oleLibraryName);
9804 wapi->ole.oleLibrary = oleLibrary;
9805 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoInitializeEx, CoInitializeEx);
9806 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoUninitialize, CoUninitialize);
9807 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoCreateInstance, CoCreateInstance);
9808 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoTaskMemFree, CoTaskMemFree);
9809 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_PropVariantClear, PropVariantClear);
9810
9811 result = true;
9812 } while (0);
9813 if (!result) {
9814 fpl__Win32UnloadApi(wapi);
9815 }
9816 wapi->isValid = result;
9817 return(result);
9818}
9819
9820// Win32 unicode dependend stuff
9821#define FPL__WIN32_CLASSNAME L"FPLWindowClassW"
9822#define FPL__WIN32_UNNAMED_WINDOW L"Unnamed FPL Unicode Window"
9823#define FPL__WIN32_UNNAMED_CONSOLE L"Unnamed FPL Unicode Console"
9824#if defined(FPL_ARCH_X64)
9825# define fpl__win32_SetWindowLongPtr fpl__global__AppState->win32.winApi.user.SetWindowLongPtrW
9826#else
9827# define fpl__win32_SetWindowLongPtr fpl__global__AppState->win32.winApi.user.SetWindowLongW
9828#endif
9829#define fpl__win32_SetWindowLong fpl__global__AppState->win32.winApi.user.SetWindowLongW
9830#define fpl__win32_GetWindowLong fpl__global__AppState->win32.winApi.user.GetWindowLongW
9831#if defined(UNICODE)
9832# define fpl__win32_LoadIcon fpl__global__AppState->win32.winApi.user.LoadIconW
9833# define fpl__win32_LoadCursor fpl__global__AppState->win32.winApi.user.LoadCursorW
9834#else
9835# define fpl__win32_LoadIcon fpl__global__AppState->win32.winApi.user.LoadIconA
9836# define fpl__win32_LoadCursor fpl__global__AppState->win32.winApi.user.LoadCursorA
9837#endif
9838
9839typedef char fpl__GameControllerName[FPL_MAX_NAME_LENGTH];
9840
9841typedef struct fpl__Win32XInputState {
9842 fpl__GameControllerName deviceNames[XUSER_MAX_COUNT];
9843 fpl_b32 isConnected[XUSER_MAX_COUNT];
9844 fpl__Win32XInputApi xinputApi;
9845 LARGE_INTEGER lastDeviceSearchTime;
9846} fpl__Win32XInputState;
9847
9848typedef struct fpl__Win32InitState {
9849 HINSTANCE appInstance;
9850 LARGE_INTEGER qpf;
9851} fpl__Win32InitState;
9852
9853typedef struct fpl__Win32AppState {
9854 fpl__Win32XInputState xinput;
9855 fpl__Win32Api winApi;
9856} fpl__Win32AppState;
9857
9858#if defined(FPL__ENABLE_WINDOW)
9859typedef struct fpl__Win32LastWindowInfo {
9860 WINDOWPLACEMENT placement;
9861 DWORD style;
9862 DWORD exStyle;
9863 fpl_b32 isMaximized;
9864 fpl_b32 isMinimized;
9865 fpl_b32 wasResolutionChanged;
9866} fpl__Win32LastWindowInfo;
9867
9868typedef struct fpl__Win32WindowState {
9869 wchar_t windowClass[256];
9870 fpl__Win32LastWindowInfo lastFullscreenInfo;
9871 void *mainFiber;
9872 void *messageFiber;
9873 HWND windowHandle;
9874 HDC deviceContext;
9875 HBRUSH backgroundBrush;
9876 HCURSOR defaultCursor;
9877 int pixelFormat;
9878 fpl_b32 isCursorActive;
9879 fpl_b32 isFrameInteraction;
9880} fpl__Win32WindowState;
9881#endif // FPL__ENABLE_WINDOW
9882
9883#endif // FPL_PLATFORM_WINDOWS
9884
9885// ############################################################################
9886//
9887// > TYPES_POSIX
9888//
9889// ############################################################################
9890#if defined(FPL_SUBPLATFORM_POSIX)
9891# include <sys/types.h> // data types
9892# include <sys/mman.h> // mmap, munmap
9893# include <sys/stat.h> // mkdir
9894# include <sys/errno.h> // errno
9895# include <sys/time.h> // gettimeofday
9896# include <sys/utsname.h> // uname
9897# include <signal.h> // pthread_kill
9898# include <time.h> // clock_gettime, nanosleep
9899# include <dlfcn.h> // dlopen, dlclose
9900# include <fcntl.h> // open
9901# include <unistd.h> // read, write, close, access, rmdir, getpid, sysconf, geteuid
9902# include <ctype.h> // isspace
9903# include <pwd.h> // getpwuid
9904
9905// @TODO(final): Detect the case of (Older POSIX versions where st_atim != st_atime)
9906#if !defined(FPL_PLATFORM_ANDROID)
9907# define st_atime st_atim.tv_sec
9908# define st_mtime st_mtim.tv_sec
9909# define st_ctime st_ctim.tv_sec
9910#endif
9911
9912#if defined(FPL_PLATFORM_LINUX)
9913# define fpl__lseek64 lseek64
9914# define fpl__off64_t off64_t
9915#else
9916# define fpl__lseek64 lseek
9917# define fpl__off64_t off_t
9918#endif
9919
9920// Little macros for loading a library and getting proc address for POSIX
9921#define FPL__POSIX_LOAD_LIBRARY_BREAK(mod, target, libName) \
9922 (target) = dlopen(libName, FPL__POSIX_DL_LOADTYPE); \
9923 if((target) == fpl_null) { \
9924 FPL__WARNING(mod, "Failed loading library '%s'", (libName)); \
9925 break; \
9926 }
9927
9928#define FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(mod, libHandle, libName, target, type, name) \
9929 (target)->name = (type *)dlsym(libHandle, #name)
9930
9931#define FPL__POSIX_GET_FUNCTION_ADDRESS_BREAK(mod, libHandle, libName, target, type, name) \
9932 (target)->name = (type *)dlsym(libHandle, #name); \
9933 if ((target)->name == fpl_null) { \
9934 FPL__WARNING(mod, "Failed getting procedure address '%s' from library '%s'", #name, libName); \
9935 break; \
9936 }
9937#if !defined(FPL_NO_RUNTIME_LINKING)
9938# define FPL__POSIX_LOAD_LIBRARY FPL__POSIX_LOAD_LIBRARY_BREAK
9939# define FPL__POSIX_GET_FUNCTION_ADDRESS FPL__POSIX_GET_FUNCTION_ADDRESS_BREAK
9940#else
9941# define FPL__POSIX_LOAD_LIBRARY(mod, target, libName)
9942# define FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(mod, libHandle, libName, target, type, name) \
9943 (target)->name = name
9944# define FPL__POSIX_GET_FUNCTION_ADDRESS(mod, libHandle, libName, target, type, name) \
9945 (target)->name = name
9946#endif
9947
9948#define FPL__FUNC_PTHREAD_pthread_self(name) pthread_t name(void)
9949typedef FPL__FUNC_PTHREAD_pthread_self(fpl__pthread_func_pthread_self);
9950#define FPL__FUNC_PTHREAD_pthread_setschedparam(name) int name(pthread_t thread, int policy, const struct sched_param *param)
9951typedef FPL__FUNC_PTHREAD_pthread_setschedparam(fpl__pthread_func_pthread_setschedparam);
9952#define FPL__FUNC_PTHREAD_pthread_getschedparam(name) int name(pthread_t thread, int *policy, struct sched_param *param)
9953typedef FPL__FUNC_PTHREAD_pthread_getschedparam(fpl__pthread_func_pthread_getschedparam);
9954#define FPL__FUNC_PTHREAD_pthread_setschedprio(name) int name(pthread_t thread, int prio)
9955typedef FPL__FUNC_PTHREAD_pthread_setschedprio(fpl__pthread_func_pthread_setschedprio);
9956
9957#define FPL__FUNC_PTHREAD_pthread_attr_init(name) int name(pthread_attr_t *attr)
9958typedef FPL__FUNC_PTHREAD_pthread_attr_init(fpl__pthread_func_pthread_attr_init);
9959#define FPL__FUNC_PTHREAD_pthread_attr_getschedparam(name) int name(const pthread_attr_t *__restrict__ attr, struct sched_param *__restrict__ param)
9960typedef FPL__FUNC_PTHREAD_pthread_attr_getschedparam(fpl__pthread_func_pthread_attr_getschedparam);
9961#define FPL__FUNC_PTHREAD_pthread_attr_setschedparam(name) int name(pthread_attr_t *__restrict__ attr, const struct sched_param *__restrict__ param)
9962typedef FPL__FUNC_PTHREAD_pthread_attr_setschedparam(fpl__pthread_func_pthread_attr_setschedparam);
9963#define FPL__FUNC_PTHREAD_pthread_attr_setstacksize(name) int name(pthread_attr_t *attr, size_t stacksize)
9964typedef FPL__FUNC_PTHREAD_pthread_attr_setstacksize(fpl__pthread_func_pthread_attr_setstacksize);
9965#define FPL__FUNC_PTHREAD_pthread_attr_setdetachstate(name) int name(pthread_attr_t *attr, int detachstate);
9966typedef FPL__FUNC_PTHREAD_pthread_attr_setdetachstate(fpl__pthread_func_pthread_attr_setdetachstate);
9967#define FPL__FUNC_PTHREAD_pthread_attr_setschedpolicy(name) int name(pthread_attr_t *__attr, int __policy)
9968typedef FPL__FUNC_PTHREAD_pthread_attr_setschedpolicy(fpl__pthread_func_pthread_attr_setschedpolicy);
9969
9970#define FPL__FUNC_PTHREAD_pthread_create(name) int name(pthread_t *, const pthread_attr_t *, void *(*__start_routine) (void *), void *)
9971typedef FPL__FUNC_PTHREAD_pthread_create(fpl__pthread_func_pthread_create);
9972#define FPL__FUNC_PTHREAD_pthread_kill(name) int name(pthread_t thread, int sig)
9973typedef FPL__FUNC_PTHREAD_pthread_kill(fpl__pthread_func_pthread_kill);
9974#define FPL__FUNC_PTHREAD_pthread_join(name) int name(pthread_t __th, void **retval)
9975typedef FPL__FUNC_PTHREAD_pthread_join(fpl__pthread_func_pthread_join);
9976#define FPL__FUNC_PTHREAD_pthread_exit(name) void name(void *__retval)
9977typedef FPL__FUNC_PTHREAD_pthread_exit(fpl__pthread_func_pthread_exit);
9978#define FPL__FUNC_PTHREAD_pthread_yield(name) int name(void)
9979typedef FPL__FUNC_PTHREAD_pthread_yield(fpl__pthread_func_pthread_yield);
9980#define FPL__FUNC_PTHREAD_pthread_timedjoin_np(name) int name(pthread_t thread, void **retval, const struct timespec *abstime)
9981typedef FPL__FUNC_PTHREAD_pthread_timedjoin_np(fpl__pthread_func_pthread_timedjoin_np);
9982
9983#define FPL__FUNC_PTHREAD_pthread_mutex_init(name) int name(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
9984typedef FPL__FUNC_PTHREAD_pthread_mutex_init(fpl__pthread_func_pthread_mutex_init);
9985#define FPL__FUNC_PTHREAD_pthread_mutex_destroy(name) int name(pthread_mutex_t *mutex)
9986typedef FPL__FUNC_PTHREAD_pthread_mutex_destroy(fpl__pthread_func_pthread_mutex_destroy);
9987#define FPL__FUNC_PTHREAD_pthread_mutex_lock(name) int name(pthread_mutex_t *mutex)
9988typedef FPL__FUNC_PTHREAD_pthread_mutex_lock(fpl__pthread_func_pthread_mutex_lock);
9989#define FPL__FUNC_PTHREAD_pthread_mutex_trylock(name) int name(pthread_mutex_t *mutex)
9990typedef FPL__FUNC_PTHREAD_pthread_mutex_trylock(fpl__pthread_func_pthread_mutex_trylock);
9991#define FPL__FUNC_PTHREAD_pthread_mutex_unlock(name) int name(pthread_mutex_t *mutex)
9992typedef FPL__FUNC_PTHREAD_pthread_mutex_unlock(fpl__pthread_func_pthread_mutex_unlock);
9993
9994#define FPL__FUNC_PTHREAD_pthread_cond_init(name) int name(pthread_cond_t *cond, const pthread_condattr_t *attr)
9995typedef FPL__FUNC_PTHREAD_pthread_cond_init(fpl__pthread_func_pthread_cond_init);
9996#define FPL__FUNC_PTHREAD_pthread_cond_destroy(name) int name(pthread_cond_t *cond)
9997typedef FPL__FUNC_PTHREAD_pthread_cond_destroy(fpl__pthread_func_pthread_cond_destroy);
9998#define FPL__FUNC_PTHREAD_pthread_cond_timedwait(name) int name(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
9999typedef FPL__FUNC_PTHREAD_pthread_cond_timedwait(fpl__pthread_func_pthread_cond_timedwait);
10000#define FPL__FUNC_PTHREAD_pthread_cond_wait(name) int name(pthread_cond_t *cond, pthread_mutex_t *mutex)
10001typedef FPL__FUNC_PTHREAD_pthread_cond_wait(fpl__pthread_func_pthread_cond_wait);
10002#define FPL__FUNC_PTHREAD_pthread_cond_broadcast(name) int name(pthread_cond_t *cond)
10003typedef FPL__FUNC_PTHREAD_pthread_cond_broadcast(fpl__pthread_func_pthread_cond_broadcast);
10004#define FPL__FUNC_PTHREAD_pthread_cond_signal(name) int name(pthread_cond_t *cond)
10005typedef FPL__FUNC_PTHREAD_pthread_cond_signal(fpl__pthread_func_pthread_cond_signal);
10006
10007#define FPL__FUNC_PTHREAD_sem_init(name) int name(sem_t *__sem, int __pshared, unsigned int __value)
10008typedef FPL__FUNC_PTHREAD_sem_init(fpl__pthread_func_sem_init);
10009#define FPL__FUNC_PTHREAD_sem_destroy(name) int name(sem_t *__sem)
10010typedef FPL__FUNC_PTHREAD_sem_destroy(fpl__pthread_func_sem_destroy);
10011#define FPL__FUNC_PTHREAD_sem_wait(name) int name(sem_t *__sem)
10012typedef FPL__FUNC_PTHREAD_sem_wait(fpl__pthread_func_sem_wait);
10013#define FPL__FUNC_PTHREAD_sem_timedwait(name) int name(sem_t *__restrict __sem, const struct timespec *__restrict __abstime)
10014typedef FPL__FUNC_PTHREAD_sem_timedwait(fpl__pthread_func_sem_timedwait);
10015#define FPL__FUNC_PTHREAD_sem_trywait(name) int name(sem_t *__sem)
10016typedef FPL__FUNC_PTHREAD_sem_trywait(fpl__pthread_func_sem_trywait);
10017#define FPL__FUNC_PTHREAD_sem_post(name) int name(sem_t *__sem)
10018typedef FPL__FUNC_PTHREAD_sem_post(fpl__pthread_func_sem_post);
10019#define FPL__FUNC_PTHREAD_sem_getvalue(name) int name(sem_t *__restrict __sem, int *__restrict __sval)
10020typedef FPL__FUNC_PTHREAD_sem_getvalue(fpl__pthread_func_sem_getvalue);
10021
10022typedef struct fpl__PThreadApi {
10023 void *libHandle;
10024
10025 // pthread_t
10026 fpl__pthread_func_pthread_self *pthread_self;
10027 fpl__pthread_func_pthread_setschedparam *pthread_setschedparam;
10028 fpl__pthread_func_pthread_getschedparam *pthread_getschedparam;
10029 fpl__pthread_func_pthread_setschedprio *pthread_setschedprio;
10030
10031 fpl__pthread_func_pthread_create *pthread_create;
10032 fpl__pthread_func_pthread_kill *pthread_kill;
10033 fpl__pthread_func_pthread_join *pthread_join;
10034 fpl__pthread_func_pthread_exit *pthread_exit;
10035 fpl__pthread_func_pthread_yield *pthread_yield;
10036 fpl__pthread_func_pthread_timedjoin_np *pthread_timedjoin_np;
10037
10038 // pthread_attr_t
10039 fpl__pthread_func_pthread_attr_init *pthread_attr_init;
10040 fpl__pthread_func_pthread_attr_getschedparam *pthread_attr_getschedparam;
10041 fpl__pthread_func_pthread_attr_setschedparam *pthread_attr_setschedparam;
10042 fpl__pthread_func_pthread_attr_setstacksize *pthread_attr_setstacksize;
10043 fpl__pthread_func_pthread_attr_setdetachstate *pthread_attr_setdetachstate;
10044 fpl__pthread_func_pthread_attr_setschedpolicy *pthread_attr_setschedpolicy;
10045
10046 // pthread_mutex_t
10047 fpl__pthread_func_pthread_mutex_init *pthread_mutex_init;
10048 fpl__pthread_func_pthread_mutex_destroy *pthread_mutex_destroy;
10049 fpl__pthread_func_pthread_mutex_lock *pthread_mutex_lock;
10050 fpl__pthread_func_pthread_mutex_trylock *pthread_mutex_trylock;
10051 fpl__pthread_func_pthread_mutex_unlock *pthread_mutex_unlock;
10052
10053 // pthread_cond_t
10054 fpl__pthread_func_pthread_cond_init *pthread_cond_init;
10055 fpl__pthread_func_pthread_cond_destroy *pthread_cond_destroy;
10056 fpl__pthread_func_pthread_cond_timedwait *pthread_cond_timedwait;
10057 fpl__pthread_func_pthread_cond_wait *pthread_cond_wait;
10058 fpl__pthread_func_pthread_cond_broadcast *pthread_cond_broadcast;
10059 fpl__pthread_func_pthread_cond_signal *pthread_cond_signal;
10060
10061 // sem_t
10062 fpl__pthread_func_sem_init *sem_init;
10063 fpl__pthread_func_sem_destroy *sem_destroy;
10064 fpl__pthread_func_sem_wait *sem_wait;
10065 fpl__pthread_func_sem_timedwait *sem_timedwait;
10066 fpl__pthread_func_sem_trywait *sem_trywait;
10067 fpl__pthread_func_sem_post *sem_post;
10068 fpl__pthread_func_sem_getvalue *sem_getvalue;
10069} fpl__PThreadApi;
10070
10071#define FPL__POSIX_DL_LOADTYPE RTLD_NOW
10072
10073fpl_internal void fpl__PThreadUnloadApi(fpl__PThreadApi *pthreadApi) {
10074 fplAssert(pthreadApi != fpl_null);
10075 if (pthreadApi->libHandle != fpl_null) {
10076 dlclose(pthreadApi->libHandle);
10077 }
10078 fplClearStruct(pthreadApi);
10079}
10080
10081fpl_internal bool fpl__PThreadLoadApi(fpl__PThreadApi *pthreadApi) {
10082 const char *libpthreadFileNames[] = {
10083 "libpthread.so",
10084 "libpthread.so.0",
10085 };
10086 bool result = false;
10087 for (uint32_t index = 0; index < fplArrayCount(libpthreadFileNames); ++index) {
10088 const char *libName = libpthreadFileNames[index];
10089 fplClearStruct(pthreadApi);
10090 do {
10091 void *libHandle = fpl_null;
10092 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_PTHREAD, libHandle, libName);
10093
10094 // pthread_t
10095 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_self, pthread_self);
10096 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_setschedparam, pthread_setschedparam);
10097 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_getschedparam, pthread_getschedparam);
10098 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_setschedprio, pthread_setschedprio);
10099
10100 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_create, pthread_create);
10101 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_kill, pthread_kill);
10102 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_join, pthread_join);
10103 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_exit, pthread_exit);
10104 FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_yield, pthread_yield);
10105 FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_timedjoin_np, pthread_timedjoin_np);
10106
10107 // pthread_attr_t
10108 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_init, pthread_attr_init);
10109 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_getschedparam, pthread_attr_getschedparam);
10110 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setschedparam, pthread_attr_setschedparam);
10111 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setstacksize, pthread_attr_setstacksize);
10112 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setdetachstate, pthread_attr_setdetachstate);
10113 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setschedpolicy, pthread_attr_setschedpolicy);
10114
10115 // pthread_mutex_t
10116 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_init, pthread_mutex_init);
10117 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_destroy, pthread_mutex_destroy);
10118 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_lock, pthread_mutex_lock);
10119 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_trylock, pthread_mutex_trylock);
10120 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_unlock, pthread_mutex_unlock);
10121
10122 // pthread_cond_t
10123 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_init, pthread_cond_init);
10124 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_destroy, pthread_cond_destroy);
10125 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_timedwait, pthread_cond_timedwait);
10126 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_wait, pthread_cond_wait);
10127 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_broadcast, pthread_cond_broadcast);
10128 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_signal, pthread_cond_signal);
10129
10130 // sem_t
10131 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_init, sem_init);
10132 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_destroy, sem_destroy);
10133 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_wait, sem_wait);
10134 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_timedwait, sem_timedwait);
10135 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_trywait, sem_trywait);
10136 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_post, sem_post);
10137 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_getvalue, sem_getvalue);
10138
10139 pthreadApi->libHandle = libHandle;
10140 result = true;
10141 } while (0);
10142 if (result) {
10143 break;
10144 }
10145 fpl__PThreadUnloadApi(pthreadApi);
10146 }
10147 return(result);
10148}
10149
10150typedef struct fpl__PosixInitState {
10151 int dummy;
10152} fpl__PosixInitState;
10153
10154typedef struct fpl__PosixAppState {
10155 fpl__PThreadApi pthreadApi;
10156} fpl__PosixAppState;
10157#endif // FPL_SUBPLATFORM_POSIX
10158
10159// ############################################################################
10160//
10161// > TYPES_LINUX
10162//
10163// ############################################################################
10164#if defined(FPL_PLATFORM_LINUX)
10165typedef struct fpl__LinuxInitState {
10166 int dummy;
10167} fpl__LinuxInitState;
10168
10169#if defined(FPL__ENABLE_WINDOW)
10170#define FPL__LINUX_MAX_GAME_CONTROLLER_COUNT 4
10171typedef struct fpl__LinuxGameController {
10172 char deviceName[512 + 1];
10173 char displayName[FPL_MAX_NAME_LENGTH];
10174 int fd;
10175 uint8_t axisCount;
10176 uint8_t buttonCount;
10177 fplGamepadState state;
10178} fpl__LinuxGameController;
10179
10180typedef struct fpl__LinuxGameControllersState {
10181 fpl__LinuxGameController controllers[FPL__LINUX_MAX_GAME_CONTROLLER_COUNT];
10182 uint64_t lastCheckTime;
10183} fpl__LinuxGameControllersState;
10184#endif
10185
10186typedef struct fpl__LinuxAppState {
10187#if defined(FPL__ENABLE_WINDOW)
10188 fpl__LinuxGameControllersState controllersState;
10189#endif
10190 int dummy;
10191} fpl__LinuxAppState;
10192
10193// Forward declarations
10194#if defined(FPL__ENABLE_WINDOW)
10195fpl_internal void fpl__LinuxFreeGameControllers(fpl__LinuxGameControllersState *controllersState);
10196fpl_internal void fpl__LinuxPollGameControllers(const fplSettings *settings, fpl__LinuxGameControllersState *controllersState, const bool useEvents);
10197#endif
10198
10199#endif // FPL_PLATFORM_LINUX
10200
10201// ############################################################################
10202//
10203// > TYPES_UNIX
10204//
10205// ############################################################################
10206#if defined(FPL_PLATFORM_UNIX)
10207typedef struct fpl__UnixInitState {
10208 int dummy;
10209} fpl__UnixInitState;
10210
10211typedef struct fpl__UnixAppState {
10212 int dummy;
10213} fpl__UnixAppState;
10214#endif // FPL_PLATFORM_UNIX
10215
10216// ############################################################################
10217//
10218// > TYPES_X11
10219//
10220// ############################################################################
10221#if defined(FPL_SUBPLATFORM_X11)
10222
10223#include <X11/keysym.h> // Keyboard symbols (XK_Escape, etc.)
10224
10225//
10226// X11 Api
10227//
10228#define FPL__FUNC_X11_XFree(name) int name(void *data)
10229typedef FPL__FUNC_X11_XFree(fpl__func_x11_XFree);
10230#define FPL__FUNC_X11_XFlush(name) int name(Display *display)
10231typedef FPL__FUNC_X11_XFlush(fpl__func_x11_XFlush);
10232#define FPL__FUNC_X11_XOpenDisplay(name) Display *name(char *display_name)
10233typedef FPL__FUNC_X11_XOpenDisplay(fpl__func_x11_XOpenDisplay);
10234#define FPL__FUNC_X11_XCloseDisplay(name) int name(Display *display)
10235typedef FPL__FUNC_X11_XCloseDisplay(fpl__func_x11_XCloseDisplay);
10236#define FPL__FUNC_X11_XDefaultScreen(name) int name(Display *display)
10237typedef FPL__FUNC_X11_XDefaultScreen(fpl__func_x11_XDefaultScreen);
10238#define FPL__FUNC_X11_XRootWindow(name) Window name(Display *display, int screen_number)
10239typedef FPL__FUNC_X11_XRootWindow(fpl__func_x11_XRootWindow);
10240#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)
10241typedef FPL__FUNC_X11_XCreateWindow(fpl__func_x11_XCreateWindow);
10242#define FPL__FUNC_X11_XDestroyWindow(name) int name(Display *display, Window w)
10243typedef FPL__FUNC_X11_XDestroyWindow(fpl__func_x11_XDestroyWindow);
10244#define FPL__FUNC_X11_XCreateColormap(name) Colormap name(Display *display, Window w, Visual *visual, int alloc)
10245typedef FPL__FUNC_X11_XCreateColormap(fpl__func_x11_XCreateColormap);
10246#define FPL__FUNC_X11_XDefaultColormap(name) Colormap name(Display *display, int screen_number)
10247typedef FPL__FUNC_X11_XDefaultColormap(fpl__func_x11_XDefaultColormap);
10248#define FPL__FUNC_X11_XFreeColormap(name) int name(Display *display, Colormap colormap)
10249typedef FPL__FUNC_X11_XFreeColormap(fpl__func_x11_XFreeColormap);
10250#define FPL__FUNC_X11_XMapWindow(name) int name(Display *display, Window w)
10251typedef FPL__FUNC_X11_XMapWindow(fpl__func_x11_XMapWindow);
10252#define FPL__FUNC_X11_XUnmapWindow(name) int name(Display *display, Window w)
10253typedef FPL__FUNC_X11_XUnmapWindow(fpl__func_x11_XUnmapWindow);
10254#define FPL__FUNC_X11_XStoreName(name) int name(Display *display, Window w, _Xconst char *window_name)
10255typedef FPL__FUNC_X11_XStoreName(fpl__func_x11_XStoreName);
10256#define FPL__FUNC_X11_XDefaultVisual(name) Visual *name(Display *display, int screen_number)
10257typedef FPL__FUNC_X11_XDefaultVisual(fpl__func_x11_XDefaultVisual);
10258#define FPL__FUNC_X11_XDefaultDepth(name) int name(Display *display, int screen_number)
10259typedef FPL__FUNC_X11_XDefaultDepth(fpl__func_x11_XDefaultDepth);
10260#define FPL__FUNC_X11_XInternAtom(name) Atom name(Display *display, const char *atom_name, Bool only_if_exists)
10261typedef FPL__FUNC_X11_XInternAtom(fpl__func_x11_XInternAtom);
10262#define FPL__FUNC_X11_XSetWMProtocols(name) Status name(Display *display, Window w, Atom *protocols, int count)
10263typedef FPL__FUNC_X11_XSetWMProtocols(fpl__func_x11_XSetWMProtocols);
10264#define FPL__FUNC_X11_XPending(name) int name(Display *display)
10265typedef FPL__FUNC_X11_XPending(fpl__func_x11_XPending);
10266#define FPL__FUNC_X11_XSync(name) int name(Display *display, Bool discard)
10267typedef FPL__FUNC_X11_XSync(fpl__func_x11_XSync);
10268#define FPL__FUNC_X11_XNextEvent(name) int name(Display *display, XEvent *event_return)
10269typedef FPL__FUNC_X11_XNextEvent(fpl__func_x11_XNextEvent);
10270#define FPL__FUNC_X11_XPeekEvent(name) int name(Display *display, XEvent *event_return)
10271typedef FPL__FUNC_X11_XPeekEvent(fpl__func_x11_XPeekEvent);
10272#define FPL__FUNC_X11_XEventsQueued(name) int name(Display *display, int mode)
10273typedef FPL__FUNC_X11_XEventsQueued(fpl__func_x11_XEventsQueued);
10274#define FPL__FUNC_X11_XGetWindowAttributes(name) Status name(Display *display, Window w, XWindowAttributes *window_attributes_return)
10275typedef FPL__FUNC_X11_XGetWindowAttributes(fpl__func_x11_XGetWindowAttributes);
10276#define FPL__FUNC_X11_XResizeWindow(name) int name(Display *display, Window w, unsigned int width, unsigned int height)
10277typedef FPL__FUNC_X11_XResizeWindow(fpl__func_x11_XResizeWindow);
10278#define FPL__FUNC_X11_XMoveWindow(name) int name(Display *display, Window w, int x, int y)
10279typedef FPL__FUNC_X11_XMoveWindow(fpl__func_x11_XMoveWindow);
10280#define FPL__FUNC_X11_XGetKeyboardMapping(name) KeySym *name(Display *display, KeyCode first_keycode, int keycode_count, int *keysyms_per_keycode_return)
10281typedef FPL__FUNC_X11_XGetKeyboardMapping(fpl__func_x11_XGetKeyboardMapping);
10282#define FPL__FUNC_X11_XLookupString(name) int name(XKeyEvent* event_struct, char* buffer_return, int bytes_buffer, KeySym* keysym_return, XComposeStatus* status_in_out)
10283typedef FPL__FUNC_X11_XLookupString(fpl__func_x11_XLookupString);
10284#define FPL__FUNC_X11_XSendEvent(name) Status name(Display *display, Window w, Bool propagate, long event_mask, XEvent *event_send)
10285typedef FPL__FUNC_X11_XSendEvent(fpl__func_x11_XSendEvent);
10286#define FPL__FUNC_X11_XMatchVisualInfo(name) Status name(Display* display, int screen, int depth, int clazz, XVisualInfo* vinfo_return)
10287typedef FPL__FUNC_X11_XMatchVisualInfo(fpl__func_x11_XMatchVisualInfo);
10288#define FPL__FUNC_X11_XCreateGC(name) GC name(Display* display, Drawable d, unsigned long valuemask, XGCValues* values)
10289typedef FPL__FUNC_X11_XCreateGC(fpl__func_x11_XCreateGC);
10290#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)
10291typedef FPL__FUNC_X11_XGetImage(fpl__func_x11_XGetImage);
10292#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)
10293typedef FPL__FUNC_X11_XCreateImage(fpl__func_x11_XCreateImage);
10294#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)
10295typedef FPL__FUNC_X11_XPutImage(fpl__func_x11_XPutImage);
10296#define FPL__FUNC_X11_XMapRaised(name) int name(Display *display, Window w)
10297typedef FPL__FUNC_X11_XMapRaised(fpl__func_x11_XMapRaised);
10298#define FPL__FUNC_X11_XCreatePixmap(name) Pixmap name(Display * display, Drawable d, unsigned int width, unsigned int height, unsigned int depth)
10299typedef FPL__FUNC_X11_XCreatePixmap(fpl__func_x11_XCreatePixmap);
10300#define FPL__FUNC_X11_XSelectInput(name) int name(Display * display, Window w, long eventMask)
10301typedef FPL__FUNC_X11_XSelectInput(fpl__func_x11_XSelectInput);
10302#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)
10303typedef FPL__FUNC_X11_XGetWindowProperty(fpl__func_x11_XGetWindowProperty);
10304#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)
10305typedef FPL__FUNC_X11_XChangeProperty(fpl__func_x11_XChangeProperty);
10306#define FPL__FUNC_X11_XDeleteProperty(name) int name(Display* display, Window w,Atom prop)
10307typedef FPL__FUNC_X11_XDeleteProperty(fpl__func_x11_XDeleteProperty);
10308#define FPL__FUNC_X11_XStringListToTextProperty(name) Status name(char** list, int count, XTextProperty* text_prop_return)
10309typedef FPL__FUNC_X11_XStringListToTextProperty(fpl__func_x11_XStringListToTextProperty);
10310#define FPL__FUNC_X11_XSetWMIconName(name) void name(Display* display, Window w, XTextProperty *text_prop)
10311typedef FPL__FUNC_X11_XSetWMIconName(fpl__func_x11_XSetWMIconName);
10312#define FPL__FUNC_X11_XSetWMName(name) void name(Display* display, Window w, XTextProperty *text_prop)
10313typedef FPL__FUNC_X11_XSetWMName(fpl__func_x11_XSetWMName);
10314#define FPL__FUNC_X11_XQueryKeymap(name) int name(Display* display, char [32])
10315typedef FPL__FUNC_X11_XQueryKeymap(fpl__func_x11_XQueryKeymap);
10316#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)
10317typedef FPL__FUNC_X11_XQueryPointer(fpl__func_x11_XQueryPointer);
10318#define FPL__FUNC_X11_XConvertSelection(name) int name(Display *display, Atom selection, Atom target, Atom property, Window requestor, Time time)
10319typedef FPL__FUNC_X11_XConvertSelection(fpl__func_x11_XConvertSelection);
10320#define FPL__FUNC_X11_XInitThreads(name) Status name(void)
10321typedef FPL__FUNC_X11_XInitThreads(fpl__func_x11_XInitThreads);
10322#define FPL__FUNC_X11_XSetErrorHandler(name) XErrorHandler name(XErrorHandler *handler)
10323typedef FPL__FUNC_X11_XSetErrorHandler(fpl__func_x11_XSetErrorHandler);
10324
10325typedef struct fpl__X11Api {
10326 void *libHandle;
10327 fpl__func_x11_XFlush *XFlush;
10328 fpl__func_x11_XFree *XFree;
10329 fpl__func_x11_XOpenDisplay *XOpenDisplay;
10330 fpl__func_x11_XCloseDisplay *XCloseDisplay;
10331 fpl__func_x11_XDefaultScreen *XDefaultScreen;
10332 fpl__func_x11_XRootWindow *XRootWindow;
10333 fpl__func_x11_XCreateWindow *XCreateWindow;
10334 fpl__func_x11_XDestroyWindow *XDestroyWindow;
10335 fpl__func_x11_XCreateColormap *XCreateColormap;
10336 fpl__func_x11_XFreeColormap *XFreeColormap;
10337 fpl__func_x11_XDefaultColormap *XDefaultColormap;
10338 fpl__func_x11_XMapWindow *XMapWindow;
10339 fpl__func_x11_XUnmapWindow *XUnmapWindow;
10340 fpl__func_x11_XStoreName *XStoreName;
10341 fpl__func_x11_XDefaultVisual *XDefaultVisual;
10342 fpl__func_x11_XDefaultDepth *XDefaultDepth;
10343 fpl__func_x11_XInternAtom *XInternAtom;
10344 fpl__func_x11_XSetWMProtocols *XSetWMProtocols;
10345 fpl__func_x11_XPending *XPending;
10346 fpl__func_x11_XSync *XSync;
10347 fpl__func_x11_XNextEvent *XNextEvent;
10348 fpl__func_x11_XPeekEvent *XPeekEvent;
10349 fpl__func_x11_XEventsQueued *XEventsQueued;
10350 fpl__func_x11_XGetWindowAttributes *XGetWindowAttributes;
10351 fpl__func_x11_XResizeWindow *XResizeWindow;
10352 fpl__func_x11_XMoveWindow *XMoveWindow;
10353 fpl__func_x11_XGetKeyboardMapping *XGetKeyboardMapping;
10354 fpl__func_x11_XLookupString *XLookupString;
10355 fpl__func_x11_XSendEvent *XSendEvent;
10356 fpl__func_x11_XMatchVisualInfo *XMatchVisualInfo;
10357 fpl__func_x11_XCreateGC *XCreateGC;
10358 fpl__func_x11_XGetImage *XGetImage;
10359 fpl__func_x11_XPutImage *XPutImage;
10360 fpl__func_x11_XMapRaised *XMapRaised;
10361 fpl__func_x11_XCreateImage *XCreateImage;
10362 fpl__func_x11_XCreatePixmap *XCreatePixmap;
10363 fpl__func_x11_XSelectInput *XSelectInput;
10364 fpl__func_x11_XGetWindowProperty *XGetWindowProperty;
10365 fpl__func_x11_XChangeProperty *XChangeProperty;
10366 fpl__func_x11_XDeleteProperty *XDeleteProperty;
10367 fpl__func_x11_XStringListToTextProperty *XStringListToTextProperty;
10368 fpl__func_x11_XSetWMIconName *XSetWMIconName;
10369 fpl__func_x11_XSetWMName *XSetWMName;
10370 fpl__func_x11_XQueryKeymap *XQueryKeymap;
10371 fpl__func_x11_XQueryPointer *XQueryPointer;
10372 fpl__func_x11_XConvertSelection *XConvertSelection;
10373 fpl__func_x11_XInitThreads *XInitThreads;
10374 fpl__func_x11_XSetErrorHandler *XSetErrorHandler;
10375} fpl__X11Api;
10376
10377fpl_internal void fpl__UnloadX11Api(fpl__X11Api *x11Api) {
10378 fplAssert(x11Api != fpl_null);
10379 if (x11Api->libHandle != fpl_null) {
10380 dlclose(x11Api->libHandle);
10381 }
10382 fplClearStruct(x11Api);
10383}
10384
10385fpl_internal bool fpl__LoadX11Api(fpl__X11Api *x11Api) {
10386 fplAssert(x11Api != fpl_null);
10387 const char *libFileNames[] = {
10388 "libX11.so",
10389 "libX11.so.7",
10390 "libX11.so.6",
10391 "libX11.so.5",
10392 };
10393 bool result = false;
10394 for (uint32_t index = 0; index < fplArrayCount(libFileNames); ++index) {
10395 const char *libName = libFileNames[index];
10396 fplClearStruct(x11Api);
10397 do {
10398 void *libHandle = fpl_null;
10399 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_X11, libHandle, libName);
10400 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XFlush, XFlush);
10401 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XFree, XFree);
10402 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XOpenDisplay, XOpenDisplay);
10403 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCloseDisplay, XCloseDisplay);
10404 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultScreen, XDefaultScreen);
10405 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XRootWindow, XRootWindow);
10406 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateWindow, XCreateWindow);
10407 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDestroyWindow, XDestroyWindow);
10408 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateColormap, XCreateColormap);
10409 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XFreeColormap, XFreeColormap);
10410 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultColormap, XDefaultColormap);
10411 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMapWindow, XMapWindow);
10412 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XUnmapWindow, XUnmapWindow);
10413 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XStoreName, XStoreName);
10414 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultVisual, XDefaultVisual);
10415 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultDepth, XDefaultDepth);
10416 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XInternAtom, XInternAtom);
10417 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetWMProtocols, XSetWMProtocols);
10418 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XPending, XPending);
10419 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSync, XSync);
10420 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XNextEvent, XNextEvent);
10421 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XPeekEvent, XPeekEvent);
10422 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XEventsQueued, XEventsQueued);
10423 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetWindowAttributes, XGetWindowAttributes);
10424 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XResizeWindow, XResizeWindow);
10425 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMoveWindow, XMoveWindow);
10426 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetKeyboardMapping, XGetKeyboardMapping);
10427 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XLookupString, XLookupString);
10428 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSendEvent, XSendEvent);
10429 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMatchVisualInfo, XMatchVisualInfo);
10430 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateGC, XCreateGC);
10431 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetImage, XGetImage);
10432 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XPutImage, XPutImage);
10433 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMapRaised, XMapRaised);
10434 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateImage, XCreateImage);
10435 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreatePixmap, XCreatePixmap);
10436 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSelectInput, XSelectInput);
10437 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetWindowProperty, XGetWindowProperty);
10438 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XChangeProperty, XChangeProperty);
10439 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDeleteProperty, XDeleteProperty);
10440 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XStringListToTextProperty, XStringListToTextProperty);
10441 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetWMIconName, XSetWMIconName);
10442 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetWMName, XSetWMName);
10443 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XQueryKeymap, XQueryKeymap);
10444 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XQueryPointer, XQueryPointer);
10445 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XConvertSelection, XConvertSelection);
10446 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XInitThreads, XInitThreads);
10447 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetErrorHandler, XSetErrorHandler);
10448 x11Api->libHandle = libHandle;
10449 result = true;
10450 } while (0);
10451 if (result) {
10452 break;
10453 }
10454 fpl__UnloadX11Api(x11Api);
10455 }
10456 return(result);
10457}
10458
10459typedef struct fpl__X11SubplatformState {
10460 fpl__X11Api api;
10461} fpl__X11SubplatformState;
10462
10463typedef struct fpl__X11WindowStateInfo {
10464 fplWindowState state;
10465 fplWindowVisibilityState visibility;
10466 fplWindowPosition position;
10467 fplWindowSize size;
10468} fpl__X11WindowStateInfo;
10469
10470typedef struct fpl__X11Xdnd {
10471 int version;
10472 Window source;
10473 Atom format;
10474} fpl__X11Xdnd;
10475
10476#define FPL__FUNC_X11_ErrorHandlerCallback(name) int name(Display *display, XErrorEvent *ev)
10477typedef FPL__FUNC_X11_ErrorHandlerCallback(fpl__func_X11ErrorHandlerCallback);
10478
10479typedef struct fpl__X11WindowState {
10480 fpl__X11WindowStateInfo lastWindowStateInfo;
10481 Colormap colorMap;
10482 Display *display;
10483 fpl__func_X11ErrorHandlerCallback *lastErrorHandler;
10484 fpl__X11Xdnd xdnd;
10485 Window root;
10486 Window window;
10487 Visual *visual;
10488 Atom wmProtocols;
10489 Atom wmDeleteWindow;
10490 Atom wmState;
10491 Atom netWMPing;
10492 Atom netWMState;
10493 Atom netWMStateFocused;
10494 Atom netWMStateFullscreen;
10495 Atom netWMStateHidden;
10496 Atom netWMStateMaximizedVert;
10497 Atom netWMStateMaximizedHorz;
10498 Atom netWMPid;
10499 Atom netWMIcon;
10500 Atom netWMName;
10501 Atom netWMIconName;
10502 Atom utf8String;
10503 Atom motifWMHints;
10504 // drag and drop
10505 Atom xdndAware;
10506 Atom xdndEnter;
10507 Atom xdndPosition;
10508 Atom xdndStatus;
10509 Atom xdndActionCopy;
10510 Atom xdndDrop;
10511 Atom xdndFinished;
10512 Atom xdndSelection;
10513 Atom xdndTypeList;
10514 Atom textUriList;
10515 int screen;
10516 int colorDepth;
10517} fpl__X11WindowState;
10518
10519#define FPL__XDND_VERSION 5
10520
10521#endif // FPL_SUBPLATFORM_X11
10522
10523// ****************************************************************************
10524//
10525// > PLATFORM_STATES
10526//
10527// - Defines the final PlatformInitState and PlatformAppState
10528// - Declares all required global variables
10529//
10530// ****************************************************************************
10531#if !defined(FPL__PLATFORM_STATES_DEFINED)
10532#define FPL__PLATFORM_STATES_DEFINED
10533//
10534// Platform initialization state
10535//
10536typedef struct fpl__PlatformInitSettings {
10537 fplMemorySettings memorySettings;
10538} fpl__PlatformInitSettings;
10539
10540typedef struct fpl__PlatformInitState {
10541#if defined(FPL_SUBPLATFORM_POSIX)
10542 fpl__PosixInitState posix;
10543#endif
10544
10545 fpl__PlatformInitSettings initSettings;
10546 fplPlatformResultType initResult;
10547 fpl_b32 isInitialized;
10548
10549 union {
10550# if defined(FPL_PLATFORM_WINDOWS)
10551 fpl__Win32InitState win32;
10552# elif defined(FPL_PLATFORM_LINUX)
10553 fpl__LinuxInitState plinux;
10554# elif defined(FPL_PLATFORM_UNIX)
10555 fpl__UnixInitState punix;
10556# endif
10557 };
10558} fpl__PlatformInitState;
10559fpl_globalvar fpl__PlatformInitState fpl__global__InitState = fplZeroInit;
10560
10561#if defined(FPL__ENABLE_WINDOW)
10562#define FPL__MAX_EVENT_COUNT 32768
10563typedef struct fpl__EventQueue {
10564 // @FIXME(final): Internal events are not Thread-Safe!
10565 fplEvent events[FPL__MAX_EVENT_COUNT];
10566 uint32_t pollIndex;
10567 uint32_t pushCount;
10568} fpl__EventQueue;
10569
10570typedef struct fpl__PlatformWindowState {
10571 fpl__EventQueue eventQueue;
10572 fplKey keyMap[256];
10573 fplButtonState keyStates[256];
10574 uint64_t keyPressTimes[256];
10575 fplButtonState mouseStates[5];
10576 fpl_b32 isRunning;
10577
10578#if defined(FPL_PLATFORM_WINDOWS)
10579 fpl__Win32WindowState win32;
10580#endif
10581#if defined(FPL_SUBPLATFORM_X11)
10582 fpl__X11WindowState x11;
10583#endif
10584} fpl__PlatformWindowState;
10585#endif // FPL__ENABLE_WINDOW
10586
10587#if defined(FPL__ENABLE_VIDEO)
10588typedef struct fpl__PlatformVideoState {
10589 void *mem; // Points to fpl__VideoState
10590 size_t memSize;
10591} fpl__PlatformVideoState;
10592#endif // FPL__ENABLE_VIDEO
10593
10594#if defined(FPL__ENABLE_AUDIO)
10595typedef struct fpl__PlatformAudioState {
10596 void *mem; // Points to fpl__AudioState
10597 size_t memSize;
10598 size_t maxBackendSize;
10599 uintptr_t offsetToBackend;
10600} fpl__PlatformAudioState;
10601#endif
10602
10603//
10604// Platform application state
10605//
10606typedef struct fpl__PlatformAppState fpl__PlatformAppState;
10607struct fpl__PlatformAppState {
10608 // Subplatforms
10609#if defined(FPL_SUBPLATFORM_POSIX)
10610 fpl__PosixAppState posix;
10611#endif
10612#if defined(FPL_SUBPLATFORM_X11)
10613 fpl__X11SubplatformState x11;
10614#endif
10615
10616 // Window/video/audio
10617#if defined(FPL__ENABLE_WINDOW)
10618 fpl__PlatformWindowState window;
10619#endif
10620#if defined(FPL__ENABLE_VIDEO)
10621 fpl__PlatformVideoState video;
10622#endif
10623#if defined(FPL__ENABLE_AUDIO)
10624 fpl__PlatformAudioState audio;
10625#endif
10626
10627 // Settings
10628 fplSettings initSettings;
10629 fplSettings currentSettings;
10630 fplInitFlags initFlags;
10631
10632 // Platforms
10633 union {
10634# if defined(FPL_PLATFORM_WINDOWS)
10635 fpl__Win32AppState win32;
10636# elif defined(FPL_PLATFORM_LINUX)
10637 fpl__LinuxAppState plinux;
10638# elif defined(FPL_PLATFORM_UNIX)
10639 fpl__UnixAppState plinux;
10640# endif
10641 };
10642};
10643
10644//
10645// Internal window
10646//
10647#if defined(FPL__ENABLE_WINDOW)
10648fpl_internal fplKey fpl__GetMappedKey(const fpl__PlatformWindowState *windowState, const uint64_t keyCode) {
10649 fplKey result;
10650 if (keyCode < fplArrayCount(windowState->keyMap))
10651 result = windowState->keyMap[keyCode];
10652 else
10653 result = fplKey_None;
10654 return(result);
10655}
10656
10657fpl_internal void fpl__ClearInternalEvents(void) {
10658 fpl__PlatformAppState *appState = fpl__global__AppState;
10659 fplAssert(appState != fpl_null);
10660 fpl__EventQueue *eventQueue = &appState->window.eventQueue;
10661 // @FIXME(final): Internal events are not thread-safe, introduce a proper thread-safe queue here!
10662 uint32_t eventCount = eventQueue->pollIndex;
10663 eventQueue->pollIndex = 0;
10664 for (size_t eventIndex = 0; eventIndex < eventCount; ++eventIndex) {
10665 fplEvent *ev = &eventQueue->events[eventIndex];
10667 fpl__ReleaseDynamicMemory(ev->window.dropFiles.internalMemory.base);
10669 }
10670 }
10671 eventQueue->pushCount = 0;
10672}
10673
10674fpl_internal bool fpl__PollInternalEvent(fplEvent *ev) {
10675 fpl__PlatformAppState *appState = fpl__global__AppState;
10676 bool result = false;
10677 if (appState != fpl_null) {
10678 fpl__EventQueue *eventQueue = &appState->window.eventQueue;
10679 // @FIXME(final): Internal events are not thread-safe, introduce a proper thread-safe queue here!
10680 if (eventQueue->pollIndex < eventQueue->pushCount) {
10681 uint32_t eventIndex = eventQueue->pollIndex++;
10682 *ev = eventQueue->events[eventIndex];
10683 result = true;
10684 } else if (eventQueue->pushCount > 0) {
10685 eventQueue->pollIndex = 0;
10686 eventQueue->pushCount = 0;
10687 }
10688 }
10689 return(result);
10690}
10691
10692fpl_internal void fpl__PushInternalEvent(const fplEvent *event) {
10693 fpl__PlatformAppState *appState = fpl__global__AppState;
10694 fplAssert(appState != fpl_null);
10695 fpl__EventQueue *eventQueue = &appState->window.eventQueue;
10696 // @FIXME(final): Internal events are not thread-safe, introduce a proper thread-safe queue here!
10697 if (eventQueue->pushCount < FPL__MAX_EVENT_COUNT) {
10698 uint32_t eventIndex = eventQueue->pushCount++;
10699 eventQueue->events[eventIndex] = *event;
10700 }
10701}
10702
10703fpl_internal void fpl__PushWindowStateEvent(const fplWindowEventType windowType) {
10704 fplEvent newEvent = fplZeroInit;
10705 newEvent.type = fplEventType_Window;
10706 newEvent.window.type = windowType;
10707 fpl__PushInternalEvent(&newEvent);
10708}
10709
10710fpl_internal void fpl__PushWindowSizeEvent(const fplWindowEventType windowType, uint32_t w, uint32_t h) {
10711 fplEvent newEvent = fplZeroInit;
10712 newEvent.type = fplEventType_Window;
10713 newEvent.window.type = windowType;
10714 newEvent.window.size.width = w;
10715 newEvent.window.size.height = h;
10716 fpl__PushInternalEvent(&newEvent);
10717}
10718
10719fpl_internal void fpl__PushWindowPositionEvent(const fplWindowEventType windowType, int32_t x, int32_t y) {
10720 fplEvent newEvent = fplZeroInit;
10721 newEvent.type = fplEventType_Window;
10722 newEvent.window.type = windowType;
10723 newEvent.window.position.left = x;
10724 newEvent.window.position.top = y;
10725 fpl__PushInternalEvent(&newEvent);
10726}
10727
10728fpl_internal void fpl__PushWindowDropFilesEvent(const char *filePath, const size_t fileCount, const char **files, const fplMemoryBlock *memory) {
10729 fplEvent newEvent = fplZeroInit;
10730 newEvent.type = fplEventType_Window;
10732 newEvent.window.dropFiles.fileCount = fileCount;
10733 newEvent.window.dropFiles.files = files;
10734 newEvent.window.dropFiles.internalMemory = *memory;
10735 fpl__PushInternalEvent(&newEvent);
10736}
10737
10738fpl_internal void fpl__PushKeyboardButtonEvent(const uint64_t keyCode, const fplKey mappedKey, const fplKeyboardModifierFlags modifiers, const fplButtonState buttonState) {
10739 fplEvent newEvent = fplZeroInit;
10740 newEvent.type = fplEventType_Keyboard;
10742 newEvent.keyboard.keyCode = keyCode;
10743 newEvent.keyboard.modifiers = modifiers;
10744 newEvent.keyboard.buttonState = buttonState;
10745 newEvent.keyboard.mappedKey = mappedKey;
10746 fpl__PushInternalEvent(&newEvent);
10747}
10748
10749fpl_internal void fpl__PushKeyboardInputEvent(const uint32_t textCode, const fplKey mappedKey) {
10750 fplEvent newEvent = fplZeroInit;
10751 newEvent.type = fplEventType_Keyboard;
10753 newEvent.keyboard.keyCode = (uint64_t)textCode;
10754 newEvent.keyboard.mappedKey = mappedKey;
10755 fpl__PushInternalEvent(&newEvent);
10756}
10757
10758fpl_internal void fpl__PushMouseButtonEvent(const int32_t x, const int32_t y, const fplMouseButtonType mouseButton, const fplButtonState buttonState) {
10759 fplEvent newEvent = fplZeroInit;
10760 newEvent.type = fplEventType_Mouse;
10762 newEvent.mouse.mouseX = x;
10763 newEvent.mouse.mouseY = y;
10764 newEvent.mouse.mouseButton = mouseButton;
10765 newEvent.mouse.buttonState = buttonState;
10766 fpl__PushInternalEvent(&newEvent);
10767}
10768
10769fpl_internal void fpl__PushMouseWheelEvent(const int32_t x, const int32_t y, const float wheelDelta) {
10770 fplEvent newEvent = fplZeroInit;
10771 newEvent.type = fplEventType_Mouse;
10774 newEvent.mouse.mouseX = x;
10775 newEvent.mouse.mouseY = y;
10776 newEvent.mouse.wheelDelta = wheelDelta;
10777 fpl__PushInternalEvent(&newEvent);
10778}
10779
10780fpl_internal void fpl__PushMouseMoveEvent(const int32_t x, const int32_t y) {
10781 fplEvent newEvent = fplZeroInit;
10782 newEvent.type = fplEventType_Mouse;
10785 newEvent.mouse.mouseX = x;
10786 newEvent.mouse.mouseY = y;
10787 fpl__PushInternalEvent(&newEvent);
10788}
10789
10790fpl_internal void fpl__HandleKeyboardButtonEvent(fpl__PlatformWindowState *windowState, const uint64_t time, const uint64_t keyCode, const fplKeyboardModifierFlags modifiers, const fplButtonState buttonState, const bool force) {
10791#if defined(FPL_LOG_KEY_EVENTS)
10792 const char *buttonStateName = "";
10793 if (buttonState == fplButtonState_Press)
10794 buttonStateName = "Press";
10795 else if (buttonState == fplButtonState_Repeat)
10796 buttonStateName = "Repeat";
10797 else
10798 buttonStateName = "Released";
10799 FPL_LOG_INFO(FPL__MODULE_OS, "[%llu] Keyboard button event with keycode: '%llu', state: '%s'", time, keyCode, buttonStateName);
10800#endif
10801
10802 fplKey mappedKey = fpl__GetMappedKey(windowState, keyCode);
10803 bool repeat = false;
10804 if (force) {
10805 repeat = (buttonState == fplButtonState_Repeat);
10806 windowState->keyStates[keyCode] = buttonState;
10807 } else {
10808 if (keyCode < fplArrayCount(windowState->keyStates)) {
10809 if ((buttonState == fplButtonState_Release) && (windowState->keyStates[keyCode] == fplButtonState_Release)) {
10810 return;
10811 }
10812 if ((buttonState == fplButtonState_Press) && (windowState->keyStates[keyCode] >= fplButtonState_Press)) {
10813 repeat = true;
10814 }
10815 windowState->keyStates[keyCode] = buttonState;
10816 }
10817 }
10818 fpl__PushKeyboardButtonEvent(keyCode, mappedKey, modifiers, repeat ? fplButtonState_Repeat : buttonState);
10819}
10820
10821fpl_internal void fpl__HandleKeyboardInputEvent(fpl__PlatformWindowState *windowState, const uint64_t keyCode, const uint32_t textCode) {
10822 fplKey mappedKey = fpl__GetMappedKey(windowState, keyCode);
10823 fpl__PushKeyboardInputEvent(textCode, mappedKey);
10824}
10825
10826fpl_internal void fpl__HandleMouseButtonEvent(fpl__PlatformWindowState *windowState, const int32_t x, const int32_t y, const fplMouseButtonType mouseButton, const fplButtonState buttonState) {
10827 if (mouseButton < fplArrayCount(windowState->mouseStates)) {
10828 windowState->mouseStates[(int)mouseButton] = buttonState;
10829 }
10830 fpl__PushMouseButtonEvent(x, y, mouseButton, buttonState);
10831}
10832
10833fpl_internal void fpl__HandleMouseMoveEvent(fpl__PlatformWindowState *windowState, const int32_t x, const int32_t y) {
10834 fpl__PushMouseMoveEvent(x, y);
10835}
10836
10837fpl_internal void fpl__HandleMouseWheelEvent(fpl__PlatformWindowState *windowState, const int32_t x, const int32_t y, const float wheelDelta) {
10838 fpl__PushMouseWheelEvent(x, y, wheelDelta);
10839}
10840
10841// @NOTE(final): Callback used for setup a window before it is created
10842#define FPL__FUNC_PREPARE_VIDEO_WINDOW(name) bool name(fpl__PlatformAppState *appState, const fplInitFlags initFlags, const fplSettings *initSettings)
10843typedef FPL__FUNC_PREPARE_VIDEO_WINDOW(callback_PreSetupWindow);
10844
10845// @NOTE(final): Callback used for setup a window after it was created
10846#define FPL__FUNC_FINALIZE_VIDEO_WINDOW(name) bool name(fpl__PlatformAppState *appState, const fplInitFlags initFlags, const fplSettings *initSettings)
10847typedef FPL__FUNC_FINALIZE_VIDEO_WINDOW(callback_PostSetupWindow);
10848
10849typedef struct fpl__SetupWindowCallbacks {
10850 callback_PreSetupWindow *preSetup;
10851 callback_PostSetupWindow *postSetup;
10852} fpl__SetupWindowCallbacks;
10853#endif // FPL__ENABLE_WINDOW
10854
10855#endif // FPL__PLATFORM_STATES_DEFINED
10856
10857// ****************************************************************************
10858//
10859// > COMMON (Common Implementations)
10860//
10861// - Standard C library includes
10862// - Internal macros
10863// - Error state handling
10864// - Threading state handling
10865// - CommonAudioState
10866// - Common Implementations (Strings, Memory, Atomics, Path, etc.)
10867//
10868// ****************************************************************************
10869#if !defined(FPL__COMMON_DEFINED)
10870#define FPL__COMMON_DEFINED
10871
10872//
10873// Internal memory
10874//
10875fpl_internal void *fpl__AllocateDynamicMemory(const size_t size, const size_t alignment) {
10876 void *result = fpl__AllocateMemory(&fpl__global__InitState.initSettings.memorySettings.dynamic, size, alignment);
10877 return(result);
10878}
10879
10880fpl_internal void fpl__ReleaseDynamicMemory(void *ptr) {
10881 fpl__ReleaseMemory(&fpl__global__InitState.initSettings.memorySettings.dynamic, ptr);
10882}
10883
10884fpl_internal void *fpl__AllocateTemporaryMemory(const size_t size, const size_t alignment) {
10885 void *result = fpl__AllocateMemory(&fpl__global__InitState.initSettings.memorySettings.temporary, size, alignment);
10886 return(result);
10887}
10888
10889fpl_internal void fpl__ReleaseTemporaryMemory(void *ptr) {
10890 fpl__ReleaseMemory(&fpl__global__InitState.initSettings.memorySettings.temporary, ptr);
10891}
10892
10893//
10894// Audio constants
10895//
10896static const uint32_t FPL__DEFAULT_AUDIO_SAMPLERATE = 44100;
10897static const fplAudioFormatType FPL__DEFAULT_AUDIO_FORMAT = fplAudioFormatType_S16;
10898static const uint16_t FPL__DEFAULT_AUDIO_CHANNELS = 2;
10899static const uint16_t FPL__DEFAULT_AUDIO_PERIODS = 3;
10900static const uint32_t FPL__DEFAULT_AUDIO_BUFFERSIZE_LOWLATENCY_IN_MSECS = 10;
10901static const uint32_t FPL__DEFAULT_AUDIO_BUFFERSIZE_CONSERVATIVE_IN_MSECS = 25;
10902
10903//
10904// Internal types and functions
10905//
10906#define FPL__MAX_LAST_ERROR_STRING_LENGTH 256
10907#define FPL__MAX_ERRORSTATE_COUNT 256
10908
10909typedef struct fpl__ErrorState {
10910 char errors[FPL__MAX_ERRORSTATE_COUNT][FPL__MAX_LAST_ERROR_STRING_LENGTH];
10911 uint32_t count;
10912} fpl__ErrorState;
10913
10914fpl_globalvar fpl__ErrorState fpl__global__LastErrorState = fplZeroInit;
10915
10916fpl_internal void fpl__PushError_Formatted(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, va_list argList) {
10917 fplAssert(format != fpl_null);
10918
10919 if (level <= fplLogLevel_Error) {
10920 fpl__ErrorState *state = &fpl__global__LastErrorState;
10921 char buffer[FPL__MAX_LAST_ERROR_STRING_LENGTH] = fplZeroInit;
10922 size_t formattedLen = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
10923 if (formattedLen > 0) {
10924 fplAssert(state->count < FPL__MAX_ERRORSTATE_COUNT);
10925 size_t errorIndex = state->count;
10926 state->count = (state->count + 1) % FPL__MAX_ERRORSTATE_COUNT;
10927 fplCopyStringLen(buffer, formattedLen, state->errors[errorIndex], FPL__MAX_LAST_ERROR_STRING_LENGTH);
10928 }
10929 }
10930
10931#if defined(FPL__ENABLE_LOGGING)
10932 va_list listCopy;
10933 va_copy(listCopy, argList);
10934 fpl__LogWriteArgs(funcName, lineNumber, level, format, listCopy);
10935 va_end(listCopy);
10936#endif
10937}
10938
10939fpl_internal void fpl__HandleError(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, ...) {
10940 va_list valist;
10941 va_start(valist, format);
10942 fpl__PushError_Formatted(funcName, lineNumber, level, format, valist);
10943 va_end(valist);
10944
10945#if defined(FPL_CRASH_ON_ERROR) || defined(FPL_CRASH_ON_WARNING)
10946 fplLogLevel minErrorLevel = fplLogLevel_Error;
10947# if defined(FPL_CRASH_ON_WARNING)
10948 minErrorLevel = fplLogLevel_Warning;
10949# endif
10950 if (level >= minErrorLevel) {
10951 // @NOTE(final): Force a null pointer assignment crash here
10952 *(int *)0 = 0;
10953 }
10954#endif
10955}
10956
10957//
10958// Argument Errors
10959//
10960
10961//
10962
10963
10964fpl_internal void fpl__ArgumentInvalidError(const char *funcName, const int line, const char *paramName) {
10965 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter are not valid", paramName);
10966}
10967fpl_internal void fpl__ArgumentNullError(const char *funcName, const int line, const char *paramName) {
10968 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter are not allowed to be null", paramName);
10969}
10970fpl_internal void fpl__ArgumentZeroError(const char *funcName, const int line, const char *paramName) {
10971 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter must be greater than zero", paramName);
10972}
10973fpl_internal void fpl__ArgumentMinError(const char *funcName, const int line, const char *paramName, const size_t value, const size_t minValue) {
10974 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter '%zu' must be greater or equal than '%zu'", paramName, value, minValue);
10975}
10976fpl_internal void fpl__ArgumentMaxError(const char *funcName, const int line, const char *paramName, const size_t value, const size_t maxValue) {
10977 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter '%zu' must be less or equal than '%zu'", paramName, value, maxValue);
10978}
10979fpl_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) {
10980 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter '%zu' must be in range of '%zu' to '%zu'", paramName, value, minValue, maxValue);
10981}
10982
10983#define FPL__CheckArgumentInvalid(arg, cond, ret) \
10984 if((cond)) { \
10985 fpl__ArgumentInvalidError(FPL_FUNCTION_NAME, __LINE__, #arg); \
10986 return (ret); \
10987 }
10988
10989#define FPL__CheckArgumentInvalidNoRet(arg, cond) \
10990 if((cond)) { \
10991 fpl__ArgumentInvalidError(FPL_FUNCTION_NAME, __LINE__, #arg); \
10992 return; \
10993 }
10994#define FPL__CheckArgumentNull(arg, ret) \
10995 if((arg) == fpl_null) { \
10996 fpl__ArgumentNullError(FPL_FUNCTION_NAME, __LINE__, #arg); \
10997 return (ret); \
10998 }
10999#define FPL__CheckArgumentNullNoRet(arg) \
11000 if((arg) == fpl_null) { \
11001 fpl__ArgumentNullError(FPL_FUNCTION_NAME, __LINE__, #arg); \
11002 return; \
11003 }
11004#define FPL__CheckArgumentZero(arg, ret) \
11005 if((arg) == 0) { \
11006 fpl__ArgumentZeroError(FPL_FUNCTION_NAME, __LINE__, #arg); \
11007 return (ret); \
11008 }
11009#define FPL__CheckArgumentZeroNoRet(arg) \
11010 if((arg) == 0) { \
11011 fpl__ArgumentZeroError(FPL_FUNCTION_NAME, __LINE__, #arg); \
11012 return; \
11013 }
11014#define FPL__CheckArgumentMin(arg, minValue, ret) \
11015 if((arg) < (minValue)) { \
11016 fpl__ArgumentMinError(FPL_FUNCTION_NAME, __LINE__, #arg, arg, minValue); \
11017 return (ret); \
11018 }
11019#define FPL__CheckArgumentMax(arg, maxValue, ret) \
11020 if((arg) > (maxValue)) { \
11021 fpl__ArgumentMaxError(FPL_FUNCTION_NAME, __LINE__, #arg, arg, maxValue); \
11022 return (ret); \
11023 }
11024#define FPL__CheckPlatform(ret) \
11025 if(fpl__global__AppState == fpl_null) { \
11026 FPL__ERROR(FPL__MODULE_CORE, "[%s] Platform is not initialized", FPL_FUNCTION_NAME); \
11027 return (ret); \
11028 }
11029#define FPL__CheckPlatformNoRet() \
11030 if(fpl__global__AppState == fpl_null) { \
11031 FPL__ERROR(FPL__MODULE_CORE, "[%s] Platform is not initialized", FPL_FUNCTION_NAME); \
11032 return; \
11033 }
11034
11035#define FPL__CheckApi(cond, name, ret) \
11036 if(!(cond)) { \
11037 FPL__ERROR(FPL__MODULE_API, "The API '%s' is not loaded", (name)); \
11038 return (ret); \
11039 }
11040#define FPL__CheckApiNoRet(cond, name) \
11041 if(!(cond)) { \
11042 FPL__ERROR(FPL__MODULE_API, "The API '%s' is not loaded", (name)); \
11043 return; \
11044 }
11045
11046#if !defined(FPL_MAX_THREAD_COUNT)
11047 // Maximum number of active threads you can have in your process
11048# define FPL_MAX_THREAD_COUNT 256
11049#endif
11050
11051#if !defined(FPL_MAX_SIGNAL_COUNT)
11052 // Maximum number of active signals you can wait for
11053# define FPL_MAX_SIGNAL_COUNT 256
11054#endif
11055
11056typedef struct fpl__ThreadState {
11057 fplThreadHandle mainThread;
11058 fplThreadHandle threads[FPL_MAX_THREAD_COUNT];
11059} fpl__ThreadState;
11060
11061fpl_globalvar fpl__ThreadState fpl__global__ThreadState = fplZeroInit;
11062
11063fpl_internal fplThreadHandle *fpl__GetFreeThread(void) {
11064 fplThreadHandle *result = fpl_null;
11065 for (uint32_t index = 0; index < FPL_MAX_THREAD_COUNT; ++index) {
11066 fplThreadHandle *thread = fpl__global__ThreadState.threads + index;
11067 fplThreadState state = fplGetThreadState(thread);
11068 if (state == fplThreadState_Stopped) {
11069 result = thread;
11070 break;
11071 }
11072 }
11073 return(result);
11074}
11075
11076fpl_internal bool fpl__IsEqualsMemory(const void *a, const void *b, const size_t size) {
11077 const uint8_t *ptrA = (const uint8_t *)a;
11078 const uint8_t *ptrB = (const uint8_t *)b;
11079 size_t s = size;
11080 // @SPEED(final): This may be very slow, so we should use a faster function for comparing memory.
11081 bool result = true;
11082#if 1
11083 while (s-- > 0) {
11084 if (*ptrA != *ptrB) {
11085 result = false;
11086 break;
11087 }
11088 ptrA++;
11089 ptrB++;
11090 }
11091#else
11092 result = memcmp(a, b, size) == 0;
11093#endif
11094 return(result);
11095}
11096
11097fpl_internal bool fpl__IsZeroMemory(const void *memory, const size_t size) {
11098 const uint8_t *ptr = (const uint8_t *)memory;
11099 // @SPEED(final): This may be very slow, so we should use a faster function for comparing memory.
11100 bool result = true;
11101#if 1
11102 size_t s = size;
11103 while (s-- > 0) {
11104 if (*ptr) {
11105 result = false;
11106 break;
11107 }
11108 ptr++;
11109 }
11110#else
11111 result = memcmp(a, b, size) == 0;
11112#endif
11113 return(result);
11114}
11115
11116//
11117// Common Strings
11118//
11119#if !defined(FPL__COMMON_STRINGS_DEFINED)
11120#define FPL__COMMON_STRINGS_DEFINED
11121
11122fpl_common_api bool fplIsStringMatchWildcard(const char *source, const char *wildcard) {
11123 // Supported patterns:
11124 // * = Match zero or more characters
11125 // ? = Match one character
11126 if (source == fpl_null || wildcard == fpl_null) {
11127 return false;
11128 }
11129 const char *s = source;
11130 const char *w = wildcard;
11131 while (*w) {
11132 if (*w == '?') {
11133 if (!*s) {
11134 return false;
11135 }
11136 ++s;
11137 } else if (*w == '*') {
11138 while (*s) {
11139 char nw = w[1];
11140 if (nw != 0) {
11141 if ((*s == nw) || (nw == '?') || (nw == '*')) {
11142 break;
11143 }
11144 }
11145 ++s;
11146 }
11147 } else {
11148 if (*s != *w) {
11149 return false;
11150 }
11151 ++s;
11152 }
11153 ++w;
11154 }
11155 return true;
11156}
11157
11158fpl_common_api bool fplIsStringEqualLen(const char *a, const size_t aLen, const char *b, const size_t bLen) {
11159 if ((a == fpl_null) || (b == fpl_null)) {
11160 return false;
11161 }
11162 if (aLen != bLen) {
11163 return false;
11164 }
11165 bool result = true;
11166 for (size_t index = 0; index < aLen; ++index) {
11167 char aChar = a[index];
11168 char bChar = b[index];
11169 if (aChar != bChar) {
11170 result = false;
11171 break;
11172 }
11173 }
11174 return(result);
11175}
11176
11177fpl_common_api bool fplIsStringEqual(const char *a, const char *b) {
11178 if ((a == fpl_null) || (b == fpl_null)) {
11179 return (a == b);
11180 }
11181 bool result = true;
11182 for (;;) {
11183 const char aChar = *(a++);
11184 const char bChar = *(b++);
11185 if (aChar == 0 || bChar == 0) {
11186 result = (aChar == bChar);
11187 break;
11188 }
11189 if (aChar != bChar) {
11190 result = false;
11191 break;
11192 }
11193 }
11194 return(result);
11195}
11196
11197fpl_common_api char *fplEnforcePathSeparatorLen(char *path, size_t maxPathLen) {
11198 FPL__CheckArgumentNull(path, fpl_null);
11199 FPL__CheckArgumentZero(maxPathLen, fpl_null);
11200 char *end = path;
11201 while (*end) {
11202 end++;
11203 }
11204 size_t len = end - path;
11205 char *result = fpl_null;
11206 if (len > 0) {
11207 if (path[len - 1] != FPL_PATH_SEPARATOR) {
11208 if (len + 1 <= maxPathLen) {
11209 path[len] = FPL_PATH_SEPARATOR;
11210 path[len + 1] = 0;
11211 result = &path[len + 1];
11212 } else {
11213 FPL__ERROR(FPL__MODULE_PATHS, "Cannot append path separator: Max length '%zu' of path '%s' is exceeded", maxPathLen, path);
11214 }
11215 } else {
11216 result = &path[len];
11217 }
11218 }
11219 return(result);
11220}
11221
11222fpl_common_api char *fplEnforcePathSeparator(char *path) {
11223 FPL__CheckArgumentNull(path, fpl_null);
11224 char *end = path;
11225 while (*end) {
11226 end++;
11227 }
11228 size_t len = end - path;
11229 char *result = fpl_null;
11230 if (len > 0) {
11231 if (path[len - 1] != FPL_PATH_SEPARATOR) {
11232 path[len] = FPL_PATH_SEPARATOR;
11233 path[len + 1] = 0;
11234 result = &path[len + 1];
11235 } else {
11236 result = &path[len];
11237 }
11238 }
11239 return(result);
11240}
11241
11242fpl_common_api char *fplStringAppendLen(const char *appended, const size_t appendedLen, char *buffer, size_t maxBufferLen) {
11243 FPL__CheckArgumentNull(appended, fpl_null);
11244 FPL__CheckArgumentZero(maxBufferLen, fpl_null);
11245 if (appendedLen == 0) {
11246 return buffer;
11247 }
11248 size_t curBufferLen = fplGetStringLength(buffer);
11249 size_t requiredSize = curBufferLen + appendedLen + 1;
11250 FPL__CheckArgumentMin(maxBufferLen, requiredSize, fpl_null);
11251 char *start = buffer + curBufferLen;
11252 size_t remainingBufferSize = maxBufferLen - (curBufferLen > 0 ? curBufferLen + 1 : 0);
11253 char *result = fplCopyStringLen(appended, appendedLen, start, remainingBufferSize);
11254 return(result);
11255}
11256
11257fpl_common_api char *fplStringAppend(const char *appended, char *buffer, size_t maxBufferLen) {
11258 size_t appendedLen = fplGetStringLength(appended);
11259 char *result = fplStringAppendLen(appended, appendedLen, buffer, maxBufferLen);
11260 return(result);
11261}
11262
11263fpl_common_api size_t fplGetStringLength(const char *str) {
11264 uint32_t result = 0;
11265 if (str != fpl_null) {
11266 while (*str++) {
11267 result++;
11268 }
11269 }
11270 return(result);
11271}
11272
11273fpl_common_api char *fplCopyStringLen(const char *source, const size_t sourceLen, char *dest, const size_t maxDestLen) {
11274 if (source != fpl_null && dest != fpl_null) {
11275 size_t requiredLen = sourceLen + 1;
11276 FPL__CheckArgumentMin(maxDestLen, requiredLen, fpl_null);
11277 fplMemoryCopy(source, sourceLen * sizeof(char), dest);
11278 char *result = dest + sourceLen;
11279 *result = 0;
11280 return(result);
11281 } else {
11282 return(fpl_null);
11283 }
11284}
11285
11286fpl_common_api char *fplCopyString(const char *source, char *dest, const size_t maxDestLen) {
11287 char *result = fpl_null;
11288 if (source != fpl_null) {
11289 size_t sourceLen = fplGetStringLength(source);
11290 result = fplCopyStringLen(source, sourceLen, dest, maxDestLen);
11291 }
11292 return(result);
11293}
11294
11295fpl_common_api size_t fplStringFormatArgs(char *destBuffer, const size_t maxDestBufferLen, const char *format, va_list argList) {
11296 FPL__CheckArgumentNull(format, 0);
11297
11298 va_list listCopy;
11299 va_copy(listCopy, argList);
11300
11301 // @NOTE(final): Need to clear the first character, otherwise vsnprintf() does weird things... O_o
11302 if (destBuffer != fpl_null) {
11303 FPL__CheckArgumentMin(maxDestBufferLen, 1, 0);
11304 destBuffer[0] = 0;
11305 }
11306
11307 int charCount = 0;
11308# if defined(FPL_NO_CRT)
11309# if defined(FPL_USERFUNC_vsnprintf)
11310 charCount = FPL_USERFUNC_vsnprintf(destBuffer, maxDestBufferLen, format, listCopy);
11311# else
11312 charCount = 0;
11313# endif
11314# else
11315 charCount = vsnprintf(destBuffer, maxDestBufferLen, format, listCopy);
11316# endif
11317 if (charCount < 0) {
11318 FPL__ERROR(FPL__MODULE_STRINGS, "Format parameter are '%s' are invalid", format);
11319 return 0;
11320 }
11321 size_t result = charCount;
11322 if (destBuffer != fpl_null) {
11323 size_t requiredMaxDestBufferLen = charCount + 1;
11324 FPL__CheckArgumentMin(maxDestBufferLen, requiredMaxDestBufferLen, 0);
11325 destBuffer[charCount] = 0;
11326 }
11327 va_end(listCopy);
11328 return(result);
11329}
11330
11331fpl_common_api size_t fplStringFormat(char *destBuffer, const size_t maxDestBufferLen, const char *format, ...) {
11332 FPL__CheckArgumentNull(format, 0);
11333 va_list argList;
11334 va_start(argList, format);
11335 size_t result = fplStringFormatArgs(destBuffer, maxDestBufferLen, format, argList);
11336 va_end(argList);
11337 return(result);
11338}
11339
11340fpl_common_api size_t fplS32ToString(const int32_t value, char *buffer, const size_t maxBufferLen) {
11341 int32_t v = value;
11342
11343 bool isNegative = false;
11344 if (v < 0) {
11345 isNegative = true;
11346 v = -v;
11347 }
11348
11349 int32_t tmp = v;
11350 size_t digitCount = 0;
11351 do {
11352 tmp = tmp / 10;
11353 ++digitCount;
11354 } while (tmp != 0);
11355
11356 size_t result = digitCount + (isNegative ? 1 : 0);
11357
11358 if (buffer != fpl_null) {
11359 size_t requiredLen = result + 1;
11360 FPL__CheckArgumentMin(maxBufferLen, requiredLen, 0);
11361
11362 char *p = buffer;
11363 if (isNegative) {
11364 *p++ = '-';
11365 }
11366 p += digitCount;// Go back to the very end, because we are writing the digits back in reverse order
11367 char *lastP = p;
11368
11369 const char *digits = "0123456789";
11370 tmp = value;
11371 do {
11372 *--p = digits[tmp % 10];
11373 tmp /= 10;
11374 } while (tmp != 0);
11375
11376 *lastP = 0;
11377 }
11378
11379 return (result);
11380}
11381
11382fpl_common_api int32_t fplStringToS32Len(const char *str, const size_t len) {
11383 FPL__CheckArgumentNull(str, 0);
11384 FPL__CheckArgumentZero(len, 0);
11385 const char *p = str;
11386 bool isNegative = false;
11387 if (*p == '-') {
11388 if (len == 1) {
11389 return 0;
11390 }
11391 isNegative = true;
11392 ++p;
11393 }
11394 uint32_t value = 0;
11395 while (*p && ((size_t)(p - str) < len)) {
11396 char c = *p;
11397 if (c < '0' || c > '9') {
11398 return(0);
11399 }
11400 int v = (int)(*p - '0');
11401 value *= 10;
11402 value += (uint32_t)v;
11403 ++p;
11404 }
11405 int32_t result = isNegative ? -(int32_t)value : (int32_t)value;
11406 return(result);
11407}
11408
11409fpl_common_api int32_t fplStringToS32(const char *str) {
11410 size_t len = fplGetStringLength(str);
11411 int32_t result = fplStringToS32Len(str, len);
11412 return(result);
11413}
11414#endif // FPL__COMMON_STRINGS_DEFINED
11415
11416//
11417// Common Console
11418//
11419#if !defined(FPL__COMMON_CONSOLE_DEFINED)
11420#define FPL__COMMON_CONSOLE_DEFINED
11421
11422fpl_common_api void fplConsoleFormatOut(const char *format, ...) {
11423 FPL__CheckArgumentNullNoRet(format);
11424 char buffer[FPL_MAX_BUFFER_LENGTH];
11425 va_list argList;
11426 va_start(argList, format);
11427 size_t len = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
11428 va_end(argList);
11429 if (len > 0) {
11430 fplConsoleOut(buffer);
11431 }
11432}
11433
11434fpl_common_api void fplConsoleFormatError(const char *format, ...) {
11435 FPL__CheckArgumentNullNoRet(format);
11436 char buffer[FPL_MAX_BUFFER_LENGTH];
11437 va_list argList;
11438 va_start(argList, format);
11439 size_t len = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
11440 va_end(argList);
11441 if (len > 0) {
11442 fplConsoleError(buffer);
11443 }
11444}
11445#endif // FPL__COMMON_CONSOLE_DEFINED
11446
11447//
11448// Common Memory
11449//
11450#if !defined(FPL__COMMON_MEMORY_DEFINED)
11451#define FPL__COMMON_MEMORY_DEFINED
11452
11453fpl_common_api void *fplMemoryAlignedAllocate(const size_t size, const size_t alignment) {
11454 FPL__CheckArgumentZero(size, fpl_null);
11455 FPL__CheckArgumentZero(alignment, fpl_null);
11456 if (alignment & (alignment - 1)) {
11457 FPL__ERROR(FPL__MODULE_MEMORY, "Alignment parameter '%zu' must be a power of two", alignment);
11458 return fpl_null;
11459 }
11460 // Allocate empty memory to hold a size of a pointer + alignment padding + the actual data
11461 size_t newSize = sizeof(void *) + (alignment << 1) + size;
11462 void *basePtr = fplMemoryAllocate(newSize);
11463 // The resulting address starts after the stored base pointer
11464 void *alignedPtr = (void *)((uint8_t *)basePtr + sizeof(void *));
11465 // Move the resulting address to a aligned one when not aligned
11466 uintptr_t mask = alignment - 1;
11467 if ((alignment > 1) && (((uintptr_t)alignedPtr & mask) != 0)) {
11468 uintptr_t offset = ((uintptr_t)alignment - ((uintptr_t)alignedPtr & mask));
11469 alignedPtr = (uint8_t *)alignedPtr + offset;
11470 }
11471 // Write the base pointer before the alignment pointer
11472 *(void **)((void *)((uint8_t *)alignedPtr - sizeof(void *))) = basePtr;
11473 // Ensure alignment
11474 fplAssert(fplIsAligned(alignedPtr, alignment));
11475 return(alignedPtr);
11476}
11477
11478fpl_common_api void fplMemoryAlignedFree(void *ptr) {
11479 FPL__CheckArgumentNullNoRet(ptr);
11480 // Free the base pointer which is stored to the left from the given pointer
11481 void *basePtr = *(void **)((void *)((uint8_t *)ptr - sizeof(void *)));
11482 fplAssert(basePtr != fpl_null);
11483 fplMemoryFree(basePtr);
11484}
11485
11486#define FPL__MEM_SHIFT_64 3
11487#define FPL__MEM_MASK_64 0x00000007
11488#define FPL__MEM_SHIFT_32 2
11489#define FPL__MEM_MASK_32 0x00000003
11490#define FPL__MEM_SHIFT_16 1
11491#define FPL__MEM_MASK_16 0x0000000
11492
11493// Clearing memory in chunks
11494#define FPL__MEMORY_SET(T, memory, size, shift, mask, value) \
11495 do { \
11496 size_t setBytes = 0; \
11497 if (sizeof(T) > sizeof(uint8_t)) { \
11498 T setValue = 0; \
11499 for (uint32_t bytesIncrement = 0; bytesIncrement < sizeof(T); ++bytesIncrement) { \
11500 uint32_t bitShift = bytesIncrement * 8; \
11501 setValue |= ((T)value << bitShift); \
11502 } \
11503 T *dataBlock = (T *)(memory); \
11504 T *dataBlockEnd = (T *)(memory) + (size >> shift); \
11505 while (dataBlock < dataBlockEnd) { \
11506 *dataBlock++ = setValue; \
11507 setBytes += sizeof(T); \
11508 } \
11509 } \
11510 uint8_t *data8 = (uint8_t *)memory + setBytes; \
11511 uint8_t *data8End = (uint8_t *)memory + size; \
11512 while (data8 < data8End) { \
11513 *data8++ = value; \
11514 } \
11515 } while (0);
11516
11517#define FPL__MEMORY_CLEAR(T, memory, size, shift, mask) \
11518 do { \
11519 size_t clearBytes = 0; \
11520 if (sizeof(T) > sizeof(uint8_t)) { \
11521 T *dataBlock = (T *)(memory); \
11522 T *dataBlockEnd = (T *)(memory) + (size >> shift); \
11523 while (dataBlock < dataBlockEnd) { \
11524 *dataBlock++ = 0; \
11525 clearBytes += sizeof(T); \
11526 } \
11527 } \
11528 uint8_t *data8 = (uint8_t *)memory + clearBytes; \
11529 uint8_t *data8End = (uint8_t *)memory + size; \
11530 while (data8 < data8End) { \
11531 *data8++ = 0; \
11532 } \
11533 } while (0);
11534
11535#define FPL__MEMORY_COPY(T, source, sourceSize, dest, shift, mask) \
11536 do { \
11537 size_t copiedBytes = 0; \
11538 if (sizeof(T) > sizeof(uint8_t)) { \
11539 const T *sourceDataBlock = (const T *)(source); \
11540 const T *sourceDataBlockEnd = (const T *)(source) + (sourceSize >> shift); \
11541 T *destDataBlock = (T *)(dest); \
11542 while (sourceDataBlock < sourceDataBlockEnd) { \
11543 *destDataBlock++ = *sourceDataBlock++; \
11544 copiedBytes += sizeof(T); \
11545 } \
11546 } \
11547 const uint8_t *sourceData8 = (const uint8_t *)source + copiedBytes; \
11548 const uint8_t *sourceData8End = (const uint8_t *)source + sourceSize; \
11549 uint8_t *destData8 = (uint8_t *)dest + copiedBytes; \
11550 while (sourceData8 < sourceData8End) { \
11551 *destData8++ = *sourceData8++; \
11552 } \
11553 } while (0);
11554
11555fpl_common_api void fplMemorySet(void *mem, const uint8_t value, const size_t size) {
11556 FPL__CheckArgumentNullNoRet(mem);
11557 FPL__CheckArgumentZeroNoRet(size);
11558#if defined(FPL__ENABLE_MEMORY_MACROS)
11559 if (size % 8 == 0) {
11560 FPL__MEMORY_SET(uint64_t, mem, size, FPL__MEM_SHIFT_64, FPL__MEM_MASK_64, value);
11561 } else if (size % 4 == 0) {
11562 FPL__MEMORY_SET(uint32_t, mem, size, FPL__MEM_SHIFT_32, FPL__MEM_MASK_32, value);
11563 } else if (size % 2 == 0) {
11564 FPL__MEMORY_SET(uint16_t, mem, size, FPL__MEM_SHIFT_16, FPL__MEM_MASK_16, value);
11565 } else {
11566 FPL__MEMORY_SET(uint8_t, mem, size, 0, 0, value);
11567 }
11568#elif defined(FPL_PLATFORM_WINDOWS)
11569 FillMemory(mem, size, value);
11570#else
11571 memset(mem, value, size);
11572#endif
11573}
11574
11575fpl_common_api void fplMemoryClear(void *mem, const size_t size) {
11576 FPL__CheckArgumentNullNoRet(mem);
11577 FPL__CheckArgumentZeroNoRet(size);
11578#if defined(FPL__ENABLE_MEMORY_MACROS)
11579 if (size % 8 == 0) {
11580 FPL__MEMORY_CLEAR(uint64_t, mem, size, FPL__MEM_SHIFT_64, FPL__MEM_MASK_64);
11581 } else if (size % 4 == 0) {
11582 FPL__MEMORY_CLEAR(uint32_t, mem, size, FPL__MEM_SHIFT_32, FPL__MEM_MASK_32);
11583 } else if (size % 2 == 0) {
11584 FPL__MEMORY_CLEAR(uint16_t, mem, size, FPL__MEM_SHIFT_16, FPL__MEM_MASK_16);
11585 } else {
11586 FPL__MEMORY_CLEAR(uint8_t, mem, size, 0, 0);
11587 }
11588#elif defined(FPL_PLATFORM_WINDOWS)
11589 ZeroMemory(mem, size);
11590#else
11591 memset(mem, 0, size);
11592#endif
11593}
11594
11595fpl_common_api void fplMemoryCopy(const void *sourceMem, const size_t sourceSize, void *targetMem) {
11596 FPL__CheckArgumentNullNoRet(sourceMem);
11597 FPL__CheckArgumentZeroNoRet(sourceSize);
11598 FPL__CheckArgumentNullNoRet(targetMem);
11599#if defined(FPL__ENABLE_MEMORY_MACROS)
11600 if (sourceSize % 8 == 0) {
11601 FPL__MEMORY_COPY(uint64_t, sourceMem, sourceSize, targetMem, FPL__MEM_SHIFT_64, FPL__MEM_MASK_64);
11602 } else if (sourceSize % 4 == 0) {
11603 FPL__MEMORY_COPY(uint32_t, sourceMem, sourceSize, targetMem, FPL__MEM_SHIFT_32, FPL__MEM_MASK_32);
11604 } else if (sourceSize % 2 == 0) {
11605 FPL__MEMORY_COPY(uint16_t, sourceMem, sourceSize, targetMem, FPL__MEM_SHIFT_16, FPL__MEM_MASK_16);
11606 } else {
11607 FPL__MEMORY_COPY(uint8_t, sourceMem, sourceSize, targetMem, 0, 0);
11608 }
11609#elif defined(FPL_PLATFORM_WINDOWS)
11610 CopyMemory(targetMem, sourceMem, sourceSize);
11611#else
11612 memcpy(targetMem, sourceMem, sourceSize);
11613#endif
11614}
11615#endif // FPL__COMMON_MEMORY_DEFINED
11616
11617//
11618// Common Hardware
11619//
11620// https://github.com/google/cpu_features
11621// https://github.com/google/benchmark/blob/v1.1.0/src/cycleclock.h
11622
11623//
11624// X86/X64 only (CPUID, XCR0, RDTSC)
11625//
11626#if defined(FPL_ARCH_X64) || defined(FPL_ARCH_X86)
11627
11628# define FPL__CPU_BRAND_BUFFER_SIZE 0x40
11629
11630# if defined(FPL_COMPILER_MSVC)
11631
11632 // CPUID/XCR0 for MSVC
11633# if _MSC_VER >= 1400
11634# define fpl__m_CPUID(outLeaf, functionId) __cpuid((int *)(outLeaf)->raw, (int)(functionId))
11635# endif
11636# if _MSC_VER >= 1600
11637# define fpl__m_GetXCR0() ((uint64_t)_xgetbv(0))
11638# endif
11639
11640 // RDTSC for MSVC
11641# define fpl__m_RDTSC() ((uint64_t)__rdtsc())
11642
11643# elif defined(FPL_COMPILER_GCC) ||defined(FPL_COMPILER_CLANG)
11644
11645 // CPUID for GCC/CLANG
11646fpl_internal void fpl__m_CPUID(fplCPUIDLeaf *outLeaf, const uint32_t functionId) {
11647 int eax = 0, ebx = 0, ecx = 0, edx = 0;
11648 __cpuid_count(functionId, 0, eax, ebx, ecx, edx);
11649 outLeaf->eax = eax;
11650 outLeaf->ebx = ebx;
11651 outLeaf->ecx = ecx;
11652 outLeaf->edx = edx;
11653}
11654
11655 // XCR0 for GCC/CLANG
11656fpl_internal uint64_t fpl__m_GetXCR0(void) {
11657 uint32_t eax, edx;
11658 __asm(".byte 0x0F, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0));
11659 return eax;
11660}
11661
11662 // RDTSC for non-MSVC
11663# if defined(FPL_ARCH_X86)
11664fpl_force_inline uint64_t fpl__m_RDTSC(void) {
11665 unsigned long long int result;
11666 __asm__ volatile (".byte 0x0f, 0x31" : "=A" (result));
11667 return((uint64_t)result);
11668}
11669# elif defined(FPL_ARCH_X64)
11670fpl_force_inline uint64_t fpl__m_RDTSC(void) {
11671 unsigned hi, lo;
11672 __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
11673 uint64_t result = (uint64_t)(((unsigned long long)lo) | (((unsigned long long)hi) << 32));
11674 return (result);
11675}
11676# endif
11677# endif
11678
11679fpl_common_api bool fplCPUID(const uint32_t functionId, fplCPUIDLeaf *outLeaf) {
11680#if defined(fpl__m_CPUID)
11681 fpl__m_CPUID(outLeaf, functionId);
11682 return true;
11683#endif
11684 return false;
11685}
11686
11687fpl_common_api uint64_t fplCPUXCR0(void) {
11688#if defined(fpl__m_GetXCR0)
11689 uint64_t result = fpl__m_GetXCR0();
11690 return(result);
11691#else
11692 return(0ULL);
11693#endif
11694}
11695
11696fpl_common_api uint64_t fplCPURDTSC(void) {
11697#if defined(fpl__m_RDTSC)
11698 uint64_t result = fpl__m_RDTSC();
11699 return(result);
11700#else
11701 return(0ULL);
11702#endif
11703}
11704
11706 fplClearStruct(outCaps);
11708
11709 fplCPUIDLeaf info0 = fplZeroInit;
11710 fplCPUIDLeaf info1 = fplZeroInit;
11711 fplCPUIDLeaf info7 = fplZeroInit;
11712 fplCPUIDLeaf tempLeaf = fplZeroInit;
11713
11714 if (!fplCPUID(0, &info0))
11715 return false;
11716
11717 uint32_t maxFunctionId = info0.eax;
11718
11719 if (1 <= maxFunctionId) {
11720 if (!fplCPUID(1, &info1))
11721 return false;
11722 }
11723
11724 if (7 <= maxFunctionId) {
11725 if (!fplCPUID(7, &info7))
11726 return false;
11727 }
11728
11729 bool hasXSave = fplIsBitSet(info1.ecx, 26) && fplIsBitSet(info1.ecx, 27);
11730 uint64_t xcr0 = hasXSave ? fplCPUXCR0() : 0;
11731
11732 const uint32_t MASK_XMM = 0x2;
11733 const uint32_t MASK_YMM = 0x4;
11734 const uint32_t MASK_MASKREG = 0x20;
11735 const uint32_t MASK_ZMM0_15 = 0x40;
11736 const uint32_t MASK_ZMM16_31 = 0x80;
11737
11738 const uint32_t MASK_SSE = MASK_XMM;
11739 const uint32_t MASK_AVX = MASK_XMM | MASK_YMM;
11740 const uint32_t MASK_AVX_512 = MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 | MASK_ZMM16_31;
11741
11742 bool hasSSESupport = (xcr0 & MASK_SSE) == MASK_SSE;
11743 bool hasAVXSupport = (xcr0 & MASK_AVX) == MASK_AVX;
11744 bool hasAVX512Support = (xcr0 & MASK_AVX_512) == MASK_AVX_512;
11745
11746 outCaps->x86.hasMMX = fplIsBitSet(info1.edx, 23);
11747
11748 if (hasSSESupport) {
11749 outCaps->x86.hasSSE = fplIsBitSet(info1.edx, 25);
11750 outCaps->x86.hasSSE2 = fplIsBitSet(info1.edx, 26);
11751 outCaps->x86.hasSSE3 = fplIsBitSet(info1.ecx, 0);
11752 outCaps->x86.hasSSSE3 = fplIsBitSet(info1.ecx, 9);
11753 outCaps->x86.hasSSE4_1 = fplIsBitSet(info1.ecx, 19);
11754 outCaps->x86.hasSSE4_2 = fplIsBitSet(info1.ecx, 20);
11755 }
11756
11757 if (hasAVXSupport) {
11758 outCaps->x86.hasAVX = fplIsBitSet(info1.ecx, 28);
11759 outCaps->x86.hasAVX2 = fplIsBitSet(info7.ebx, 5);
11760 }
11761
11762 if (hasAVX512Support) {
11763 outCaps->x86.hasAVX512 = fplIsBitSet(info7.ebx, 16);
11764 }
11765
11766 outCaps->x86.hasFMA3 = fplIsBitSet(info1.ecx, 12);
11767
11768 outCaps->x86.hasAES_NI = fplIsBitSet(info1.ecx, 25);
11769 outCaps->x86.hasSHA = fplIsBitSet(info7.ebx, 29);
11770 outCaps->x86.hasBMI1 = fplIsBitSet(info7.ebx, 3);
11771 outCaps->x86.hasBMI2 = fplIsBitSet(info7.ebx, 8);
11772 outCaps->x86.hasADX = fplIsBitSet(info7.ebx, 19);
11773 outCaps->x86.hasF16C = fplIsBitSet(info1.ecx, 29);
11774
11775 if (fplCPUID(0x80000001, &tempLeaf)) {
11776 outCaps->x86.hasEM64T = fplIsBitSet(info1.edx, 29);
11777 }
11778
11779 return(true);
11780}
11781
11782fpl_common_api size_t fplCPUGetName(char *destBuffer, const size_t maxDestBufferLen) {
11783 fplCPUIDLeaf cpuInfo = fplZeroInit;
11784 fplCPUID(0x80000000, &cpuInfo);
11785 uint32_t extendedIds = cpuInfo.eax;
11786
11787 // Get the information associated with each extended ID. Interpret CPU brand string.
11788 char cpuBrandBuffer[FPL__CPU_BRAND_BUFFER_SIZE] = fplZeroInit;
11789 uint32_t max = fplMin(extendedIds, 0x80000004);
11790 for (uint32_t i = 0x80000002; i <= max; ++i) {
11791 fplCPUID(i, &cpuInfo);
11792 uint32_t offset = (i - 0x80000002) << 4;
11793 fplMemoryCopy(cpuInfo.raw, sizeof(cpuInfo), cpuBrandBuffer + offset);
11794 }
11795
11796 size_t result = fplGetStringLength(cpuBrandBuffer);
11797 if (destBuffer != fpl_null) {
11798 // Copy result back to the dest buffer
11799 size_t requiredDestBufferLen = result + 1;
11800 FPL__CheckArgumentMin(maxDestBufferLen, requiredDestBufferLen, 0);
11801 fplCopyStringLen(cpuBrandBuffer, result, destBuffer, maxDestBufferLen);
11802 }
11803
11804 return(result);
11805}
11806#else
11807
11808fpl_common_api uint64_t fplCPURDTSC(void) {
11809#if defined(FPL_ARCH_ARM64)
11810 int64_t virtual_timer_value;
11811 fplAsm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
11812 return (uint64_t)virtual_timer_value;
11813#elif defined(FPL_ARCH_ARM32) && (__ARM_ARCH >= 6)
11814 uint32_t pmccntr;
11815 uint32_t pmuseren;
11816 uint32_t pmcntenset;
11817 // Read the user mode perf monitor counter access permissions.
11818 fplAsm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
11819 if (pmuseren & 1) {
11820 // Allows reading perfmon counters for user mode code.
11821 fplAsm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
11822 if (pmcntenset & 0x80000000ul) {
11823 // Is it counting?
11824 fplAsm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
11825 // The counter is set up to count every 64th cycle
11826 return (uint64_t)pmccntr * 64ULL; // Should optimize to << 6
11827 }
11828 }
11829#endif
11830 struct timeval tv;
11831 gettimeofday(&tv, fpl_null);
11832 return (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
11833}
11834
11835fpl_common_api uint64_t fplCPUXCR0(void) {
11836 // Not supported on non-x86 platforms
11837 return(0);
11838}
11839
11840fpl_common_api bool fplCPUID(const uint32_t functionId, fplCPUIDLeaf *outLeaf) {
11841 // Not supported on non-x86 platforms
11842 return(false);
11843}
11844
11846 // @IMPLEMENT(final): fplCPUGetCapabilities for non-x86 architectures
11847 return(false);
11848}
11849
11850fpl_common_api size_t fplCPUGetName(char *destBuffer, const size_t maxDestBufferLen) {
11851 // @IMPLEMENT(final): fplCPUGetName for non-x86 architectures
11852 return(0);
11853}
11854#endif
11855
11856//
11857// Common Atomics
11858//
11859#if !defined(FPL__COMMON_ATOMICS_DEFINED)
11860#define FPL__COMMON_ATOMICS_DEFINED
11861
11862fpl_common_api size_t fplAtomicFetchAndAddSize(volatile size_t *dest, const size_t addend) {
11863 fplAssert(dest != fpl_null);
11864#if defined(FPL_CPU_64BIT)
11865 size_t result = (size_t)fplAtomicFetchAndAddU64((volatile uint64_t *)dest, (uint64_t)addend);
11866#elif defined(FPL_CPU_32BIT)
11867 size_t result = (size_t)fplAtomicFetchAndAddU32((volatile uint32_t *)dest, (uint32_t)addend);
11868#else
11869# error "Unsupported architecture/platform!"
11870#endif // FPL_ARCH
11871 return (result);
11872}
11873
11874fpl_common_api void *fplAtomicFetchAndAddPtr(volatile void **dest, const intptr_t addend) {
11875 fplAssert(dest != fpl_null);
11876#if defined(FPL_CPU_64BIT)
11877 void *result = (void *)fplAtomicFetchAndAddS64((volatile int64_t *)dest, (int64_t)addend);
11878#elif defined(FPL_CPU_32BIT)
11879 void *result = (void *)fplAtomicFetchAndAddS32((volatile int32_t *)dest, (int32_t)addend);
11880#else
11881# error "Unsupported architecture/platform!"
11882#endif // FPL_ARCH
11883 return (result);
11884}
11885
11886fpl_common_api size_t fplAtomicAddAndFetchSize(volatile size_t *value, const size_t addend) {
11887 fplAssert(value != fpl_null);
11888#if defined(FPL_CPU_64BIT)
11889 size_t result = (size_t)fplAtomicAddAndFetchU64((volatile uint64_t *)value, (uint64_t)addend);
11890#elif defined(FPL_CPU_32BIT)
11891 size_t result = (size_t)fplAtomicAddAndFetchU32((volatile uint32_t *)value, (uint32_t)addend);
11892#else
11893# error "Unsupported architecture/platform!"
11894#endif // FPL_ARCH
11895 return (result);
11896}
11897
11898fpl_common_api void *fplAtomicAddAndFetchPtr(volatile void **value, const intptr_t addend) {
11899 fplAssert(value != fpl_null);
11900#if defined(FPL_CPU_64BIT)
11901 void *result = (void *)fplAtomicAddAndFetchU64((volatile uint64_t *)value, (uint64_t)addend);
11902#elif defined(FPL_CPU_32BIT)
11903 void *result = (void *)fplAtomicAddAndFetchU32((volatile uint32_t *)value, (uint32_t)addend);
11904#else
11905# error "Unsupported architecture/platform!"
11906#endif // FPL_ARCH
11907 return (result);
11908}
11909
11910fpl_common_api size_t fplAtomicIncrementSize(volatile size_t *value) {
11911 fplAssert(value != fpl_null);
11912#if defined(FPL_CPU_64BIT)
11913 size_t result = (size_t)fplAtomicIncrementU64((volatile uint64_t *)value);
11914#elif defined(FPL_CPU_32BIT)
11915 size_t result = (size_t)fplAtomicIncrementU32((volatile uint32_t *)value);
11916#else
11917# error "Unsupported architecture/platform!"
11918#endif // FPL_ARCH
11919 return (result);
11920}
11921
11922fpl_common_api void *fplAtomicIncrementPtr(volatile void **value) {
11923 fplAssert(value != fpl_null);
11924#if defined(FPL_CPU_64BIT)
11925 void *result = (void *)fplAtomicAddAndFetchU64((volatile uint64_t *)value, 8);
11926#elif defined(FPL_CPU_32BIT)
11927 void *result = (void *)fplAtomicAddAndFetchU32((volatile uint32_t *)value, 4);
11928#else
11929# error "Unsupported architecture/platform!"
11930#endif // FPL_ARCH
11931 return (result);
11932}
11933
11934fpl_common_api size_t fplAtomicExchangeSize(volatile size_t *target, const size_t value) {
11935 fplAssert(target != fpl_null);
11936#if defined(FPL_CPU_64BIT)
11937 size_t result = (size_t)fplAtomicExchangeU64((volatile uint64_t *)target, (uint64_t)value);
11938#elif defined(FPL_CPU_32BIT)
11939 size_t result = (size_t)fplAtomicExchangeU32((volatile uint32_t *)target, (uint32_t)value);
11940#else
11941# error "Unsupported architecture/platform!"
11942#endif // FPL_ARCH
11943 return (result);
11944}
11945fpl_common_api void *fplAtomicExchangePtr(volatile void **target, const void *value) {
11946 fplAssert(target != fpl_null);
11947#if defined(FPL_CPU_64BIT)
11948 void *result = (void *)fplAtomicExchangeU64((volatile uint64_t *)target, (uint64_t)value);
11949#elif defined(FPL_CPU_32BIT)
11950 void *result = (void *)fplAtomicExchangeU32((volatile uint32_t *)target, (uint32_t)value);
11951#else
11952# error "Unsupported architecture/platform!"
11953#endif // FPL_ARCH
11954 return (result);
11955}
11956
11957fpl_common_api size_t fplAtomicCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange) {
11958 fplAssert(dest != fpl_null);
11959#if defined(FPL_CPU_64BIT)
11960 size_t result = (size_t)fplAtomicCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
11961#elif defined(FPL_CPU_32BIT)
11962 size_t result = (size_t)fplAtomicCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
11963#else
11964# error "Unsupported architecture/platform!"
11965#endif // FPL_ARCH
11966 return (result);
11967}
11968fpl_common_api void *fplAtomicCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange) {
11969 fplAssert(dest != fpl_null);
11970#if defined(FPL_CPU_64BIT)
11971 void *result = (void *)fplAtomicCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
11972#elif defined(FPL_CPU_32BIT)
11973 void *result = (void *)fplAtomicCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
11974#else
11975# error "Unsupported architecture/platform!"
11976#endif // FPL_ARCH
11977 return (result);
11978}
11979
11980fpl_common_api bool fplAtomicIsCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange) {
11981 fplAssert(dest != fpl_null);
11982#if defined(FPL_CPU_64BIT)
11983 bool result = fplAtomicIsCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
11984#elif defined(FPL_CPU_32BIT)
11985 bool result = fplAtomicIsCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
11986#else
11987# error "Unsupported architecture/platform!"
11988#endif // FPL_ARCH
11989 return (result);
11990}
11991fpl_common_api bool fplAtomicIsCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange) {
11992 fplAssert(dest != fpl_null);
11993#if defined(FPL_CPU_64BIT)
11994 bool result = fplAtomicIsCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
11995#elif defined(FPL_CPU_32BIT)
11996 bool result = fplAtomicIsCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
11997#else
11998# error "Unsupported architecture/platform!"
11999#endif // FPL_ARCH
12000 return (result);
12001}
12002
12003fpl_common_api size_t fplAtomicLoadSize(volatile size_t *source) {
12004#if defined(FPL_CPU_64BIT)
12005 size_t result = (size_t)fplAtomicLoadU64((volatile uint64_t *)source);
12006#elif defined(FPL_CPU_32BIT)
12007 size_t result = (size_t)fplAtomicLoadU32((volatile uint32_t *)source);
12008#else
12009# error "Unsupported architecture/platform!"
12010#endif // FPL_ARCH
12011 return(result);
12012}
12013fpl_common_api void fplAtomicStoreSize(volatile size_t *dest, const size_t value) {
12014#if defined(FPL_CPU_64BIT)
12015 fplAtomicStoreU64((volatile uint64_t *)dest, (uint64_t)value);
12016#elif defined(FPL_CPU_32BIT)
12017 fplAtomicStoreU32((volatile uint32_t *)dest, (uint32_t)value);
12018#else
12019# error "Unsupported architecture/platform!"
12020#endif // FPL_ARCH
12021}
12022
12023fpl_common_api void *fplAtomicLoadPtr(volatile void **source) {
12024#if defined(FPL_CPU_64BIT)
12025 void *result = (void *)fplAtomicLoadU64((volatile uint64_t *)source);
12026#elif defined(FPL_CPU_32BIT)
12027 void *result = (void *)fplAtomicLoadU32((volatile uint32_t *)source);
12028#else
12029# error "Unsupported architecture/platform!"
12030#endif // FPL_ARCH
12031 return(result);
12032}
12033fpl_common_api void fplAtomicStorePtr(volatile void **dest, const void *value) {
12034#if defined(FPL_CPU_64BIT)
12035 fplAtomicStoreU64((volatile uint64_t *)dest, (uint64_t)value);
12036#elif defined(FPL_CPU_32BIT)
12037 fplAtomicStoreU32((volatile uint32_t *)dest, (uint32_t)value);
12038#else
12039# error "Unsupported architecture/platform!"
12040#endif // FPL_ARCH
12041}
12042
12043#endif // FPL__COMMON_ATOMICS_DEFINED
12044
12045//
12046// Common Threading
12047//
12049 if (thread == fpl_null) {
12051 }
12052 fplThreadState result = (fplThreadState)fplAtomicLoadU32((volatile uint32_t *)&thread->currentState);
12053 return(result);
12054}
12055
12057 const fplThreadHandle *result = &fpl__global__ThreadState.mainThread;
12058 return(result);
12059}
12060
12062 size_t result = 0;
12063 for (size_t threadIndex = 0; threadIndex < FPL_MAX_THREAD_COUNT; ++threadIndex) {
12064 fplThreadState state = (fplThreadState)fplAtomicLoadU32((volatile uint32_t *)&fpl__global__ThreadState.threads[threadIndex].currentState);
12065 if (state == fplThreadState_Stopped) {
12066 ++result;
12067 }
12068 }
12069 return(result);
12070}
12071
12073 size_t result = 0;
12074 for (size_t threadIndex = 0; threadIndex < FPL_MAX_THREAD_COUNT; ++threadIndex) {
12075 fplThreadState state = (fplThreadState)fplAtomicLoadU32((volatile uint32_t *)&fpl__global__ThreadState.threads[threadIndex].currentState);
12076 if (state != fplThreadState_Stopped) {
12077 ++result;
12078 }
12079 }
12080 return(result);
12081}
12082
12083//
12084// Common Files
12085//
12086#if !defined(FPL__COMMON_FILES_DEFINED)
12087#define FPL__COMMON_FILES_DEFINED
12088
12089fpl_common_api size_t fplFileReadBlock(const fplFileHandle *fileHandle, const size_t sizeToRead, void *targetBuffer, const size_t maxTargetBufferSize) {
12090#if defined(FPL_CPU_64BIT)
12091 return fplFileReadBlock64(fileHandle, sizeToRead, targetBuffer, maxTargetBufferSize);
12092#else
12093 return fplFileReadBlock32(fileHandle, (uint32_t)sizeToRead, targetBuffer, (uint32_t)maxTargetBufferSize);
12094#endif
12095}
12096
12097fpl_common_api size_t fplFileWriteBlock(const fplFileHandle *fileHandle, void *sourceBuffer, const size_t sourceSize) {
12098#if defined(FPL_CPU_64BIT)
12099 return fplFileWriteBlock64(fileHandle, sourceBuffer, sourceSize);
12100#else
12101 return fplFileWriteBlock32(fileHandle, sourceBuffer, (uint32_t)sourceSize);
12102#endif
12103}
12104
12105fpl_common_api size_t fplFileSetPosition(const fplFileHandle *fileHandle, const intptr_t position, const fplFilePositionMode mode) {
12106#if defined(FPL_CPU_64BIT)
12107 return fplFileSetPosition64(fileHandle, position, mode);
12108#else
12109 return fplFileSetPosition32(fileHandle, (int32_t)position, mode);
12110#endif
12111}
12112
12113fpl_common_api size_t fplFileGetPosition(const fplFileHandle *fileHandle) {
12114#if defined(FPL_CPU_64BIT)
12115 return fplFileGetPosition64(fileHandle);
12116#else
12117 return fplFileGetPosition32(fileHandle);
12118#endif
12119}
12120
12121fpl_common_api size_t fplFileGetSizeFromPath(const char *filePath) {
12122#if defined(FPL_CPU_64BIT)
12123 return fplFileGetSizeFromPath64(filePath);
12124#else
12125 return fplFileGetSizeFromPath32(filePath);
12126#endif
12127}
12128
12129fpl_common_api size_t fplFileGetSizeFromHandle(const fplFileHandle *fileHandle) {
12130#if defined(FPL_CPU_64BIT)
12131 return fplFileGetSizeFromHandle64(fileHandle);
12132#else
12133 return fplFileGetSizeFromHandle32(fileHandle);
12134#endif
12135}
12136
12137#endif // FPL__COMMON_FILES_DEFINED
12138
12139//
12140// Common Paths
12141//
12142#if !defined(FPL__COMMON_PATHS_DEFINED)
12143#define FPL__COMMON_PATHS_DEFINED
12144
12145fpl_common_api size_t fplExtractFilePath(const char *sourcePath, char *destPath, const size_t maxDestLen) {
12146 FPL__CheckArgumentNull(sourcePath, 0);
12147 size_t sourceLen = fplGetStringLength(sourcePath);
12148 size_t result = 0;
12149 if (sourceLen > 0) {
12150 int pathLen = 0;
12151 const char *chPtr = (const char *)sourcePath;
12152 while (*chPtr) {
12153 if (*chPtr == FPL_PATH_SEPARATOR) {
12154 pathLen = (int)(chPtr - sourcePath);
12155 }
12156 ++chPtr;
12157 }
12158 result = pathLen;
12159 if (destPath != fpl_null) {
12160 size_t requiredDestLen = pathLen + 1;
12161 FPL__CheckArgumentMin(maxDestLen, requiredDestLen, 0);
12162 fplCopyStringLen(sourcePath, pathLen, destPath, maxDestLen);
12163 }
12164 }
12165 return(result);
12166}
12167
12168fpl_common_api const char *fplExtractFileExtension(const char *sourcePath) {
12169 const char *result = fpl_null;
12170 if (sourcePath != fpl_null) {
12171 const char *chPtr = sourcePath;
12172 // Find last separator first
12173 const char *lastPathSep = fpl_null;
12174 while (*chPtr) {
12175 if (*chPtr == FPL_PATH_SEPARATOR) {
12176 lastPathSep = chPtr;
12177 }
12178 ++chPtr;
12179 }
12180 // Start either by the last found separator or from the very start
12181 if (lastPathSep != fpl_null) {
12182 chPtr = lastPathSep;
12183 } else {
12184 chPtr = sourcePath;
12185 }
12186 const char *lastExt = fpl_null;
12187 while (*chPtr) {
12188 if (*chPtr == FPL_FILE_EXT_SEPARATOR) {
12189 lastExt = chPtr;
12190 }
12191 ++chPtr;
12192 }
12193 if (lastExt != fpl_null) {
12194 result = lastExt;
12195 }
12196 }
12197 return(result);
12198}
12199
12200fpl_common_api const char *fplExtractFileName(const char *sourcePath) {
12201 const char *result = fpl_null;
12202 if (sourcePath) {
12203 result = sourcePath;
12204 const char *chPtr = sourcePath;
12205 while (*chPtr) {
12206 if (*chPtr == FPL_PATH_SEPARATOR) {
12207 result = chPtr + 1;
12208 }
12209 ++chPtr;
12210 }
12211 }
12212 return(result);
12213}
12214
12215fpl_common_api size_t fplChangeFileExtension(const char *filePath, const char *newFileExtension, char *destPath, const size_t maxDestLen) {
12216 FPL__CheckArgumentNull(filePath, 0);
12217 FPL__CheckArgumentNull(newFileExtension, 0);
12218
12219 size_t filePathLen = fplGetStringLength(filePath);
12220 FPL__CheckArgumentZero(filePathLen, 0);
12221
12222 size_t extLen = fplGetStringLength(newFileExtension);
12223
12224 size_t result = 0;
12225 if (filePath != fpl_null) {
12226 // Find last path
12227 const char *chPtr = (const char *)filePath;
12228 const char *lastPathSeparatorPtr = fpl_null;
12229 while (*chPtr) {
12230 if (*chPtr == FPL_PATH_SEPARATOR) {
12231 lastPathSeparatorPtr = chPtr;
12232 }
12233 ++chPtr;
12234 }
12235 // Find last ext separator
12236 if (lastPathSeparatorPtr != fpl_null) {
12237 chPtr = lastPathSeparatorPtr + 1;
12238 } else {
12239 chPtr = (const char *)filePath;
12240 }
12241 const char *lastExtSeparatorPtr = fpl_null;
12242 while (*chPtr) {
12243 if (*chPtr == FPL_FILE_EXT_SEPARATOR) {
12244 lastExtSeparatorPtr = chPtr;
12245 }
12246 ++chPtr;
12247 }
12248
12249 size_t filenameLen;
12250 if (lastExtSeparatorPtr != fpl_null) {
12251 filenameLen = (size_t)((uintptr_t)lastExtSeparatorPtr - (uintptr_t)filePath);
12252 } else {
12253 filenameLen = filePathLen;
12254 }
12255
12256 result = filenameLen + extLen;
12257
12258 // Copy parts
12259 if (destPath != fpl_null) {
12260 size_t requiredDestLen = result + 1;
12261 FPL__CheckArgumentMin(maxDestLen, requiredDestLen, 0);
12262
12263 fplCopyStringLen(filePath, filenameLen, destPath, maxDestLen);
12264 char *destExtPtr = destPath + filenameLen;
12265 fplCopyStringLen(newFileExtension, extLen, destExtPtr, maxDestLen - filenameLen);
12266 }
12267 }
12268 return(result);
12269}
12270
12271fpl_common_api size_t fplPathCombine(char *destPath, const size_t maxDestPathLen, const size_t pathCount, ...) {
12272 FPL__CheckArgumentZero(pathCount, 0);
12273
12274 size_t result = 0;
12275
12276 size_t curDestPosition = 0;
12277 char *currentDestPtr = destPath;
12278
12279 va_list vargs;
12280 va_start(vargs, pathCount);
12281 for (size_t pathIndex = 0; pathIndex < pathCount; ++pathIndex) {
12282 const char *path = va_arg(vargs, const char *);
12283 size_t pathLen = fplGetStringLength(path);
12284
12285 bool requireSeparator = pathIndex < (pathCount - 1);
12286 size_t requiredPathLen = requireSeparator ? pathLen + 1 : pathLen;
12287
12288 result += requiredPathLen;
12289
12290 if (destPath != fpl_null) {
12291 size_t requiredDestLen = result + 1;
12292 FPL__CheckArgumentMin(maxDestPathLen, requiredDestLen, 0);
12293
12294 fplCopyStringLen(path, pathLen, currentDestPtr, maxDestPathLen - curDestPosition);
12295 currentDestPtr += pathLen;
12296 if (requireSeparator) {
12297 *currentDestPtr++ = FPL_PATH_SEPARATOR;
12298 }
12299 curDestPosition += requiredPathLen;
12300 }
12301 }
12302 if (currentDestPtr != fpl_null) {
12303 *currentDestPtr = 0;
12304 }
12305 va_end(vargs);
12306 return(result);
12307}
12308#endif // FPL__COMMON_PATHS_DEFINED
12309
12310//
12311// Common Window
12312//
12313#if !defined(FPL__COMMON_WINDOW_DEFINED)
12314#define FPL__COMMON_WINDOW_DEFINED
12315fpl_common_api char *fplGetWindowTitle(char *outTitle, const size_t maxOutTitleLength) {
12316 FPL__CheckPlatform(fpl_null);
12317 fpl__PlatformAppState *appState = fpl__global__AppState;
12318 char *result = fplCopyString(appState->currentSettings.window.title, outTitle, maxOutTitleLength);
12319 return(result);
12320}
12321
12322fpl_common_api void fplSetWindowInputEvents(const bool enabled) {
12323 FPL__CheckPlatformNoRet();
12324 fpl__PlatformAppState *appState = fpl__global__AppState;
12325 appState->currentSettings.input.disabledEvents = !enabled;
12326}
12327#endif // FPL__COMMON_WINDOW_DEFINED
12328
12329//
12330// Common Logging
12331//
12332#if defined(FPL__ENABLE_LOGGING)
12334 FPL__CheckArgumentNullNoRet(params);
12335 fpl__global__LogSettings = *params;
12336 fpl__global__LogSettings.isInitialized = true;
12337}
12339 return &fpl__global__LogSettings;
12340}
12341fpl_common_api void fplSetMaxLogLevel(const fplLogLevel maxLevel) {
12342 fpl__global__LogSettings.maxLevel = maxLevel;
12343}
12345 return fpl__global__LogSettings.maxLevel;
12346}
12347#endif
12348
12349fpl_common_api const char *fplGetLastError(void) {
12350 const char *result = "";
12351 const fpl__ErrorState *errorState = &fpl__global__LastErrorState;
12352 if (errorState->count > 0) {
12353 size_t index = errorState->count - 1;
12354 result = fplGetErrorByIndex(index);
12355 }
12356 return (result);
12357}
12358
12359fpl_common_api const char *fplGetErrorByIndex(const size_t index) {
12360 const char *result = "";
12361 const fpl__ErrorState *errorState = &fpl__global__LastErrorState;
12362 if (index < errorState->count) {
12363 result = errorState->errors[index];
12364 } else {
12365 result = errorState->errors[errorState->count - 1];
12366 }
12367 return (result);
12368}
12369
12370fpl_common_api size_t fplGetErrorCount(void) {
12371 size_t result = 0;
12372 const fpl__ErrorState *errorState = &fpl__global__LastErrorState;
12373 result = errorState->count;
12374 return (result);
12375}
12376
12377fpl_common_api void fplErrorsClear(void) {
12378 fpl__ErrorState *errorState = &fpl__global__LastErrorState;
12379 fplClearStruct(errorState);
12380}
12381
12383 FPL__CheckPlatform(fpl_null);
12384 const fpl__PlatformAppState *appState = fpl__global__AppState;
12385 return &appState->currentSettings;
12386}
12387
12389 FPL__CheckArgumentNullNoRet(video);
12390 fplClearStruct(video);
12391 video->isVSync = false;
12392 video->isAutoSize = true;
12393
12394#if defined(FPL__ENABLE_VIDEO_OPENGL)
12397#endif
12398
12399#if defined(FPL__ENABLE_VIDEO_VULKAN)
12401 video->graphics.vulkan.appVersion = fplStructInit(fplVersionInfo, "1.0.0", "1", "0", "0", "0");
12402 video->graphics.vulkan.engineVersion = fplStructInit(fplVersionInfo, "1.0.0", "1", "0", "0", "0");
12403 video->graphics.vulkan.apiVersion = fplStructInit(fplVersionInfo, "1.1.0", "1", "1", "0", "0");
12405#endif
12406
12407 // @NOTE(final): Auto detect video backend
12408#if defined(FPL__ENABLE_VIDEO_OPENGL)
12410#elif defined(FPL__ENABLE_VIDEO_SOFTWARE)
12412#elif defined(FPL__ENABLE_VIDEO_VULKAN)
12414#else
12416#endif
12417}
12418
12419
12420#define FPL__AUDIO_CHANNEL_TYPE_COUNT FPL__ENUM_COUNT(fplAudioChannelType_First, fplAudioChannelType_Last)
12423fpl_globalvar const char *fpl__global_audioChannelTypeNameTable[] = {
12424 FPL__ENUM_NAME("None", fplAudioChannelType_None),
12425 FPL__ENUM_NAME("Front Left", fplAudioChannelType_FrontLeft),
12426 FPL__ENUM_NAME("Front Right", fplAudioChannelType_FrontRight),
12427 FPL__ENUM_NAME("Front Center", fplAudioChannelType_FrontCenter),
12428 FPL__ENUM_NAME("Low Frequency", fplAudioChannelType_LowFrequency),
12429 FPL__ENUM_NAME("Back Left", fplAudioChannelType_BackLeft),
12430 FPL__ENUM_NAME("Back Right", fplAudioChannelType_BackRight),
12431 FPL__ENUM_NAME("Front Left Of Center", fplAudioChannelType_FrontLeftOfCenter),
12432 FPL__ENUM_NAME("Front Right Of Center", fplAudioChannelType_FrontRightOfCenter),
12433 FPL__ENUM_NAME("Back Center", fplAudioChannelType_BackCenter),
12434 FPL__ENUM_NAME("Side Left", fplAudioChannelType_SideLeft),
12435 FPL__ENUM_NAME("Side Right", fplAudioChannelType_SideRight),
12436 FPL__ENUM_NAME("Top Center", fplAudioChannelType_TopCenter),
12437 FPL__ENUM_NAME("Top Front Left", fplAudioChannelType_TopFrontLeft),
12438 FPL__ENUM_NAME("Top Front Center", fplAudioChannelType_TopFrontCenter),
12439 FPL__ENUM_NAME("Top Front Right", fplAudioChannelType_TopFrontRight),
12440 FPL__ENUM_NAME("Top Back Left", fplAudioChannelType_TopBackLeft),
12441 FPL__ENUM_NAME("Top Back Center", fplAudioChannelType_TopBackCenter),
12442 FPL__ENUM_NAME("Top Back Right", fplAudioChannelType_TopBackRight),
12443 FPL__ENUM_NAME("AUX 0", fplAudioChannelType_AUX0),
12444 FPL__ENUM_NAME("AUX 1", fplAudioChannelType_AUX1),
12445 FPL__ENUM_NAME("AUX 2", fplAudioChannelType_AUX2),
12446 FPL__ENUM_NAME("AUX 3", fplAudioChannelType_AUX3),
12447 FPL__ENUM_NAME("AUX 4", fplAudioChannelType_AUX4),
12448 FPL__ENUM_NAME("AUX 5", fplAudioChannelType_AUX5),
12449 FPL__ENUM_NAME("AUX 6", fplAudioChannelType_AUX6),
12450 FPL__ENUM_NAME("AUX 7", fplAudioChannelType_AUX7),
12451 FPL__ENUM_NAME("AUX 8", fplAudioChannelType_AUX8),
12452 FPL__ENUM_NAME("AUX 9", fplAudioChannelType_AUX9),
12453 FPL__ENUM_NAME("AUX 10", fplAudioChannelType_AUX10),
12454 FPL__ENUM_NAME("AUX 11", fplAudioChannelType_AUX11),
12455 FPL__ENUM_NAME("AUX 12", fplAudioChannelType_AUX12),
12456 FPL__ENUM_NAME("AUX 13", fplAudioChannelType_AUX13),
12457 FPL__ENUM_NAME("AUX 14", fplAudioChannelType_AUX14),
12458 FPL__ENUM_NAME("AUX 15", fplAudioChannelType_AUX15),
12459};
12460fplStaticAssert(fplArrayCount(fpl__global_audioChannelTypeNameTable) == FPL__AUDIO_CHANNEL_TYPE_COUNT);
12461
12463 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplAudioChannelType_First, fplAudioChannelType_Last);
12464 const char *result = fpl__global_audioChannelTypeNameTable[index];
12465 return(result);
12466}
12467
12469 FPL__CheckArgumentNullNoRet(audio);
12470 fplClearStruct(audio);
12471 audio->startAuto = true;
12472 audio->stopAuto = true;
12473 audio->manualLoad = false;
12474}
12475
12477 FPL__CheckArgumentNullNoRet(window);
12478 fplClearStruct(window);
12479 window->title[0] = 0;
12480 window->windowSize.width = 0;
12481 window->windowSize.height = 0;
12482 window->fullscreenSize.width = 0;
12483 window->fullscreenSize.height = 0;
12484 window->isFullscreen = false;
12485 window->isResizable = true;
12486 window->isDecorated = true;
12487 window->isFloating = false;
12488 window->isScreenSaverPrevented = false;
12489 window->isMonitorPowerPrevented = false;
12490}
12491
12493 FPL__CheckArgumentNullNoRet(console);
12494 fplClearStruct(console);
12495 console->title[0] = 0;
12496}
12497
12499 FPL__CheckArgumentNullNoRet(input);
12500 fplClearStruct(input);
12501 input->controllerDetectionFrequency = 1000;
12502}
12503
12505 FPL__CheckArgumentNullNoRet(settings);
12506 fplClearStruct(settings);
12511}
12512
12514 fplSettings result;
12515 fplSetDefaultSettings(&result);
12516 return(result);
12517}
12518
12519#define FPL__PLATFORMRESULTTYPE_COUNT FPL__ENUM_COUNT(fplPlatformResultType_First, fplPlatformResultType_Last)
12522fpl_globalvar const char *fpl__global_platformResultTypeNameTable[] = {
12523 FPL__ENUM_NAME("Failed Window", fplPlatformResultType_FailedWindow),
12524 FPL__ENUM_NAME("Failed Audio", fplPlatformResultType_FailedAudio),
12525 FPL__ENUM_NAME("Failed Video", fplPlatformResultType_FailedVideo),
12526 FPL__ENUM_NAME("Failed Platform", fplPlatformResultType_FailedPlatform),
12527 FPL__ENUM_NAME("Out of Memory", fplPlatformResultType_OutOfMemory),
12528 FPL__ENUM_NAME("Already Initialized", fplPlatformResultType_AlreadyInitialized),
12529 FPL__ENUM_NAME("Not Initialized", fplPlatformResultType_NotInitialized),
12530 FPL__ENUM_NAME("Success", fplPlatformResultType_Success),
12531};
12532fplStaticAssert(fplArrayCount(fpl__global_platformResultTypeNameTable) == FPL__PLATFORMRESULTTYPE_COUNT);
12533
12535 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplPlatformResultType_First, fplPlatformResultType_Last);
12536 const char *result = fpl__global_platformResultTypeNameTable[index];
12537 return(result);
12538}
12539
12540#define FPL__ARCHTYPE_COUNT FPL__ENUM_COUNT(fplCPUArchType_First, fplCPUArchType_Last)
12543fpl_globalvar const char *fpl__global_ArchTypeNameTable[] = {
12544 FPL__ENUM_NAME("Unknown", fplCPUArchType_Unknown),
12545 FPL__ENUM_NAME("x86", fplCPUArchType_x86),
12546 FPL__ENUM_NAME("x86_64", fplCPUArchType_x86_64),
12547 FPL__ENUM_NAME("x64", fplCPUArchType_x64),
12548 FPL__ENUM_NAME("arm32", fplCPUArchType_Arm32),
12549 FPL__ENUM_NAME("arm64", fplCPUArchType_Arm64),
12550};
12551fplStaticAssert(fplArrayCount(fpl__global_ArchTypeNameTable) == FPL__ARCHTYPE_COUNT);
12552
12553fpl_common_api const char *fplCPUGetArchName(const fplCPUArchType type) {
12554 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplCPUArchType_First, fplCPUArchType_Last);
12555 const char *result = fpl__global_ArchTypeNameTable[index];
12556 return(result);
12557}
12558
12559#define FPL__CPU_CAPABILITIESTYPE_COUNT FPL__ENUM_COUNT(fplCPUCapabilitiesType_First, fplCPUCapabilitiesType_Last)
12562fpl_globalvar const char *fpl__global_CPUCapabilitesTypeNameTable[] = {
12563 FPL__ENUM_NAME("Unknown", fplCPUCapabilitiesType_Unknown),
12564 FPL__ENUM_NAME("X86", fplCPUCapabilitiesType_X86),
12565 FPL__ENUM_NAME("ARM", fplCPUCapabilitiesType_ARM),
12566};
12567fplStaticAssert(fplArrayCount(fpl__global_CPUCapabilitesTypeNameTable) == FPL__CPU_CAPABILITIESTYPE_COUNT);
12568
12570 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplCPUCapabilitiesType_First, fplCPUCapabilitiesType_Last);
12571 const char *result = fpl__global_CPUCapabilitesTypeNameTable[index];
12572 return(result);
12573}
12574
12575//
12576// Debug out
12577//
12578#if defined(FPL_PLATFORM_WINDOWS)
12579fpl_platform_api void fplDebugOut(const char *text) {
12580 wchar_t buffer[FPL_MAX_BUFFER_LENGTH];
12581 fplUTF8StringToWideString(text, fplGetStringLength(text), buffer, fplArrayCount(buffer));
12582 OutputDebugStringW(buffer);
12583}
12584#else
12585fpl_platform_api void fplDebugOut(const char *text) {
12586 fplConsoleOut(text);
12587}
12588#endif
12589
12590fpl_common_api void fplDebugFormatOut(const char *format, ...) {
12591 if (format != fpl_null) {
12592 char buffer[FPL_MAX_BUFFER_LENGTH];
12593 va_list argList;
12594 va_start(argList, format);
12595 fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
12596 va_end(argList);
12597 fplDebugOut(buffer);
12598 }
12599}
12600
12601//
12602// Color
12603//
12604fpl_common_api fplColor32 fplCreateColorRGBA(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) {
12605 fplColor32 result = fplStructInit(fplColor32, { b, g, r, a });
12606 return(result);
12607}
12608
12609#endif // FPL__COMMON_DEFINED
12610
12611// ############################################################################
12612//
12613// > WIN32_PLATFORM (Win32, Win64)
12614//
12615// ############################################################################
12616#if defined(FPL_PLATFORM_WINDOWS)
12617
12618# if defined(FPL_ARCH_X86)
12619# define FPL_MEMORY_BARRIER() \
12620 LONG barrier; \
12621 _InterlockedOr(&barrier, 0);
12622# elif defined(FPL_ARCH_X64)
12623 // @NOTE(final): No need for hardware memory fence on X64 because the hardware guarantees memory order always.
12624# define FPL_MEMORY_BARRIER()
12625# endif
12626
12627#if defined(FPL__ENABLE_WINDOW)
12628
12629fpl_internal DWORD fpl__Win32MakeWindowStyle(const fplWindowSettings *settings) {
12630 DWORD result = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
12631 if (settings->isFullscreen || !settings->isDecorated) {
12632 result |= WS_POPUP;
12633 } else {
12634 result |= WS_OVERLAPPEDWINDOW;
12635 if (!settings->isResizable) {
12636 result &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
12637 }
12638 }
12639 return(result);
12640}
12641
12642fpl_internal DWORD fpl__Win32MakeWindowExStyle(const fplWindowSettings *settings) {
12643 DWORD result = WS_EX_APPWINDOW;
12644 if (settings->isFullscreen || settings->isFloating) {
12645 result |= WS_EX_TOPMOST;
12646 }
12647 return(result);
12648}
12649
12650fpl_internal void fpl__Win32SaveWindowState(const fpl__Win32Api *wapi, fpl__Win32LastWindowInfo *target, HWND windowHandle) {
12651 target->isMaximized = !!wapi->user.IsZoomed(windowHandle);
12652 target->isMinimized = !!wapi->user.IsIconic(windowHandle);
12653 target->style = fpl__win32_GetWindowLong(windowHandle, GWL_STYLE);
12654 target->exStyle = fpl__win32_GetWindowLong(windowHandle, GWL_EXSTYLE);
12655 wapi->user.GetWindowPlacement(windowHandle, &target->placement);
12656}
12657
12658fpl_internal void fpl__Win32RestoreWindowState(const fpl__Win32Api *wapi, const fpl__Win32LastWindowInfo *target, HWND windowHandle) {
12659 fplAssert(target->style > 0 && target->exStyle > 0);
12660 fpl__win32_SetWindowLong(windowHandle, GWL_STYLE, target->style);
12661 fpl__win32_SetWindowLong(windowHandle, GWL_EXSTYLE, target->exStyle);
12662 wapi->user.SetWindowPlacement(windowHandle, &target->placement);
12663 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
12664 if (target->isMaximized) {
12665 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
12666 } else if (target->isMinimized) {
12667 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
12668 }
12669}
12670
12671fpl_internal bool fpl__Win32LeaveFullscreen(void) {
12672 const fpl__PlatformAppState *platState = fpl__global__AppState;
12673 fplAssert(platState != fpl_null);
12674 const fpl__Win32AppState *win32State = &platState->win32;
12675 const fpl__Win32Api *wapi = &win32State->winApi;
12676 const fplWindowSettings *settings = &platState->currentSettings.window;
12677 const fpl__Win32WindowState *win32Window = &platState->window.win32;
12678 const fpl__Win32LastWindowInfo *fullscreenInfo = &win32Window->lastFullscreenInfo;
12679 HWND windowHandle = win32Window->windowHandle;
12680 fpl__Win32RestoreWindowState(wapi, fullscreenInfo, windowHandle);
12681 bool result;
12682 if (fullscreenInfo->wasResolutionChanged) {
12683 result = (wapi->user.ChangeDisplaySettingsW(fpl_null, CDS_RESET) == DISP_CHANGE_SUCCESSFUL);
12684 } else {
12685 result = true;
12686 }
12687 return(result);
12688}
12689
12690fpl_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) {
12691 fpl__PlatformAppState *platState = fpl__global__AppState;
12692 fplAssert(platState != fpl_null);
12693 fpl__Win32AppState *win32State = &platState->win32;
12694 const fpl__Win32Api *wapi = &win32State->winApi;
12695 const fplWindowSettings *settings = &platState->currentSettings.window;
12696 fpl__Win32WindowState *win32Window = &platState->window.win32;
12697 fpl__Win32LastWindowInfo *fullscreenInfo = &win32Window->lastFullscreenInfo;
12698
12699 HWND windowHandle = win32Window->windowHandle;
12700 HDC deviceContext = win32Window->deviceContext;
12701
12702 fplAssert(fullscreenInfo->style > 0 && fullscreenInfo->exStyle > 0);
12703 fpl__win32_SetWindowLong(windowHandle, GWL_STYLE, fullscreenInfo->style & ~(WS_CAPTION | WS_THICKFRAME));
12704 fpl__win32_SetWindowLong(windowHandle, GWL_EXSTYLE, fullscreenInfo->exStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
12705
12706 MONITORINFO monitor = fplZeroInit;
12707 monitor.cbSize = sizeof(monitor);
12708 wapi->user.GetMonitorInfoW(wapi->user.MonitorFromWindow(windowHandle, MONITOR_DEFAULTTONEAREST), &monitor);
12709
12710 bool result;
12711 if (allowResolutionChange && (fullscreenWidth > 0) && (fullscreenHeight > 0)) {
12712 int32_t useFullscreenWidth = fullscreenWidth;
12713 int32_t useFullscreenHeight = fullscreenHeight;
12714
12715 DWORD useRefreshRate = refreshRate;
12716 if (!useRefreshRate) {
12717 useRefreshRate = wapi->gdi.GetDeviceCaps(deviceContext, VREFRESH);
12718 }
12719
12720 DWORD useColourBits = colorBits;
12721 if (!useColourBits) {
12722 useColourBits = wapi->gdi.GetDeviceCaps(deviceContext, BITSPIXEL);
12723 }
12724
12725 RECT windowRect;
12726 // @TODO(final/Win32): This may not be correct to assume 0, 0 as origin for the current display
12727 windowRect.left = 0;
12728 windowRect.top = 0;
12729 windowRect.right = windowRect.left + useFullscreenWidth;
12730 windowRect.bottom = windowRect.top + useFullscreenHeight;
12731
12732 WINDOWPLACEMENT placement = fplZeroInit;
12733 placement.length = sizeof(placement);
12734 placement.rcNormalPosition = windowRect;
12735 placement.showCmd = SW_SHOW;
12736 wapi->user.SetWindowPlacement(windowHandle, &placement);
12737 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
12738
12739 DEVMODEW fullscreenSettings = fplZeroInit;
12740 wapi->user.EnumDisplaySettingsW(fpl_null, 0, &fullscreenSettings);
12741 fullscreenSettings.dmPelsWidth = useFullscreenWidth;
12742 fullscreenSettings.dmPelsHeight = useFullscreenHeight;
12743 fullscreenSettings.dmBitsPerPel = useColourBits;
12744 fullscreenSettings.dmDisplayFrequency = useRefreshRate;
12745 fullscreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
12746 result = (wapi->user.ChangeDisplaySettingsW(&fullscreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL);
12747 fullscreenInfo->wasResolutionChanged = true;
12748 } else {
12749 RECT windowRect = fplZeroInit;
12750 if ((xpos != INT32_MAX) && (ypos != INT32_MAX) && (fullscreenWidth != 0) && (fullscreenHeight != 0)) {
12751 windowRect.left = xpos;
12752 windowRect.top = ypos;
12753 windowRect.right = xpos + fullscreenWidth;
12754 windowRect.top = ypos + fullscreenHeight;
12755 } else {
12756 windowRect = monitor.rcMonitor;
12757 }
12758 WINDOWPLACEMENT placement = fplZeroInit;
12759 placement.length = sizeof(placement);
12760 placement.rcNormalPosition = windowRect;
12761 placement.showCmd = SW_SHOWNORMAL;
12762 wapi->user.SetWindowPlacement(windowHandle, &placement);
12763 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
12764 result = true;
12765 fullscreenInfo->wasResolutionChanged = false;
12766 }
12767
12768 return(result);
12769}
12770
12771fpl_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) {
12772 FPL__CheckPlatform(false);
12773 fpl__PlatformAppState *appState = fpl__global__AppState;
12774 fpl__Win32AppState *win32AppState = &appState->win32;
12775 fpl__Win32WindowState *windowState = &appState->window.win32;
12776 fplWindowSettings *windowSettings = &appState->currentSettings.window;
12777 fpl__Win32LastWindowInfo *fullscreenState = &windowState->lastFullscreenInfo;
12778 const fpl__Win32Api *wapi = &win32AppState->winApi;
12779
12780 HWND windowHandle = windowState->windowHandle;
12781
12782 // Save current window info if not already fullscreen
12783 if (!windowSettings->isFullscreen) {
12784 fpl__Win32SaveWindowState(wapi, fullscreenState, windowHandle);
12785 if (fullscreenState->isMaximized || fullscreenState->isMinimized) {
12786 wapi->user.ShowWindow(windowHandle, SW_RESTORE);
12787 }
12788 }
12789
12790 if (value) {
12791 // Enter fullscreen mode or fallback to window mode
12792 windowSettings->isFullscreen = fpl__Win32EnterFullscreen(x, y, w, h, refreshRate, 0, allowResolutionChange);
12793 if (!windowSettings->isFullscreen) {
12794 fpl__Win32LeaveFullscreen();
12795 }
12796 } else {
12797 fpl__Win32LeaveFullscreen();
12798 windowSettings->isFullscreen = false;
12799 }
12800 bool result = windowSettings->isFullscreen != 0;
12801 return(result);
12802}
12803
12804fpl_internal float fpl__Win32XInputProcessStickValue(const SHORT value, const SHORT deadZoneThreshold) {
12805 float result = 0;
12806 if (value < -deadZoneThreshold) {
12807 result = (float)((value + deadZoneThreshold) / (32768.0f - deadZoneThreshold));
12808 } else if (value > deadZoneThreshold) {
12809 result = (float)((value - deadZoneThreshold) / (32767.0f - deadZoneThreshold));
12810 }
12811 return(result);
12812}
12813
12814fpl_internal void fpl__Win32XInputGamepadToGamepadState(const XINPUT_GAMEPAD *newState, fplGamepadState *outState) {
12815 // If we got here, the controller is definitily by connected
12816 outState->isConnected = true;
12817
12818 // Analog sticks
12819 outState->leftStickX = fpl__Win32XInputProcessStickValue(newState->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
12820 outState->leftStickY = fpl__Win32XInputProcessStickValue(newState->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
12821 outState->rightStickX = fpl__Win32XInputProcessStickValue(newState->sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
12822 outState->rightStickY = fpl__Win32XInputProcessStickValue(newState->sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
12823
12824 // Triggers
12825 outState->leftTrigger = (float)newState->bLeftTrigger / 255.0f;
12826 outState->rightTrigger = (float)newState->bRightTrigger / 255.0f;
12827
12828 // Digital pad buttons
12829 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_UP)
12830 outState->dpadUp.isDown = true;
12831 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
12832 outState->dpadDown.isDown = true;
12833 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
12834 outState->dpadLeft.isDown = true;
12835 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
12836 outState->dpadRight.isDown = true;
12837
12838 // Action buttons
12839 if (newState->wButtons & XINPUT_GAMEPAD_A)
12840 outState->actionA.isDown = true;
12841 if (newState->wButtons & XINPUT_GAMEPAD_B)
12842 outState->actionB.isDown = true;
12843 if (newState->wButtons & XINPUT_GAMEPAD_X)
12844 outState->actionX.isDown = true;
12845 if (newState->wButtons & XINPUT_GAMEPAD_Y)
12846 outState->actionY.isDown = true;
12847
12848 // Center buttons
12849 if (newState->wButtons & XINPUT_GAMEPAD_START)
12850 outState->start.isDown = true;
12851 if (newState->wButtons & XINPUT_GAMEPAD_BACK)
12852 outState->back.isDown = true;
12853
12854 // Shoulder buttons
12855 if (newState->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER)
12856 outState->leftShoulder.isDown = true;
12857 if (newState->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
12858 outState->rightShoulder.isDown = true;
12859
12860 // Thumb buttons
12861 if (newState->wButtons & XINPUT_GAMEPAD_LEFT_THUMB)
12862 outState->leftThumb.isDown = true;
12863 if (newState->wButtons & XINPUT_GAMEPAD_RIGHT_THUMB)
12864 outState->rightThumb.isDown = true;
12865
12866 // The controller is only active, when any button or any movement happened
12867 outState->isActive = !fpl__IsZeroMemory(newState, sizeof(*newState));
12868}
12869
12870fpl_internal void fpl__Win32UpdateGameControllers(const fplSettings *settings, const fpl__Win32InitState *initState, fpl__Win32XInputState *xinputState) {
12871 fplAssert(settings != fpl_null);
12872 fplAssert(xinputState != fpl_null);
12873 if (xinputState->xinputApi.XInputGetState != fpl_null) {
12874 //
12875 // Detect new controller (Only at a fixed frequency)
12876 //
12877 if (xinputState->lastDeviceSearchTime.QuadPart == 0) {
12878 QueryPerformanceCounter(&xinputState->lastDeviceSearchTime);
12879 }
12880 LARGE_INTEGER currentDeviceSearchTime, currentDeviceSearchFreq;
12881 QueryPerformanceCounter(&currentDeviceSearchTime);
12882 QueryPerformanceFrequency(&currentDeviceSearchFreq);
12883 uint64_t deviceSearchDifferenceTimeInMs = ((currentDeviceSearchTime.QuadPart - xinputState->lastDeviceSearchTime.QuadPart) / (currentDeviceSearchFreq.QuadPart / 1000));
12884 if ((settings->input.controllerDetectionFrequency == 0) || (deviceSearchDifferenceTimeInMs > settings->input.controllerDetectionFrequency)) {
12885 xinputState->lastDeviceSearchTime = currentDeviceSearchTime;
12886 for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; ++controllerIndex) {
12887 XINPUT_STATE controllerState = fplZeroInit;
12888 if (xinputState->xinputApi.XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) {
12889 if (!xinputState->isConnected[controllerIndex]) {
12890 // Connected
12891 xinputState->isConnected[controllerIndex] = true;
12892 fplStringFormat(xinputState->deviceNames[controllerIndex], fplArrayCount(xinputState->deviceNames[controllerIndex]), "XInput-Device [%d]", controllerIndex);
12893
12894 fplEvent ev = fplZeroInit;
12897 ev.gamepad.deviceIndex = controllerIndex;
12898 ev.gamepad.deviceName = xinputState->deviceNames[controllerIndex];
12899 fpl__PushInternalEvent(&ev);
12900 }
12901 } else {
12902 if (xinputState->isConnected[controllerIndex]) {
12903 // Disconnected
12904 xinputState->isConnected[controllerIndex] = false;
12905
12906 fplEvent ev = fplZeroInit;
12909 ev.gamepad.deviceIndex = controllerIndex;
12910 ev.gamepad.deviceName = xinputState->deviceNames[controllerIndex];
12911 fpl__PushInternalEvent(&ev);
12912 }
12913 }
12914 }
12915 }
12916
12917 //
12918 // Update controller state when connected only
12919 //
12920 for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; ++controllerIndex) {
12921 if (xinputState->isConnected[controllerIndex]) {
12922 XINPUT_STATE controllerState = fplZeroInit;
12923 if (xinputState->xinputApi.XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) {
12924 // State changed
12925 fplEvent ev = fplZeroInit;
12928 ev.gamepad.deviceIndex = controllerIndex;
12929 ev.gamepad.deviceName = xinputState->deviceNames[controllerIndex];
12930 const XINPUT_GAMEPAD *newPadState = &controllerState.Gamepad;
12931 fpl__Win32XInputGamepadToGamepadState(newPadState, &ev.gamepad.state);
12933 fpl__PushInternalEvent(&ev);
12934 }
12935 }
12936 }
12937 }
12938}
12939
12940fpl_internal bool fpl__Win32IsKeyDown(const fpl__Win32Api *wapi, const int virtualKey) {
12941 bool result = (wapi->user.GetAsyncKeyState(virtualKey) & 0x8000) != 0;
12942 return(result);
12943}
12944
12945fpl_internal bool fpl__Win32IsKeyActive(const fpl__Win32Api *wapi, const int virtualKey) {
12946 bool result = (wapi->user.GetKeyState(virtualKey) & 0x0001) != 0;
12947 return(result);
12948}
12949
12950fpl_internal bool fpl__Win32IsCursorInWindow(const fpl__Win32Api *wapi, const fpl__Win32WindowState *win32Window) {
12951 POINT pos;
12952 if (!wapi->user.GetCursorPos(&pos)) {
12953 return false;
12954 }
12955 // Not this window?
12956 if (wapi->user.WindowFromPoint(pos) != win32Window->windowHandle) {
12957 return false;
12958 }
12959 // Cursor in client rect?
12960 RECT area;
12961 wapi->user.GetClientRect(win32Window->windowHandle, &area);
12962
12963 wapi->user.ClientToScreen(win32Window->windowHandle, (LPPOINT)&area.left);
12964 wapi->user.ClientToScreen(win32Window->windowHandle, (LPPOINT)&area.right);
12965 bool result = wapi->user.PtInRect(&area, pos) == TRUE;
12966
12967 return(result);
12968}
12969
12970fpl_internal void fpl__Win32LoadCursor(const fpl__Win32Api *wapi, const fpl__Win32WindowState *window) {
12971 if (window->isCursorActive) {
12972 wapi->user.SetCursor(fpl__win32_LoadCursor(fpl_null, IDC_ARROW));
12973 } else {
12974 wapi->user.SetCursor(fpl_null);
12975 }
12976}
12977
12978fpl_internal void fpl__Win32UpdateClipRect(const fpl__Win32Api *wapi, const fpl__Win32WindowState *window) {
12979 if (window != fpl_null) {
12980 RECT clipRect;
12981 wapi->user.GetClientRect(window->windowHandle, &clipRect);
12982 wapi->user.ClientToScreen(window->windowHandle, (POINT *)&clipRect.left);
12983 wapi->user.ClientToScreen(window->windowHandle, (POINT *)&clipRect.right);
12984 wapi->user.ClipCursor(&clipRect);
12985 } else {
12986 wapi->user.ClipCursor(fpl_null);
12987 }
12988}
12989
12990fpl_internal void fpl__Win32SetCursorState(const fpl__Win32Api *wapi, fpl__Win32WindowState *window, const bool state) {
12991 // @NOTE(final): We use RAWINPUT to remove the mouse device entirely when it needs to be hidden
12992 if (!state) {
12993 const RAWINPUTDEVICE rid = fplStructInit(RAWINPUTDEVICE, 0x01, 0x02, 0, window->windowHandle);
12994 if (!wapi->user.RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
12995 FPL__ERROR(FPL__MODULE_WINDOW, "Failed register raw input mouse device for window handle '%p'", window->windowHandle);
12996 }
12997 } else {
12998 const RAWINPUTDEVICE rid = fplStructInit(RAWINPUTDEVICE, 0x01, 0x02, RIDEV_REMOVE, fpl_null);
12999 if (!wapi->user.RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
13000 FPL__ERROR(FPL__MODULE_WINDOW, "Failed to unregister raw input mouse device");
13001 }
13002 }
13003 if (fpl__Win32IsCursorInWindow(wapi, window)) {
13004 fpl__Win32LoadCursor(wapi, window);
13005 }
13006}
13007
13008fpl_internal void fpl__Win32ShowCursor(const fpl__Win32Api *wapi, fpl__Win32WindowState *window) {
13009 fpl__Win32SetCursorState(wapi, window, false);
13010}
13011fpl_internal void fpl__Win32HideCursor(const fpl__Win32Api *wapi, fpl__Win32WindowState *window) {
13012 fpl__Win32SetCursorState(wapi, window, true);
13013}
13014
13015fpl_internal fplKeyboardModifierFlags fpl__Win32GetKeyboardModifiers(const fpl__Win32Api *wapi) {
13017 bool lAltKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LMENU);
13018 bool rAltKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RMENU);
13019 bool lShiftKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LSHIFT);
13020 bool rShiftKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RSHIFT);
13021 bool lCtrlKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LCONTROL);
13022 bool rCtrlKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RCONTROL);
13023 bool lSuperKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LWIN);
13024 bool rSuperKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RWIN);
13025 bool capsLockActive = fpl__Win32IsKeyActive(wapi, VK_CAPITAL);
13026 bool numLockActive = fpl__Win32IsKeyActive(wapi, VK_NUMLOCK);
13027 bool scrollLockActive = fpl__Win32IsKeyActive(wapi, VK_SCROLL);
13028 if (lAltKeyIsDown) {
13029 modifiers |= fplKeyboardModifierFlags_LAlt;
13030 }
13031 if (rAltKeyIsDown) {
13032 modifiers |= fplKeyboardModifierFlags_RAlt;
13033 }
13034 if (lShiftKeyIsDown) {
13036 }
13037 if (rShiftKeyIsDown) {
13039 }
13040 if (lCtrlKeyIsDown) {
13041 modifiers |= fplKeyboardModifierFlags_LCtrl;
13042 }
13043 if (rCtrlKeyIsDown) {
13044 modifiers |= fplKeyboardModifierFlags_RCtrl;
13045 }
13046 if (lSuperKeyIsDown) {
13048 }
13049 if (rSuperKeyIsDown) {
13051 }
13052 if (capsLockActive) {
13054 }
13055 if (numLockActive) {
13057 }
13058 if (scrollLockActive) {
13060 }
13061 return(modifiers);
13062}
13063
13064fpl_internal void fpl__Win32HandleMessage(const fpl__Win32Api *wapi, fpl__PlatformAppState *appState, fpl__Win32WindowState *windowState, MSG *msg) {
13065 if (appState->currentSettings.window.callbacks.eventCallback != fpl_null) {
13066 appState->currentSettings.window.callbacks.eventCallback(fplGetPlatformType(), windowState, &msg, appState->currentSettings.window.callbacks.eventUserData);
13067 }
13068 wapi->user.TranslateMessage(msg);
13069 wapi->user.DispatchMessageW(msg);
13070}
13071
13072fpl_internal void CALLBACK fpl__Win32MessageFiberProc(struct fpl__PlatformAppState *appState) {
13073 fpl__Win32AppState *win32State = &appState->win32;
13074 fpl__Win32WindowState *windowState = &appState->window.win32;
13075 const fpl__Win32Api *wapi = &win32State->winApi;
13076 wapi->user.SetTimer(appState->window.win32.windowHandle, 1, 1, 0);
13077 for (;;) {
13078 MSG message;
13079 while (wapi->user.PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) {
13080 fpl__Win32HandleMessage(wapi, appState, windowState, &message);
13081 }
13082 SwitchToFiber(appState->window.win32.mainFiber);
13083 }
13084}
13085
13086LRESULT CALLBACK fpl__Win32MessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
13087 fpl__PlatformAppState *appState = fpl__global__AppState;
13088 fplAssert(appState != fpl_null);
13089
13090 fpl__Win32AppState *win32State = &appState->win32;
13091 fpl__Win32WindowState *win32Window = &appState->window.win32;
13092 const fpl__Win32Api *wapi = &win32State->winApi;
13093
13094 if (!win32Window->windowHandle) {
13095 return wapi->user.DefWindowProcW(hwnd, msg, wParam, lParam);
13096 }
13097
13098 LRESULT result = 0;
13099 switch (msg) {
13100 case WM_TIMER:
13101 {
13102 if (win32Window->mainFiber != fpl_null) {
13103 SwitchToFiber(win32Window->mainFiber);
13104 }
13105 } break;
13106
13107 case WM_DESTROY:
13108 case WM_CLOSE:
13109 {
13110 appState->window.isRunning = false;
13111 } break;
13112
13113 case WM_SIZE:
13114 {
13115 DWORD newWidth = LOWORD(lParam);
13116 DWORD newHeight = HIWORD(lParam);
13117 if (wParam == SIZE_MAXIMIZED) {
13118 fpl__PushWindowSizeEvent(fplWindowEventType_Maximized, newWidth, newHeight);
13119 } else if (wParam == SIZE_MINIMIZED) {
13120 fpl__PushWindowSizeEvent(fplWindowEventType_Minimized, newWidth, newHeight);
13121 } else if (wParam == SIZE_RESTORED) {
13122 fpl__PushWindowSizeEvent(fplWindowEventType_Restored, newWidth, newHeight);
13123 }
13124
13125# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
13126 if (appState->currentSettings.video.backend == fplVideoBackendType_Software) {
13127 if (appState->initSettings.video.isAutoSize) {
13128 fplResizeVideoBackBuffer(newWidth, newHeight);
13129 }
13130 }
13131# endif
13132
13133 fpl__PushWindowSizeEvent(fplWindowEventType_Resized, newWidth, newHeight);
13134
13135 return 0;
13136 } break;
13137
13138 case WM_DROPFILES:
13139 {
13140 HDROP dropHandle = (HDROP)wParam;
13141 char fileBufferA[FPL_MAX_PATH_LENGTH];
13142 UINT fileCount;
13143 wchar_t fileBufferW[FPL_MAX_PATH_LENGTH];
13144 fileCount = wapi->shell.DragQueryFileW(dropHandle, 0xFFFFFFFF, fileBufferW, 0);
13145 if (fileCount > 0) {
13146 size_t filesTableSize = fileCount * sizeof(char **);
13147 size_t maxFileStride = FPL_MAX_PATH_LENGTH * 2 + 1;
13148 size_t filesMemorySize = filesTableSize + FPL__ARBITARY_PADDING + maxFileStride * fileCount;
13149 void *filesTableMemory = fpl__AllocateDynamicMemory(filesMemorySize, 16);
13150 char **filesTable = (char **)filesTableMemory;
13151 for (UINT fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
13152 filesTable[fileIndex] = (char *)((uint8_t *)filesTableMemory + filesTableSize + FPL__ARBITARY_PADDING + fileIndex * maxFileStride);
13153 }
13154 for (UINT fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
13155 char *file = filesTable[fileIndex];
13156 fileBufferW[0] = 0;
13157 UINT dragResult = wapi->shell.DragQueryFileW(dropHandle, 0, fileBufferW, fplArrayCount(fileBufferW));
13158 size_t sourceLen = lstrlenW(fileBufferW);
13159 fplWideStringToUTF8String(fileBufferW, sourceLen, file, maxFileStride);
13160 }
13161 fplMemoryBlock memory = fplZeroInit;
13162 memory.size = filesMemorySize;
13163 memory.base = filesTableMemory;
13164 fpl__PushWindowDropFilesEvent(fileBufferA, fileCount, (const char **)filesTable, &memory);
13165 }
13166 } break;
13167
13168 case WM_SYSKEYDOWN:
13169 case WM_SYSKEYUP:
13170 case WM_KEYDOWN:
13171 case WM_KEYUP:
13172 {
13173 if (!appState->currentSettings.input.disabledEvents) {
13174 uint64_t keyCode = wParam;
13175 bool isDown = (lParam & (1 << 31)) == 0;
13176 bool wasDown = (lParam & (1 << 30)) != 0;
13177 bool altKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_MENU);
13179 fplKeyboardModifierFlags modifiers = fpl__Win32GetKeyboardModifiers(wapi);
13180 fpl__HandleKeyboardButtonEvent(&appState->window, GetTickCount(), keyCode, modifiers, keyState, false);
13181 }
13182 } break;
13183
13184 case WM_CHAR:
13185 case WM_SYSCHAR:
13186 case WM_UNICHAR:
13187 {
13188 if ((msg == WM_UNICHAR) && (wParam == UNICODE_NOCHAR)) {
13189 // @NOTE(final): WM_UNICHAR was sent by a third-party input method. Do not add any chars here!
13190 return TRUE;
13191 }
13192 fpl__HandleKeyboardInputEvent(&appState->window, (uint64_t)wParam, (uint32_t)wParam);
13193 return 0;
13194 } break;
13195
13196 case WM_ACTIVATE:
13197 {
13198 } break;
13199
13200 case WM_MOUSEACTIVATE:
13201 {
13202 if (HIWORD(lParam) == WM_LBUTTONDOWN) {
13203 if (LOWORD(lParam) == HTCLOSE || LOWORD(lParam) == HTMINBUTTON || LOWORD(lParam) == HTMAXBUTTON) {
13204 win32Window->isFrameInteraction = true;
13205 }
13206 }
13207 } break;
13208
13209 case WM_CAPTURECHANGED:
13210 {
13211 if (lParam == 0 && win32Window->isFrameInteraction) {
13212 if (!win32Window->isCursorActive) {
13213 fpl__Win32HideCursor(wapi, win32Window);
13214 }
13215 win32Window->isFrameInteraction = false;
13216 }
13217 } break;
13218
13219 case WM_SETFOCUS:
13220 {
13221 fplEvent newEvent = fplZeroInit;
13222 newEvent.type = fplEventType_Window;
13224 fpl__PushInternalEvent(&newEvent);
13225 if (win32Window->isFrameInteraction) {
13226 break;
13227 }
13228 if (!win32Window->isCursorActive) {
13229 fpl__Win32HideCursor(wapi, win32Window);
13230 }
13231 return 0;
13232 } break;
13233
13234 case WM_KILLFOCUS:
13235 {
13236 if (!win32Window->isCursorActive) {
13237 fpl__Win32ShowCursor(wapi, win32Window);
13238 }
13239 fplEvent newEvent = fplZeroInit;
13240 newEvent.type = fplEventType_Window;
13242 fpl__PushInternalEvent(&newEvent);
13243 return 0;
13244 } break;
13245
13246 case WM_ENTERSIZEMOVE:
13247 case WM_ENTERMENULOOP:
13248 {
13249 // Restore cursor when needed
13250 if (!win32Window->isCursorActive) {
13251 fpl__Win32ShowCursor(wapi, win32Window);
13252 }
13253 } break;
13254
13255 case WM_EXITSIZEMOVE:
13256 case WM_EXITMENULOOP:
13257 {
13258 if (!win32Window->isCursorActive) {
13259 fpl__Win32HideCursor(wapi, win32Window);
13260 }
13261 } break;
13262
13263 case WM_LBUTTONDOWN:
13264 case WM_LBUTTONUP:
13265 case WM_RBUTTONDOWN:
13266 case WM_RBUTTONUP:
13267 case WM_MBUTTONDOWN:
13268 case WM_MBUTTONUP:
13269 {
13270 if (!appState->currentSettings.input.disabledEvents) {
13271 fplButtonState buttonState;
13272 if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) {
13273 buttonState = fplButtonState_Press;
13274 } else {
13275 buttonState = fplButtonState_Release;
13276 }
13277 if (buttonState == fplButtonState_Press) {
13278 wapi->user.SetCapture(hwnd);
13279 } else {
13280 wapi->user.ReleaseCapture();
13281 }
13282 fplMouseButtonType mouseButton;
13283 if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONUP) {
13284 mouseButton = fplMouseButtonType_Left;
13285 } else if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONUP) {
13286 mouseButton = fplMouseButtonType_Right;
13287 } else if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONUP) {
13288 mouseButton = fplMouseButtonType_Middle;
13289 } else {
13290 mouseButton = fplMouseButtonType_None;
13291 }
13292 if (mouseButton != fplMouseButtonType_None) {
13293 int32_t mouseX = GET_X_LPARAM(lParam);
13294 int32_t mouseY = GET_Y_LPARAM(lParam);
13295 fpl__HandleMouseButtonEvent(&appState->window, mouseX, mouseY, mouseButton, buttonState);
13296 }
13297 }
13298 } break;
13299 case WM_MOUSEMOVE:
13300 {
13301 if (!appState->currentSettings.input.disabledEvents) {
13302 int32_t mouseX = GET_X_LPARAM(lParam);
13303 int32_t mouseY = GET_Y_LPARAM(lParam);
13304 fpl__HandleMouseMoveEvent(&appState->window, mouseX, mouseY);
13305 }
13306 } break;
13307 case WM_MOUSEWHEEL:
13308 {
13309 int32_t mouseX = GET_X_LPARAM(lParam);
13310 int32_t mouseY = GET_Y_LPARAM(lParam);
13311 short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
13312 float wheelDelta = zDelta / (float)WHEEL_DELTA;
13313 fpl__HandleMouseWheelEvent(&appState->window, mouseX, mouseY, wheelDelta);
13314 } break;
13315
13316 case WM_SETCURSOR:
13317 {
13318 if (LOWORD(lParam) == HTCLIENT) {
13319 fpl__Win32LoadCursor(wapi, win32Window);
13320 return TRUE;
13321 }
13322 } break;
13323
13324 case WM_PAINT:
13325 {
13326 if (appState->currentSettings.window.callbacks.exposedCallback != fpl_null) {
13327 MSG msgData = fplZeroInit;
13328 msgData.message = msg;
13329 msgData.hwnd = hwnd;
13330 msgData.wParam = wParam;
13331 msgData.lParam = lParam;
13332 appState->currentSettings.window.callbacks.exposedCallback(fplGetPlatformType(), win32Window, &msgData, appState->currentSettings.window.callbacks.exposedUserData);
13333 } else {
13334 if (appState->currentSettings.video.backend == fplVideoBackendType_None) {
13335 PAINTSTRUCT ps;
13336 HDC hdc = wapi->user.BeginPaint(hwnd, &ps);
13337 wapi->user.EndPaint(hwnd, &ps);
13338 return(0);
13339 }
13340 }
13341 } break;
13342
13343 case WM_ERASEBKGND:
13344 {
13345 // Prevent erasing of the background always, but only if a video backend is being used
13346 // @NOTE(tspaete): Do not prevent the erasing of the backend for software video output, otherwise we will get weird flickering issue at startup
13347 if (appState->currentSettings.video.backend != fplVideoBackendType_None && appState->currentSettings.video.backend != fplVideoBackendType_Software) {
13348 return 1;
13349 }
13350 } break;
13351
13352 case WM_SYSCOMMAND:
13353 {
13354 WPARAM masked = wParam & 0xFFF0;
13355 switch (masked) {
13356 case SC_SCREENSAVE:
13357 case SC_MONITORPOWER: {
13358 if (appState->currentSettings.window.isScreenSaverPrevented || appState->currentSettings.window.isMonitorPowerPrevented) {
13359 return 0;
13360 }
13361 } break;
13362 }
13363 } break;
13364
13365 default:
13366 break;
13367 }
13368 result = wapi->user.DefWindowProcW(hwnd, msg, wParam, lParam);
13369 return (result);
13370}
13371
13372fpl_internal HICON fpl__Win32LoadIconFromImageSource(const fpl__Win32Api *wapi, const HINSTANCE appInstance, const fplImageSource *imageSource) {
13373 fplAssert(imageSource != fpl_null);
13374 HICON result = 0;
13375 if (imageSource->width > 0 && imageSource->height > 0 && imageSource->data != fpl_null) {
13376 BITMAPV5HEADER bi = fplZeroInit;
13377 bi.bV5Size = sizeof(bi);
13378 bi.bV5Width = (LONG)imageSource->width;
13379 bi.bV5Height = -(LONG)imageSource->height;
13380 bi.bV5Planes = 1;
13381 bi.bV5BitCount = 32;
13382 bi.bV5Compression = BI_BITFIELDS;
13383 bi.bV5RedMask = 0x00ff0000;
13384 bi.bV5GreenMask = 0x0000ff00;
13385 bi.bV5BlueMask = 0x000000ff;
13386 bi.bV5AlphaMask = 0xff000000;
13387
13388 uint8_t *targetData = fpl_null;
13389 HDC dc = wapi->user.GetDC(fpl_null);
13390 HBITMAP colorBitmap = wapi->gdi.CreateDIBSection(dc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&targetData, fpl_null, (DWORD)0);
13391 if (colorBitmap == fpl_null) {
13392 FPL__ERROR(FPL__MODULE_WIN32, "Failed to create DIBSection from image with size %lu x %lu", imageSource->width, imageSource->height);
13393 }
13394 wapi->user.ReleaseDC(fpl_null, dc);
13395
13396 HBITMAP maskBitmap = wapi->gdi.CreateBitmap(imageSource->width, imageSource->height, 1, 1, fpl_null);
13397 if (maskBitmap == fpl_null) {
13398 FPL__ERROR(FPL__MODULE_WIN32, "Failed to create Bitmap Mask from image with size %lu x %lu", imageSource->width, imageSource->height);
13399 }
13400 if (colorBitmap != fpl_null && maskBitmap != fpl_null) {
13401 fplAssert(targetData != fpl_null);
13402 uint8_t *dst = targetData;
13403 const uint8_t *src = imageSource->data;
13404 if (imageSource->type == fplImageType_RGBA) {
13405 for (uint32_t i = 0; i < imageSource->width * imageSource->height; ++i) {
13406 dst[0] = src[2]; // R > B
13407 dst[1] = src[1]; // G > G
13408 dst[2] = src[0]; // B > R
13409 dst[3] = src[3]; // A > A
13410 src += 4;
13411 dst += 4;
13412 }
13413 ICONINFO ii = fplZeroInit;
13414 ii.fIcon = TRUE;
13415 ii.xHotspot = 0;
13416 ii.yHotspot = 0;
13417 ii.hbmMask = maskBitmap;
13418 ii.hbmColor = colorBitmap;
13419 result = wapi->user.CreateIconIndirect(&ii);
13420 } else {
13421 FPL__ERROR(FPL__MODULE_WIN32, "Image source type '%d' for icon is not supported", imageSource->type);
13422 }
13423 }
13424 if (colorBitmap != fpl_null) {
13425 wapi->gdi.DeleteObject(colorBitmap);
13426 }
13427 if (maskBitmap != fpl_null) {
13428 wapi->gdi.DeleteObject(maskBitmap);
13429 }
13430 }
13431 if (result == 0) {
13432 result = fpl__win32_LoadIcon(appInstance, IDI_APPLICATION);
13433 }
13434 return(result);
13435}
13436
13437fpl_internal fplKey fpl__Win32TranslateVirtualKey(const fpl__Win32Api *wapi, const uint64_t virtualKey) {
13438 switch (virtualKey) {
13439 case VK_BACK:
13440 return fplKey_Backspace;
13441 case VK_TAB:
13442 return fplKey_Tab;
13443
13444 case VK_CLEAR:
13445 return fplKey_Clear;
13446 case VK_RETURN:
13447 return fplKey_Return;
13448
13449 case VK_SHIFT:
13450 return fplKey_Shift;
13451 case VK_CONTROL:
13452 return fplKey_Control;
13453 case VK_MENU:
13454 return fplKey_Alt;
13455 case VK_PAUSE:
13456 return fplKey_Pause;
13457 case VK_CAPITAL:
13458 return fplKey_CapsLock;
13459
13460 case VK_ESCAPE:
13461 return fplKey_Escape;
13462 case VK_SPACE:
13463 return fplKey_Space;
13464 case VK_PRIOR:
13465 return fplKey_PageUp;
13466 case VK_NEXT:
13467 return fplKey_PageDown;
13468 case VK_END:
13469 return fplKey_End;
13470 case VK_HOME:
13471 return fplKey_Home;
13472 case VK_LEFT:
13473 return fplKey_Left;
13474 case VK_UP:
13475 return fplKey_Up;
13476 case VK_RIGHT:
13477 return fplKey_Right;
13478 case VK_DOWN:
13479 return fplKey_Down;
13480 case VK_SELECT:
13481 return fplKey_Select;
13482 case VK_PRINT:
13483 return fplKey_Print;
13484 case VK_EXECUTE:
13485 return fplKey_Execute;
13486 case VK_SNAPSHOT:
13487 return fplKey_Snapshot;
13488 case VK_INSERT:
13489 return fplKey_Insert;
13490 case VK_DELETE:
13491 return fplKey_Delete;
13492 case VK_HELP:
13493 return fplKey_Help;
13494
13495 case 0x30:
13496 return fplKey_0;
13497 case 0x31:
13498 return fplKey_1;
13499 case 0x32:
13500 return fplKey_2;
13501 case 0x33:
13502 return fplKey_3;
13503 case 0x34:
13504 return fplKey_4;
13505 case 0x35:
13506 return fplKey_5;
13507 case 0x36:
13508 return fplKey_6;
13509 case 0x37:
13510 return fplKey_7;
13511 case 0x38:
13512 return fplKey_8;
13513 case 0x39:
13514 return fplKey_9;
13515
13516 case 0x41:
13517 return fplKey_A;
13518 case 0x42:
13519 return fplKey_B;
13520 case 0x43:
13521 return fplKey_C;
13522 case 0x44:
13523 return fplKey_D;
13524 case 0x45:
13525 return fplKey_E;
13526 case 0x46:
13527 return fplKey_F;
13528 case 0x47:
13529 return fplKey_G;
13530 case 0x48:
13531 return fplKey_H;
13532 case 0x49:
13533 return fplKey_I;
13534 case 0x4A:
13535 return fplKey_J;
13536 case 0x4B:
13537 return fplKey_K;
13538 case 0x4C:
13539 return fplKey_L;
13540 case 0x4D:
13541 return fplKey_M;
13542 case 0x4E:
13543 return fplKey_N;
13544 case 0x4F:
13545 return fplKey_O;
13546 case 0x50:
13547 return fplKey_P;
13548 case 0x51:
13549 return fplKey_Q;
13550 case 0x52:
13551 return fplKey_R;
13552 case 0x53:
13553 return fplKey_S;
13554 case 0x54:
13555 return fplKey_T;
13556 case 0x55:
13557 return fplKey_U;
13558 case 0x56:
13559 return fplKey_V;
13560 case 0x57:
13561 return fplKey_W;
13562 case 0x58:
13563 return fplKey_X;
13564 case 0x59:
13565 return fplKey_Y;
13566 case 0x5A:
13567 return fplKey_Z;
13568
13569 case VK_LWIN:
13570 return fplKey_LeftSuper;
13571 case VK_RWIN:
13572 return fplKey_RightSuper;
13573 case VK_APPS:
13574 return fplKey_Apps;
13575
13576 case VK_SLEEP:
13577 return fplKey_Sleep;
13578 case VK_NUMPAD0:
13579 return fplKey_NumPad0;
13580 case VK_NUMPAD1:
13581 return fplKey_NumPad1;
13582 case VK_NUMPAD2:
13583 return fplKey_NumPad2;
13584 case VK_NUMPAD3:
13585 return fplKey_NumPad3;
13586 case VK_NUMPAD4:
13587 return fplKey_NumPad4;
13588 case VK_NUMPAD5:
13589 return fplKey_NumPad5;
13590 case VK_NUMPAD6:
13591 return fplKey_NumPad6;
13592 case VK_NUMPAD7:
13593 return fplKey_NumPad7;
13594 case VK_NUMPAD8:
13595 return fplKey_NumPad8;
13596 case VK_NUMPAD9:
13597 return fplKey_NumPad9;
13598 case VK_MULTIPLY:
13599 return fplKey_Multiply;
13600 case VK_ADD:
13601 return fplKey_Add;
13602 case VK_SEPARATOR:
13603 return fplKey_Separator;
13604 case VK_SUBTRACT:
13605 return fplKey_Substract;
13606 case VK_DECIMAL:
13607 return fplKey_Decimal;
13608 case VK_DIVIDE:
13609 return fplKey_Divide;
13610 case VK_F1:
13611 return fplKey_F1;
13612 case VK_F2:
13613 return fplKey_F2;
13614 case VK_F3:
13615 return fplKey_F3;
13616 case VK_F4:
13617 return fplKey_F4;
13618 case VK_F5:
13619 return fplKey_F5;
13620 case VK_F6:
13621 return fplKey_F6;
13622 case VK_F7:
13623 return fplKey_F7;
13624 case VK_F8:
13625 return fplKey_F8;
13626 case VK_F9:
13627 return fplKey_F9;
13628 case VK_F10:
13629 return fplKey_F10;
13630 case VK_F11:
13631 return fplKey_F11;
13632 case VK_F12:
13633 return fplKey_F12;
13634 case VK_F13:
13635 return fplKey_F13;
13636 case VK_F14:
13637 return fplKey_F14;
13638 case VK_F15:
13639 return fplKey_F15;
13640 case VK_F16:
13641 return fplKey_F16;
13642 case VK_F17:
13643 return fplKey_F17;
13644 case VK_F18:
13645 return fplKey_F18;
13646 case VK_F19:
13647 return fplKey_F19;
13648 case VK_F20:
13649 return fplKey_F20;
13650 case VK_F21:
13651 return fplKey_F21;
13652 case VK_F22:
13653 return fplKey_F22;
13654 case VK_F23:
13655 return fplKey_F23;
13656 case VK_F24:
13657 return fplKey_F24;
13658
13659 case VK_LSHIFT:
13660 return fplKey_LeftShift;
13661 case VK_RSHIFT:
13662 return fplKey_RightShift;
13663 case VK_LCONTROL:
13664 return fplKey_LeftControl;
13665 case VK_RCONTROL:
13666 return fplKey_RightControl;
13667 case VK_LMENU:
13668 return fplKey_LeftAlt;
13669 case VK_RMENU:
13670 return fplKey_RightAlt;
13671
13672 case VK_VOLUME_MUTE:
13673 return fplKey_VolumeMute;
13674 case VK_VOLUME_DOWN:
13675 return fplKey_VolumeDown;
13676 case VK_VOLUME_UP:
13677 return fplKey_VolumeUp;
13678 case VK_MEDIA_NEXT_TRACK:
13679 return fplKey_MediaNextTrack;
13680 case VK_MEDIA_PREV_TRACK:
13681 return fplKey_MediaPrevTrack;
13682 case VK_MEDIA_STOP:
13683 return fplKey_MediaStop;
13684 case VK_MEDIA_PLAY_PAUSE:
13685 return fplKey_MediaPlayPause;
13686
13687 case VK_OEM_MINUS:
13688 return fplKey_OemMinus;
13689 case VK_OEM_PLUS:
13690 return fplKey_OemPlus;
13691 case VK_OEM_COMMA:
13692 return fplKey_OemComma;
13693 case VK_OEM_PERIOD:
13694 return fplKey_OemPeriod;
13695
13696 case VK_OEM_1:
13697 return fplKey_Oem1;
13698 case VK_OEM_2:
13699 return fplKey_Oem2;
13700 case VK_OEM_3:
13701 return fplKey_Oem3;
13702 case VK_OEM_4:
13703 return fplKey_Oem4;
13704 case VK_OEM_5:
13705 return fplKey_Oem5;
13706 case VK_OEM_6:
13707 return fplKey_Oem6;
13708 case VK_OEM_7:
13709 return fplKey_Oem7;
13710 case VK_OEM_8:
13711 return fplKey_Oem8;
13712
13713 default:
13714 return fplKey_None;
13715 }
13716}
13717
13718fpl_internal bool fpl__Win32InitWindow(const fplSettings *initSettings, fplWindowSettings *currentWindowSettings, fpl__PlatformAppState *platAppState, fpl__Win32AppState *appState, fpl__Win32WindowState *windowState, const fpl__SetupWindowCallbacks *setupCallbacks) {
13719 fplAssert(appState != fpl_null);
13720 const fpl__Win32Api *wapi = &appState->winApi;
13721 const fplWindowSettings *initWindowSettings = &initSettings->window;
13722
13723 // Init keymap
13724 fplClearStruct(platAppState->window.keyMap);
13725 for (int i = 0; i < 256; ++i) {
13726 int vk = wapi->user.MapVirtualKeyW(MAPVK_VSC_TO_VK, i);
13727 if (vk == 0) {
13728 vk = i;
13729 }
13730 platAppState->window.keyMap[i] = fpl__Win32TranslateVirtualKey(wapi, vk);
13731 }
13732
13733 // Hint for windows to know, that the application is in use always
13734 if (initSettings->window.isMonitorPowerPrevented || initSettings->window.isScreenSaverPrevented) {
13735 SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
13736 } else {
13737 SetThreadExecutionState(ES_CONTINUOUS);
13738 }
13739
13740 // Presetup window
13741 if (setupCallbacks->preSetup != fpl_null) {
13742 setupCallbacks->preSetup(platAppState, platAppState->initFlags, &platAppState->initSettings);
13743 }
13744
13745 // Register window class
13746 WNDCLASSEXW windowClass = fplZeroInit;
13747 windowClass.cbSize = sizeof(windowClass);
13748 windowClass.hInstance = GetModuleHandleA(fpl_null);
13749
13750 // Set window background, either as system brush or custom brush which needs to be released when changed or the platform is released
13751 if (initWindowSettings->background.value == 0) {
13752 windowState->backgroundBrush = fpl_null;
13753 windowClass.hbrBackground = wapi->user.GetSysColorBrush(COLOR_BACKGROUND);
13754 } else {
13755 COLORREF brushColor = RGB(initWindowSettings->background.components.r, initWindowSettings->background.components.g, initWindowSettings->background.components.b);
13756 windowState->backgroundBrush = wapi->gdi.CreateSolidBrush(brushColor);
13757 windowClass.hbrBackground = windowState->backgroundBrush;
13758 }
13759
13760 windowClass.cbSize = sizeof(windowClass);
13761 windowClass.style = CS_HREDRAW | CS_VREDRAW;
13762 windowClass.hCursor = fpl__win32_LoadCursor(windowClass.hInstance, IDC_ARROW);
13763 windowClass.hIconSm = fpl__Win32LoadIconFromImageSource(wapi, windowClass.hInstance, &initWindowSettings->icons[0]);
13764 windowClass.hIcon = fpl__Win32LoadIconFromImageSource(wapi, windowClass.hInstance, &initWindowSettings->icons[1]);
13765 windowClass.lpszClassName = FPL__WIN32_CLASSNAME;
13766 windowClass.lpfnWndProc = fpl__Win32MessageProc;
13767 windowClass.style |= CS_OWNDC;
13768 lstrcpynW(windowState->windowClass, windowClass.lpszClassName, fplArrayCount(windowState->windowClass));
13769 if (wapi->user.RegisterClassExW(&windowClass) == 0) {
13770 FPL__ERROR(FPL__MODULE_WINDOW, "Failed registering window class '%s'", windowState->windowClass);
13771 return false;
13772 }
13773
13774 // Set window title
13775 wchar_t windowTitleBuffer[FPL_MAX_NAME_LENGTH];
13776 if (fplGetStringLength(initWindowSettings->title) > 0) {
13777 fplUTF8StringToWideString(initWindowSettings->title, fplGetStringLength(initWindowSettings->title), windowTitleBuffer, fplArrayCount(windowTitleBuffer));
13778 } else {
13779 const wchar_t *defaultTitle = FPL__WIN32_UNNAMED_WINDOW;
13780 lstrcpynW(windowTitleBuffer, defaultTitle, fplArrayCount(windowTitleBuffer));
13781 }
13782 wchar_t *windowTitle = windowTitleBuffer;
13783 fplWideStringToUTF8String(windowTitle, lstrlenW(windowTitle), currentWindowSettings->title, fplArrayCount(currentWindowSettings->title));
13784
13785 // Create Fibers
13786 windowState->mainFiber = ConvertThreadToFiber(0);
13787 windowState->messageFiber = CreateFiber(0, (PFIBER_START_ROUTINE)fpl__Win32MessageFiberProc, platAppState);
13788
13789 // Prepare window style, size and position
13790 DWORD style = fpl__Win32MakeWindowStyle(&initSettings->window);
13791 DWORD exStyle = fpl__Win32MakeWindowExStyle(&initSettings->window);
13792 if (initSettings->window.isResizable) {
13793 currentWindowSettings->isResizable = true;
13794 } else {
13795 currentWindowSettings->isResizable = false;
13796 }
13797 int windowX = CW_USEDEFAULT;
13798 int windowY = CW_USEDEFAULT;
13799 int windowWidth;
13800 int windowHeight;
13801 if ((initWindowSettings->windowSize.width > 0) &&
13802 (initWindowSettings->windowSize.height > 0)) {
13803 RECT windowRect;
13804 windowRect.left = 0;
13805 windowRect.top = 0;
13806 windowRect.right = initWindowSettings->windowSize.width;
13807 windowRect.bottom = initWindowSettings->windowSize.height;
13808 wapi->user.AdjustWindowRect(&windowRect, style, false);
13809 windowWidth = windowRect.right - windowRect.left;
13810 windowHeight = windowRect.bottom - windowRect.top;
13811 } else {
13812 // @NOTE(final): Operating system decide how big the window should be.
13813 windowWidth = CW_USEDEFAULT;
13814 windowHeight = CW_USEDEFAULT;
13815 }
13816
13817 // Create window
13818 windowState->windowHandle = wapi->user.CreateWindowExW(exStyle, windowClass.lpszClassName, windowTitle, style, windowX, windowY, windowWidth, windowHeight, fpl_null, fpl_null, windowClass.hInstance, fpl_null);
13819 if (windowState->windowHandle == fpl_null) {
13820 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);
13821 return false;
13822 }
13823
13824 // Accept files as drag & drop source
13825 wapi->shell.DragAcceptFiles(windowState->windowHandle, TRUE);
13826
13827 // Get actual window size and store results
13828 currentWindowSettings->windowSize.width = windowWidth;
13829 currentWindowSettings->windowSize.height = windowHeight;
13830 RECT clientRect;
13831 if (wapi->user.GetClientRect(windowState->windowHandle, &clientRect)) {
13832 currentWindowSettings->windowSize.width = clientRect.right - clientRect.left;
13833 currentWindowSettings->windowSize.height = clientRect.bottom - clientRect.top;
13834 }
13835
13836 // Get device context so we can swap the back and front buffer
13837 windowState->deviceContext = wapi->user.GetDC(windowState->windowHandle);
13838 if (windowState->deviceContext == fpl_null) {
13839 FPL__ERROR(FPL__MODULE_WINDOW, "Failed aquiring device context from window '%d'", windowState->windowHandle);
13840 return false;
13841 }
13842
13843 // Call post window setup callback
13844 if (setupCallbacks->postSetup != fpl_null) {
13845 setupCallbacks->postSetup(platAppState, platAppState->initFlags, initSettings);
13846 }
13847
13848 // Enter fullscreen if needed
13849 if (initWindowSettings->isFullscreen) {
13850 fplSetWindowFullscreenSize(true, initWindowSettings->fullscreenSize.width, initWindowSettings->fullscreenSize.height, initWindowSettings->fullscreenRefreshRate);
13851 }
13852
13853 // Show window
13854 wapi->user.ShowWindow(windowState->windowHandle, SW_SHOW);
13855 wapi->user.SetForegroundWindow(windowState->windowHandle);
13856 wapi->user.SetFocus(windowState->windowHandle);
13857
13858 // Cursor is visible at start
13859 windowState->defaultCursor = windowClass.hCursor;
13860 windowState->isCursorActive = true;
13861 platAppState->window.isRunning = true;
13862
13863 return true;
13864}
13865
13866fpl_internal void fpl__Win32ReleaseWindow(const fpl__Win32InitState *initState, const fpl__Win32AppState *appState, fpl__Win32WindowState *windowState) {
13867 const fpl__Win32Api *wapi = &appState->winApi;
13868 if (windowState->deviceContext != fpl_null) {
13869 wapi->user.ReleaseDC(windowState->windowHandle, windowState->deviceContext);
13870 windowState->deviceContext = fpl_null;
13871 }
13872 if (windowState->windowHandle != fpl_null) {
13873 wapi->user.DestroyWindow(windowState->windowHandle);
13874 windowState->windowHandle = fpl_null;
13875 wapi->user.UnregisterClassW(windowState->windowClass, initState->appInstance);
13876 }
13877 if (windowState->backgroundBrush != fpl_null) {
13878 wapi->gdi.DeleteObject(windowState->backgroundBrush);
13879 windowState->backgroundBrush = fpl_null;
13880 }
13881 if (windowState->messageFiber != fpl_null) {
13882 DeleteFiber(windowState->messageFiber);
13883 windowState->messageFiber = fpl_null;
13884 }
13885 if (windowState->mainFiber != fpl_null) {
13886 ConvertFiberToThread();
13887 windowState->mainFiber = fpl_null;
13888 }
13889}
13890
13891#endif // FPL__ENABLE_WINDOW
13892
13893fpl_internal bool fpl__Win32ThreadWaitForMultiple(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout, const bool waitForAll) {
13894 FPL__CheckArgumentNull(threads, false);
13895 FPL__CheckArgumentMax(count, FPL_MAX_THREAD_COUNT, false);
13896 fplStaticAssert(FPL_MAX_THREAD_COUNT >= MAXIMUM_WAIT_OBJECTS);
13897 const size_t actualStride = stride > 0 ? stride : sizeof(fplThreadHandle *);
13898 for (size_t index = 0; index < count; ++index) {
13899 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
13900 if (thread == fpl_null) {
13901 FPL__ERROR(FPL__MODULE_THREADING, "Thread for index '%d' are not allowed to be null", index);
13902 return false;
13903 }
13905 if (thread->internalHandle.win32ThreadHandle == fpl_null) {
13906 FPL__ERROR(FPL__MODULE_THREADING, "Thread handle for index '%d' are not allowed to be null", index);
13907 return false;
13908 }
13909 }
13910 }
13911
13912 // @NOTE(final): WaitForMultipleObjects does not work for us here, because each thread will close its handle automatically
13913 // 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.
13915 size_t minThreads = waitForAll ? count : 1;
13916 size_t stoppedThreads = 0;
13917 while (stoppedThreads < minThreads) {
13918 stoppedThreads = 0;
13919 for (size_t index = 0; index < count; ++index) {
13920 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
13922 ++stoppedThreads;
13923 }
13924 }
13925 if (stoppedThreads >= minThreads) {
13926 break;
13927 }
13928 if (timeout != FPL_TIMEOUT_INFINITE) {
13929 if ((fplMillisecondsQuery() - startTime) >= timeout) {
13930 break;
13931 }
13932 }
13934 }
13935 bool result = stoppedThreads >= minThreads;
13936 return(result);
13937}
13938
13939fpl_internal bool fpl__Win32SignalWaitForMultiple(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout, const bool waitForAll) {
13940 FPL__CheckArgumentNull(signals, false);
13941 FPL__CheckArgumentMax(count, FPL_MAX_SIGNAL_COUNT, false);
13942 // @MEMORY(final): This wastes a lof memory, use temporary memory allocation here
13943 HANDLE signalHandles[FPL_MAX_SIGNAL_COUNT];
13944 const size_t actualStride = stride > 0 ? stride : sizeof(fplSignalHandle *);
13945 for (uint32_t index = 0; index < count; ++index) {
13946 fplSignalHandle *availableSignal = *(fplSignalHandle **)((uint8_t *)signals + index * actualStride);
13947 if (availableSignal == fpl_null) {
13948 FPL__ERROR(FPL__MODULE_THREADING, "Signal for index '%d' are not allowed to be null", index);
13949 return false;
13950 }
13951 if (availableSignal->internalHandle.win32EventHandle == fpl_null) {
13952 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle for index '%d' are not allowed to be null", index);
13953 return false;
13954 }
13955 HANDLE handle = availableSignal->internalHandle.win32EventHandle;
13956 signalHandles[index] = handle;
13957 }
13958 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
13959 DWORD code = WaitForMultipleObjects((DWORD)count, signalHandles, waitForAll ? TRUE : FALSE, t);
13960 bool result = (code >= WAIT_OBJECT_0);
13961 return(result);
13962}
13963
13964fpl_internal void fpl__Win32ReleasePlatform(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
13965 fplAssert(appState != fpl_null);
13966 fpl__Win32AppState *win32AppState = &appState->win32;
13967 fpl__Win32InitState *win32InitState = &initState->win32;
13968 if (appState->initFlags & fplInitFlags_GameController) {
13969 fpl__Win32UnloadXInputApi(&win32AppState->xinput.xinputApi);
13970 }
13971 fpl__Win32UnloadApi(&win32AppState->winApi);
13972}
13973
13974fpl_internal bool fpl__Win32InitPlatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
13975 fplAssert(initState != fpl_null);
13976 fplAssert(appState != fpl_null);
13977
13978 fpl__Win32InitState *win32InitState = &initState->win32;
13979 fpl__Win32AppState *win32AppState = &appState->win32;
13980
13981 // @NOTE(final): Expect kernel32.lib to be linked always, so VirtualAlloc, LoadLibrary, CreateThread, etc. will always work.
13982
13983 // Get application instance handle
13984 win32InitState->appInstance = GetModuleHandleA(fpl_null);
13985
13986 // Query performance frequency and store it once, it will never change during runtime
13987 QueryPerformanceFrequency(&win32InitState->qpf);
13988
13989 // Get main thread infos
13990 HANDLE mainThreadHandle = GetCurrentThread();
13991 DWORD mainThreadHandleId = GetCurrentThreadId();
13992 fplThreadHandle *mainThread = &fpl__global__ThreadState.mainThread;
13993 fplClearStruct(mainThread);
13994 mainThread->id = mainThreadHandleId;
13995 mainThread->internalHandle.win32ThreadHandle = mainThreadHandle;
13997
13998 // Load windows api library
13999 if (!fpl__Win32LoadApi(&win32AppState->winApi)) {
14000 // @NOTE(final): Assume that errors are pushed on already.
14001 fpl__Win32ReleasePlatform(initState, appState);
14002 return false;
14003 }
14004
14005 // Load XInput
14006 if (initFlags & fplInitFlags_GameController) {
14007 fpl__Win32LoadXInputApi(&win32AppState->xinput.xinputApi);
14008 }
14009
14010 // Show/Hide console
14011 bool showConsole = (initFlags & fplInitFlags_Console);
14012 HWND consoleWindow = GetConsoleWindow();
14013 if (!showConsole) {
14014 if (consoleWindow != fpl_null) {
14015 win32AppState->winApi.user.ShowWindow(consoleWindow, SW_HIDE);
14016 } else {
14017 FreeConsole();
14018 }
14019 } else if (consoleWindow != fpl_null) {
14020 const fplConsoleSettings *initConsoleSettings = &initSettings->console;
14021 fplConsoleSettings *currentConsoleSettings = &appState->currentSettings.console;
14022
14023 // Setup a console title
14024 wchar_t consoleTitleBuffer[FPL_MAX_NAME_LENGTH];
14025 if (fplGetStringLength(initConsoleSettings->title) > 0) {
14026 fplUTF8StringToWideString(initConsoleSettings->title, fplGetStringLength(initConsoleSettings->title), consoleTitleBuffer, fplArrayCount(consoleTitleBuffer));
14027 } else {
14028 const wchar_t *defaultTitle = FPL__WIN32_UNNAMED_CONSOLE;
14029 lstrcpynW(consoleTitleBuffer, defaultTitle, fplArrayCount(consoleTitleBuffer));
14030 }
14031 wchar_t *windowTitle = consoleTitleBuffer;
14032 fplWideStringToUTF8String(windowTitle, lstrlenW(windowTitle), currentConsoleSettings->title, fplArrayCount(currentConsoleSettings->title));
14033 SetConsoleTitleW(windowTitle);
14034
14035 win32AppState->winApi.user.ShowWindow(consoleWindow, SW_SHOW);
14036 }
14037
14038 return (true);
14039}
14040
14041//
14042// Win32 Atomics
14043//
14045 FPL_MEMORY_BARRIER();
14046 _ReadBarrier();
14047}
14049 FPL_MEMORY_BARRIER();
14050 _WriteBarrier();
14051}
14053 FPL_MEMORY_BARRIER();
14054 _ReadWriteBarrier();
14055}
14056
14057fpl_platform_api uint32_t fplAtomicExchangeU32(volatile uint32_t *target, const uint32_t value) {
14058 fplAssert(target != fpl_null);
14059 uint32_t result = InterlockedExchange((volatile LONG *)target, value);
14060 return (result);
14061}
14062fpl_platform_api int32_t fplAtomicExchangeS32(volatile int32_t *target, const int32_t value) {
14063 fplAssert(target != fpl_null);
14064 int32_t result = InterlockedExchange((volatile LONG *)target, value);
14065 return (result);
14066}
14067fpl_platform_api uint64_t fplAtomicExchangeU64(volatile uint64_t *target, const uint64_t value) {
14068 fplAssert(target != fpl_null);
14069 uint64_t result = InterlockedExchange64((volatile LONG64 *)target, value);
14070 return (result);
14071}
14072fpl_platform_api int64_t fplAtomicExchangeS64(volatile int64_t *target, const int64_t value) {
14073 fplAssert(target != fpl_null);
14074 int64_t result = InterlockedExchange64((volatile LONG64 *)target, value);
14075 return (result);
14076}
14077
14078fpl_platform_api uint32_t fplAtomicFetchAndAddU32(volatile uint32_t *value, const uint32_t addend) {
14079 fplAssert(value != fpl_null);
14080 uint32_t result = InterlockedExchangeAdd((volatile LONG *)value, addend);
14081 return (result);
14082}
14083fpl_platform_api int32_t fplAtomicFetchAndAddS32(volatile int32_t *value, const int32_t addend) {
14084 fplAssert(value != fpl_null);
14085 int32_t result = InterlockedExchangeAdd((volatile LONG *)value, addend);
14086 return (result);
14087}
14088fpl_platform_api uint64_t fplAtomicFetchAndAddU64(volatile uint64_t *value, const uint64_t addend) {
14089 fplAssert(value != fpl_null);
14090 uint64_t result = InterlockedExchangeAdd64((volatile LONG64 *)value, addend);
14091 return (result);
14092}
14093fpl_platform_api int64_t fplAtomicFetchAndAddS64(volatile int64_t *value, const int64_t addend) {
14094 fplAssert(value != fpl_null);
14095 int64_t result = InterlockedExchangeAdd64((volatile LONG64 *)value, addend);
14096 return(result);
14097}
14098
14099fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *value, const uint32_t addend) {
14100 fplAssert(value != fpl_null);
14101 uint32_t result = InterlockedAdd((volatile LONG *)value, addend);
14102 return (result);
14103}
14104fpl_platform_api int32_t fplAtomicAddAndFetchS32(volatile int32_t *value, const int32_t addend) {
14105 fplAssert(value != fpl_null);
14106 int32_t result = InterlockedAdd((volatile LONG *)value, addend);
14107 return (result);
14108}
14109fpl_platform_api uint64_t fplAtomicAddAndFetchU64(volatile uint64_t *value, const uint64_t addend) {
14110 fplAssert(value != fpl_null);
14111 uint64_t result = InterlockedAdd64((volatile LONG64 *)value, addend);
14112 return (result);
14113}
14114fpl_platform_api int64_t fplAtomicAddAndFetchS64(volatile int64_t *value, const int64_t addend) {
14115 fplAssert(value != fpl_null);
14116 int64_t result = InterlockedAdd64((volatile LONG64 *)value, addend);
14117 return(result);
14118}
14119
14120fpl_platform_api uint32_t fplAtomicIncrementU32(volatile uint32_t *value) {
14121 fplAssert(value != fpl_null);
14122 uint32_t result = InterlockedIncrement((volatile LONG *)value);
14123 return (result);
14124}
14125fpl_platform_api int32_t fplAtomicIncrementS32(volatile int32_t *value) {
14126 fplAssert(value != fpl_null);
14127 int32_t result = InterlockedIncrement((volatile LONG *)value);
14128 return (result);
14129}
14130fpl_platform_api uint64_t fplAtomicIncrementU64(volatile uint64_t *value) {
14131 fplAssert(value != fpl_null);
14132 uint64_t result = InterlockedIncrement64((volatile LONG64 *)value);
14133 return (result);
14134}
14135fpl_platform_api int64_t fplAtomicIncrementS64(volatile int64_t *value) {
14136 fplAssert(value != fpl_null);
14137 int64_t result = InterlockedIncrement64((volatile LONG64 *)value);
14138 return(result);
14139}
14140
14141fpl_platform_api uint32_t fplAtomicCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
14142 fplAssert(dest != fpl_null);
14143 uint32_t result = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
14144 return (result);
14145}
14146fpl_platform_api int32_t fplAtomicCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
14147 fplAssert(dest != fpl_null);
14148 int32_t result = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
14149 return (result);
14150}
14151fpl_platform_api uint64_t fplAtomicCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
14152 fplAssert(dest != fpl_null);
14153 uint64_t result = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
14154 return (result);
14155}
14156fpl_platform_api int64_t fplAtomicCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
14157 fplAssert(dest != fpl_null);
14158 int64_t result = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
14159 return (result);
14160}
14161
14162fpl_platform_api bool fplAtomicIsCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
14163 fplAssert(dest != fpl_null);
14164 uint32_t value = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
14165 bool result = (value == comparand);
14166 return (result);
14167}
14168fpl_platform_api bool fplAtomicIsCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
14169 fplAssert(dest != fpl_null);
14170 int32_t value = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
14171 bool result = (value == comparand);
14172 return (result);
14173}
14174fpl_platform_api bool fplAtomicIsCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
14175 fplAssert(dest != fpl_null);
14176 uint64_t value = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
14177 bool result = (value == comparand);
14178 return (result);
14179}
14180fpl_platform_api bool fplAtomicIsCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
14181 fplAssert(dest != fpl_null);
14182 int64_t value = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
14183 bool result = (value == comparand);
14184 return (result);
14185}
14186
14187fpl_platform_api uint32_t fplAtomicLoadU32(volatile uint32_t *source) {
14188 uint32_t result = InterlockedCompareExchange((volatile LONG *)source, 0, 0);
14189 return(result);
14190}
14191fpl_platform_api uint64_t fplAtomicLoadU64(volatile uint64_t *source) {
14192 uint64_t result = InterlockedCompareExchange64((volatile LONG64 *)source, 0, 0);
14193 return(result);
14194}
14195fpl_platform_api int32_t fplAtomicLoadS32(volatile int32_t *source) {
14196 int32_t result = InterlockedCompareExchange((volatile LONG *)source, 0, 0);
14197 return(result);
14198}
14199fpl_platform_api int64_t fplAtomicLoadS64(volatile int64_t *source) {
14200 int64_t result = InterlockedCompareExchange64((volatile LONG64 *)source, 0, 0);
14201 return(result);
14202}
14203
14204fpl_platform_api void fplAtomicStoreU32(volatile uint32_t *dest, const uint32_t value) {
14205 InterlockedExchange((volatile LONG *)dest, value);
14206}
14207fpl_platform_api void fplAtomicStoreU64(volatile uint64_t *dest, const uint64_t value) {
14208 InterlockedExchange64((volatile LONG64 *)dest, value);
14209}
14210fpl_platform_api void fplAtomicStoreS32(volatile int32_t *dest, const int32_t value) {
14211 InterlockedExchange((volatile LONG *)dest, value);
14212}
14213fpl_platform_api void fplAtomicStoreS64(volatile int64_t *dest, const int64_t value) {
14214 InterlockedExchange64((volatile LONG64 *)dest, value);
14215}
14216
14217//
14218// Win32 OS
14219//
14220fpl_internal const char *fpl__Win32GetVersionName(DWORD major, DWORD minor) {
14221 const char *result;
14222 if (major == 5 && minor == 0) {
14223 result = "Windows 2000";
14224 } else if (major == 5 && minor == 1) {
14225 result = "Windows XP";
14226 } else if (major == 5 && minor == 2) {
14227 result = "Windows XP";
14228 } else if (major == 6 && minor == 0) {
14229 result = "Windows Vista";
14230 } else if (major == 6 && minor == 1) {
14231 result = "Windows 7";
14232 } else if (major == 6 && minor == 2) {
14233 result = "Windows 8";
14234 } else if (major == 6 && minor == 3) {
14235 result = "Windows 8.1";
14236 } else if (major == 10) {
14237 result = "Windows 10";
14238 } else {
14239 result = "Windows";
14240 }
14241 return(result);
14242}
14243
14244#define FPL__FUNC_NTDLL_RtlGetVersion(name) DWORD WINAPI name(PRTL_OSVERSIONINFOW lpVersionInformation)
14245typedef FPL__FUNC_NTDLL_RtlGetVersion(fpl__func_ntdll_RtlGetVersionProc);
14246#define FPL__FUNC_KERNEL32_GetVersion(name) DWORD WINAPI name(void)
14247typedef FPL__FUNC_KERNEL32_GetVersion(fpl__func_kernel32_GetVersion);
14248#define FPL__FUNC_KERNEL32_GetVersionExW(name) BOOL WINAPI name(LPOSVERSIONINFOEXW lpVersionInfo)
14249typedef FPL__FUNC_KERNEL32_GetVersionExW(fpl__func_kernel32_GetVersionExW);
14251 FPL__CheckArgumentNull(outInfos, false);
14252
14253 fplClearStruct(outInfos);
14254
14255 // @NOTE(final): Prefer RtlGetVersion always, because MS might decide to totally remove GetVersion() and GetVersionEx()
14256 HMODULE ntdllModule = GetModuleHandleA("ntdll");
14257 fpl__func_ntdll_RtlGetVersionProc *rtlGetVersionProc = (fpl__func_ntdll_RtlGetVersionProc *)(void *)GetProcAddress(ntdllModule, "RtlGetVersion");
14258 if (rtlGetVersionProc != fpl_null) {
14259 RTL_OSVERSIONINFOW info = fplZeroInit;
14260 info.dwOSVersionInfoSize = sizeof(info);
14261 if (rtlGetVersionProc(&info) == 0) {
14262 fplS32ToString((int32_t)info.dwMajorVersion, outInfos->osVersion.version.parts.major, fplArrayCount(outInfos->osVersion.version.parts.major));
14263 fplS32ToString((int32_t)info.dwMinorVersion, outInfos->osVersion.version.parts.minor, fplArrayCount(outInfos->osVersion.version.parts.minor));
14265 fplS32ToString((int32_t)info.dwBuildNumber, outInfos->osVersion.version.parts.build, fplArrayCount(outInfos->osVersion.version.parts.build));
14266 fplStringFormat(outInfos->osVersion.fullName, fplArrayCount(outInfos->osVersion.fullName), "%u.%u.%u.%u", info.dwMajorVersion, info.dwMinorVersion, 0, info.dwBuildNumber);
14267 const char *versionName = fpl__Win32GetVersionName(info.dwMajorVersion, info.dwMinorVersion);
14268 fplCopyString(versionName, outInfos->osName, fplArrayCount(outInfos->osName));
14269 return(true);
14270 }
14271 }
14272
14273 // @NOTE(final): GetVersion() and GetVersionExA() is deprecated as of windows 8.1, so we load it manually always
14274 HMODULE kernelLib = LoadLibraryA("kernel32.dll");
14275 if (kernelLib == fpl_null) {
14276 FPL__ERROR(FPL__MODULE_WIN32, "Kernel32 library could not be loaded");
14277 return false;
14278 }
14279 fpl__func_kernel32_GetVersion *getVersionProc = (fpl__func_kernel32_GetVersion *)(void *)GetProcAddress(kernelLib, "GetVersion");
14280 fpl__func_kernel32_GetVersionExW *getVersionExProc = (fpl__func_kernel32_GetVersionExW *)(void *)GetProcAddress(kernelLib, "GetVersionExW");
14281 FreeLibrary(kernelLib);
14282
14283 if (getVersionExProc != fpl_null) {
14284 OSVERSIONINFOEXW infoEx = fplZeroInit;
14285 infoEx.dwOSVersionInfoSize = sizeof(infoEx);
14286 if (getVersionExProc(&infoEx) == TRUE) {
14287 fplS32ToString((int32_t)infoEx.dwMajorVersion, outInfos->osVersion.version.parts.major, fplArrayCount(outInfos->osVersion.version.parts.major));
14288 fplS32ToString((int32_t)infoEx.dwMinorVersion, outInfos->osVersion.version.parts.minor, fplArrayCount(outInfos->osVersion.version.parts.minor));
14290 fplS32ToString((int32_t)infoEx.dwBuildNumber, outInfos->osVersion.version.parts.build, fplArrayCount(outInfos->osVersion.version.parts.build));
14291 fplStringFormat(outInfos->osVersion.fullName, fplArrayCount(outInfos->osVersion.fullName), "%u.%u.%u.%u", infoEx.dwMajorVersion, infoEx.dwMinorVersion, 0, infoEx.dwBuildNumber);
14292 const char *versionName = fpl__Win32GetVersionName(infoEx.dwMajorVersion, infoEx.dwMinorVersion);
14293 fplCopyString(versionName, outInfos->osName, fplArrayCount(outInfos->osName));
14294 return(true);
14295 }
14296 }
14297
14298 if (getVersionProc != fpl_null) {
14299 DWORD dwVersion = getVersionProc();
14300 if (dwVersion > 0) {
14301 DWORD major = (DWORD)(LOBYTE(LOWORD(dwVersion)));
14302 DWORD minor = (DWORD)(HIBYTE(LOWORD(dwVersion)));
14303 DWORD build = 0;
14304 if (dwVersion < 0x80000000) {
14305 build = (DWORD)((DWORD)(HIWORD(dwVersion)));
14306 }
14307 fplS32ToString((int32_t)major, outInfos->osVersion.version.parts.major, fplArrayCount(outInfos->osVersion.version.parts.major));
14308 fplS32ToString((int32_t)minor, outInfos->osVersion.version.parts.minor, fplArrayCount(outInfos->osVersion.version.parts.minor));
14310 fplS32ToString((int32_t)build, outInfos->osVersion.version.parts.build, fplArrayCount(outInfos->osVersion.version.parts.build));
14311 fplStringFormat(outInfos->osVersion.fullName, fplArrayCount(outInfos->osVersion.fullName), "%u.%u.%u.%u", major, minor, 0, build);
14312 const char *versionName = fpl__Win32GetVersionName(major, minor);
14313 fplCopyString(versionName, outInfos->osName, fplArrayCount(outInfos->osName));
14314 return(true);
14315 }
14316 }
14317
14318 return(false);
14319}
14320
14321#define FPL__FUNC_ADV32_GetUserNameW(name) BOOL WINAPI name(LPWSTR lpBuffer, LPDWORD pcbBuffer)
14322typedef FPL__FUNC_ADV32_GetUserNameW(fpl__func_adv32_GetUserNameW);
14323fpl_platform_api size_t fplSessionGetUsername(char *nameBuffer, const size_t maxNameBufferLen) {
14324 const char *libName = "advapi32.dll";
14325 HMODULE adv32Lib = LoadLibraryA(libName);
14326 if (adv32Lib == fpl_null) {
14327 FPL__ERROR(FPL__MODULE_WIN32, "Failed loading library '%s'", libName);
14328 return false;
14329 }
14330 fpl__func_adv32_GetUserNameW *getUserNameProc = (fpl__func_adv32_GetUserNameW *)(void *)GetProcAddress(adv32Lib, "GetUserNameW");
14331 size_t result = 0;
14332 if (getUserNameProc != fpl_null) {
14333 wchar_t wideBuffer[FPL_MAX_BUFFER_LENGTH];
14334 DWORD size = (DWORD)fplArrayCount(wideBuffer);
14335 if (getUserNameProc(wideBuffer, &size) == TRUE) {
14336 result = fplWideStringToUTF8String(wideBuffer, size, nameBuffer, maxNameBufferLen);
14337 }
14338 }
14339 FreeLibrary(adv32Lib);
14340 return(result);
14341}
14342
14343//
14344// Win32 Hardware
14345//
14347 SYSTEM_INFO sysInfo = fplZeroInit;
14348 GetSystemInfo(&sysInfo);
14349 // @NOTE(final): For now this returns the number of logical processors, which is the actual core count in most cases.
14350 size_t result = sysInfo.dwNumberOfProcessors;
14351 return(result);
14352}
14353
14354#define FPL__WIN32_PROCESSOR_ARCHITECTURE_ARM64 12
14356 fplCPUArchType result;
14357 SYSTEM_INFO sysInfo = fplZeroInit;
14358 BOOL isWow64;
14359 if (IsWow64Process(GetCurrentProcess(), &isWow64)) {
14360 if (isWow64)
14361 GetNativeSystemInfo(&sysInfo);
14362 else
14363 GetSystemInfo(&sysInfo);
14364 } else {
14365 GetSystemInfo(&sysInfo);
14366 }
14367 switch (sysInfo.wProcessorArchitecture) {
14368 case PROCESSOR_ARCHITECTURE_AMD64:
14369 result = fplCPUArchType_x86_64;
14370 break;
14371 case PROCESSOR_ARCHITECTURE_IA64:
14372 result = fplCPUArchType_x64;
14373 break;
14374 case PROCESSOR_ARCHITECTURE_ARM:
14375 result = fplCPUArchType_Arm32;
14376 break;
14377 case FPL__WIN32_PROCESSOR_ARCHITECTURE_ARM64:
14378 result = fplCPUArchType_Arm64;
14379 break;
14380 case PROCESSOR_ARCHITECTURE_UNKNOWN:
14381 result = fplCPUArchType_Unknown;
14382 break;
14383 case PROCESSOR_ARCHITECTURE_INTEL:
14384 default:
14385 result = fplCPUArchType_x86;
14386 break;
14387 }
14388 return(result);
14389}
14390
14391#define FPL__FUNC_WIN32_KERNEL32_GetPhysicallyInstalledSystemMemory(name) BOOL WINAPI name(PULONGLONG TotalMemoryInKilobytes)
14392typedef FPL__FUNC_WIN32_KERNEL32_GetPhysicallyInstalledSystemMemory(fpl__win32_kernel_func_GetPhysicallyInstalledSystemMemory);
14394 FPL__CheckArgumentNull(outInfos, false);
14395 bool result = false;
14396
14397 HMODULE kernel32lib = LoadLibraryA("kernel32.dll");
14398 if (kernel32lib == fpl_null) {
14399 return false;
14400 }
14401 fpl__win32_kernel_func_GetPhysicallyInstalledSystemMemory *getPhysicallyInstalledSystemMemory = (fpl__win32_kernel_func_GetPhysicallyInstalledSystemMemory *)(void *)GetProcAddress(kernel32lib, "GetPhysicallyInstalledSystemMemory");
14402 FreeLibrary(kernel32lib);
14403
14404 ULONGLONG installedMemorySize = 0;
14405 if (getPhysicallyInstalledSystemMemory != fpl_null) {
14406 getPhysicallyInstalledSystemMemory(&installedMemorySize);
14407 }
14408
14409 SYSTEM_INFO systemInfo = fplZeroInit;
14410 GetSystemInfo(&systemInfo);
14411
14412 MEMORYSTATUSEX statex = fplZeroInit;
14413 statex.dwLength = sizeof(statex);
14414
14415 if (GlobalMemoryStatusEx(&statex)) {
14416 fplClearStruct(outInfos);
14417 outInfos->installedPhysicalSize = installedMemorySize * 1024ull;
14418 outInfos->totalPhysicalSize = statex.ullTotalPhys;
14419 outInfos->freePhysicalSize = statex.ullAvailPhys;
14420 outInfos->totalCacheSize = statex.ullTotalVirtual;
14421 outInfos->freeCacheSize = statex.ullAvailVirtual;
14422 outInfos->pageSize = systemInfo.dwPageSize;
14423 if (outInfos->pageSize > 0) {
14424 outInfos->totalPageCount = statex.ullTotalPageFile / outInfos->pageSize;
14425 outInfos->freePageCount = statex.ullAvailPageFile / outInfos->pageSize;
14426 }
14427 result = true;
14428 }
14429 return(result);
14430}
14431
14432//
14433// Win32 Threading
14434//
14435fpl_internal DWORD WINAPI fpl__Win32ThreadProc(void *data) {
14436 fplThreadHandle *thread = (fplThreadHandle *)data;
14437 fplAssert(thread != fpl_null);
14438 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Running);
14439
14440 fplThreadParameters parameters = thread->parameters;
14441
14442
14443
14444 if (parameters.runFunc != fpl_null) {
14445 parameters.runFunc(thread, parameters.userData);
14446 }
14447
14448 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopping);
14449 HANDLE handle = thread->internalHandle.win32ThreadHandle;
14450 if (handle != fpl_null) {
14451 CloseHandle(handle);
14452 }
14453 thread->isValid = false;
14454 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
14455 ExitThread(0);
14456}
14457
14459 DWORD threadId = GetCurrentThreadId();
14460 uint32_t result = (uint32_t)threadId;
14461 return(result);
14462}
14463
14464fpl_internal bool fpl__Win32SetThreadPriority(HANDLE threadHandle, const fplThreadPriority newPriority) {
14465 int win32Priority = 0;
14466 switch (newPriority) {
14468 win32Priority = THREAD_PRIORITY_IDLE;
14469 break;
14471 win32Priority = THREAD_PRIORITY_LOWEST;
14472 break;
14474 win32Priority = THREAD_PRIORITY_NORMAL;
14475 break;
14477 win32Priority = THREAD_PRIORITY_HIGHEST;
14478 break;
14480 win32Priority = THREAD_PRIORITY_TIME_CRITICAL;
14481 break;
14482 default:
14483 FPL__ERROR("Threading", "The thread priority %d is not supported", newPriority);
14484 return(false);
14485 }
14486 bool result = SetThreadPriority(threadHandle, win32Priority) == TRUE;
14487 return(result);
14488}
14489
14491 FPL__CheckArgumentNull(parameters, fpl_null);
14492 FPL__CheckArgumentNull(parameters->runFunc, fpl_null);
14493 fplThreadHandle *result = fpl_null;
14494 fplThreadHandle *thread = fpl__GetFreeThread();
14495 if (thread != fpl_null) {
14496 DWORD creationFlags = 0;
14497 DWORD threadId = 0;
14498 SIZE_T stackSize = parameters->stackSize;
14499 thread->parameters = *parameters;
14501 HANDLE handle = CreateThread(fpl_null, stackSize, fpl__Win32ThreadProc, thread, creationFlags, &threadId);
14502 if (handle != fpl_null) {
14503 fpl__Win32SetThreadPriority(handle, thread->parameters.priority);
14504 thread->isValid = true;
14505 thread->id = threadId;
14506 thread->internalHandle.win32ThreadHandle = handle;
14507 result = thread;
14508 } else {
14509 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating thread, error code: %d", GetLastError());
14510 }
14511 } else {
14512 FPL__ERROR(FPL__MODULE_THREADING, "All %d threads are in use, you cannot create until you free one", FPL_MAX_THREAD_COUNT);
14513 }
14514 return(result);
14515}
14516
14518 FPL__CheckArgumentNull(runFunc, fpl_null);
14519 fplThreadParameters parameters = fplZeroInit;
14521 parameters.runFunc = runFunc;
14522 parameters.userData = data;
14523 fplThreadHandle *result = fplThreadCreateWithParameters(&parameters);
14524 return(result);
14525}
14526
14527fpl_internal fplThreadPriority fpl__Win32MapNativeThreadPriority(const int win32ThreadPriority) {
14528 switch (win32ThreadPriority) {
14529 case THREAD_PRIORITY_IDLE:
14531 case THREAD_PRIORITY_LOWEST:
14532 case THREAD_PRIORITY_BELOW_NORMAL:
14533 return fplThreadPriority_Low;
14534 case THREAD_PRIORITY_NORMAL:
14536 case THREAD_PRIORITY_ABOVE_NORMAL:
14537 case THREAD_PRIORITY_HIGHEST:
14539 case THREAD_PRIORITY_TIME_CRITICAL:
14541 default:
14543 }
14544}
14545
14547 FPL__CheckArgumentNull(thread, fplThreadPriority_Unknown);
14549 if (thread->isValid && thread->internalHandle.win32ThreadHandle != fpl_null) {
14550 HANDLE threadHandle = thread->internalHandle.win32ThreadHandle;
14551 int win32ThreadPriority = GetThreadPriority(threadHandle);
14552 result = fpl__Win32MapNativeThreadPriority(win32ThreadPriority);
14553 }
14554 return(result);
14555}
14556
14558 FPL__CheckArgumentNull(thread, false);
14559 bool result = false;
14560 if (thread->isValid && thread->internalHandle.win32ThreadHandle != fpl_null) {
14561 HANDLE threadHandle = thread->internalHandle.win32ThreadHandle;
14562 result = fpl__Win32SetThreadPriority(threadHandle, newPriority);
14563 }
14564 return(result);
14565}
14566
14567fpl_platform_api void fplThreadSleep(const uint32_t milliseconds) {
14568 Sleep((DWORD)milliseconds);
14569}
14570
14572 YieldProcessor();
14573 return(true);
14574}
14575
14577 FPL__CheckArgumentNull(thread, false);
14578 fplThreadState state = fplGetThreadState(thread);
14579 if (thread->isValid && (state != fplThreadState_Stopped && state != fplThreadState_Stopping)) {
14580 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopping);
14581 HANDLE handle = thread->internalHandle.win32ThreadHandle;
14582 if (handle != fpl_null) {
14583 TerminateThread(handle, 0);
14584 CloseHandle(handle);
14585 }
14586 thread->isValid = false;
14587 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
14588 return true;
14589 } else {
14590 return false;
14591 }
14592}
14593
14595 FPL__CheckArgumentNull(thread, false);
14596 bool result;
14598 if (thread->internalHandle.win32ThreadHandle == fpl_null) {
14599 FPL__ERROR(FPL__MODULE_THREADING, "Win32 thread handle are not allowed to be null");
14600 return false;
14601 }
14602 HANDLE handle = thread->internalHandle.win32ThreadHandle;
14603 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
14604 result = (WaitForSingleObject(handle, t) == WAIT_OBJECT_0);
14605 } else {
14606 result = true;
14607 }
14608 return(result);
14609}
14610
14611fpl_platform_api bool fplThreadWaitForAll(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
14612 bool result = fpl__Win32ThreadWaitForMultiple(threads, count, stride, timeout, true);
14613 return(result);
14614}
14615
14616fpl_platform_api bool fplThreadWaitForAny(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
14617 bool result = fpl__Win32ThreadWaitForMultiple(threads, count, stride, timeout, false);
14618 return(result);
14619}
14620
14622 FPL__CheckArgumentNull(mutex, false);
14623 if (mutex->isValid) {
14624 FPL__ERROR(FPL__MODULE_THREADING, "Mutex '%p' is already initialized", mutex);
14625 return false;
14626 }
14627 fplClearStruct(mutex);
14628 mutex->isValid = true;
14629 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
14630 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
14631 InitializeCriticalSection(critSection);
14632 return true;
14633}
14634
14636 FPL__CheckArgumentNullNoRet(mutex);
14637 if (mutex->isValid) {
14638 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
14639 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
14640 DeleteCriticalSection(critSection);
14641 fplClearStruct(mutex);
14642 }
14643}
14644
14646 FPL__CheckArgumentNull(mutex, false);
14647 if (!mutex->isValid) {
14648 FPL__ERROR(FPL__MODULE_THREADING, "Mutex parameter must be valid");
14649 return false;
14650 }
14651 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
14652 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
14653 EnterCriticalSection(critSection);
14654 return true;
14655}
14656
14658 FPL__CheckArgumentNull(mutex, false);
14659 if (!mutex->isValid) {
14660 FPL__ERROR(FPL__MODULE_THREADING, "Mutex parameter must be valid");
14661 return false;
14662 }
14663 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
14664 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
14665 bool result = TryEnterCriticalSection(critSection) == TRUE;
14666 return(result);
14667}
14668
14670 FPL__CheckArgumentNull(mutex, false);
14671 if (!mutex->isValid) {
14672 FPL__ERROR(FPL__MODULE_THREADING, "Mutex parameter must be valid");
14673 return false;
14674 }
14675 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
14676 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
14677 LeaveCriticalSection(critSection);
14678 return true;
14679}
14680
14681fpl_platform_api bool fplSignalInit(fplSignalHandle *signal, const fplSignalValue initialValue) {
14682 FPL__CheckArgumentNull(signal, false);
14683 if (signal->isValid) {
14684 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is already initialized", signal);
14685 return false;
14686 }
14687 HANDLE handle = CreateEventA(fpl_null, FALSE, (initialValue == fplSignalValue_Set) ? TRUE : FALSE, fpl_null);
14688 if (handle == fpl_null) {
14689 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating signal (Win32 event): %d", GetLastError());
14690 return false;
14691 }
14692 fplClearStruct(signal);
14693 signal->isValid = true;
14694 signal->internalHandle.win32EventHandle = handle;
14695 return(true);
14696}
14697
14699 FPL__CheckArgumentNullNoRet(signal);
14700 if (signal->internalHandle.win32EventHandle != fpl_null) {
14701 HANDLE handle = signal->internalHandle.win32EventHandle;
14702 CloseHandle(handle);
14703 fplClearStruct(signal);
14704 }
14705}
14706
14708 FPL__CheckArgumentNull(signal, false);
14709 if (signal->internalHandle.win32EventHandle == fpl_null) {
14710 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle are not allowed to be null");
14711 return false;
14712 }
14713 HANDLE handle = signal->internalHandle.win32EventHandle;
14714 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
14715 bool result = (WaitForSingleObject(handle, t) == WAIT_OBJECT_0);
14716 return(result);
14717}
14718
14719fpl_platform_api bool fplSignalWaitForAll(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
14720 bool result = fpl__Win32SignalWaitForMultiple(signals, count, stride, timeout, true);
14721 return(result);
14722}
14723
14724fpl_platform_api bool fplSignalWaitForAny(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
14725 bool result = fpl__Win32SignalWaitForMultiple(signals, count, stride, timeout, false);
14726 return(result);
14727}
14728
14730 FPL__CheckArgumentNull(signal, false);
14731 if (signal->internalHandle.win32EventHandle == fpl_null) {
14732 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle are not allowed to be null");
14733 return false;
14734 }
14735 HANDLE handle = signal->internalHandle.win32EventHandle;
14736 bool result = SetEvent(handle) == TRUE;
14737 return(result);
14738}
14739
14741 FPL__CheckArgumentNull(signal, false);
14742 if (signal->internalHandle.win32EventHandle == fpl_null) {
14743 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle are not allowed to be null");
14744 return false;
14745 }
14746 HANDLE handle = signal->internalHandle.win32EventHandle;
14747 bool result = ResetEvent(handle) == TRUE;
14748 return(result);
14749}
14750
14752 FPL__CheckArgumentNull(condition, false);
14753 fplClearStruct(condition);
14754 fplAssert(sizeof(condition->internalHandle.win32Condition) == sizeof(CONDITION_VARIABLE));
14755 CONDITION_VARIABLE *condVar = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
14756 InitializeConditionVariable(condVar);
14757 condition->isValid = true;
14758 return true;
14759}
14760
14762 FPL__CheckArgumentNullNoRet(condition);
14763 if (condition->isValid) {
14764 fplClearStruct(condition);
14765 }
14766}
14767
14769 FPL__CheckArgumentNull(condition, false);
14770 FPL__CheckArgumentNull(mutex, false);
14771 if (!condition->isValid) {
14772 FPL__ERROR(FPL__MODULE_THREADING, "Condition '%p' is not valid", condition);
14773 return false;
14774 }
14775 if (!mutex->isValid) {
14776 FPL__ERROR(FPL__MODULE_THREADING, "Mutex '%p' is not valid", mutex);
14777 return false;
14778 }
14779 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
14780 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
14781 CONDITION_VARIABLE *condVar = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
14782 bool result = SleepConditionVariableCS(condVar, critSection, t) != 0;
14783 return(result);
14784}
14785
14787 FPL__CheckArgumentNull(condition, false);
14788 if (!condition->isValid) {
14789 FPL__ERROR(FPL__MODULE_THREADING, "Condition '%p' is not valid", condition);
14790 return false;
14791 }
14792 CONDITION_VARIABLE *critSection = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
14793 WakeConditionVariable(critSection);
14794 return true;
14795}
14796
14798 FPL__CheckArgumentNull(condition, false);
14799 if (!condition->isValid) {
14800 FPL__ERROR(FPL__MODULE_THREADING, "Condition '%p' is not valid", condition);
14801 return false;
14802 }
14803 CONDITION_VARIABLE *critSection = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
14804 WakeAllConditionVariable(critSection);
14805 return true;
14806}
14807
14808fpl_platform_api bool fplSemaphoreInit(fplSemaphoreHandle *semaphore, const uint32_t initialValue) {
14809 FPL__CheckArgumentNull(semaphore, false);
14810 FPL__CheckArgumentMax(initialValue, INT32_MAX, false);
14811 if (semaphore->isValid) {
14812 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is already initialized", semaphore);
14813 return false;
14814 }
14815 HANDLE handle = CreateSemaphoreA(fpl_null, (LONG)initialValue, INT32_MAX, fpl_null);
14816 if (handle == fpl_null) {
14817 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating semaphore");
14818 return false;
14819 }
14820 fplClearStruct(semaphore);
14821 semaphore->isValid = true;
14822 semaphore->internalHandle.win32.handle = handle;
14823 semaphore->internalHandle.win32.value = (int32_t)initialValue;
14824 return true;
14825}
14826
14828 FPL__CheckArgumentNullNoRet(semaphore);
14829 if (semaphore->isValid) {
14830 CloseHandle(semaphore->internalHandle.win32.handle);
14831 fplClearStruct(semaphore);
14832 }
14833}
14834
14836 FPL__CheckArgumentNull(semaphore, false);
14837 if (!semaphore->isValid) {
14838 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
14839 return false;
14840 }
14841 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
14842 bool result = false;
14843 if (WaitForSingleObject(semaphore->internalHandle.win32.handle, timeout) == WAIT_OBJECT_0) {
14844 fplAtomicFetchAndAddS32(&semaphore->internalHandle.win32.value, -1);
14845 result = true;
14846 }
14847 return(result);
14848}
14849
14851 FPL__CheckArgumentNull(semaphore, false);
14852 if (!semaphore->isValid) {
14853 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
14854 return false;
14855 }
14856 bool result = false;
14857 if (WaitForSingleObject(semaphore->internalHandle.win32.handle, 0) == WAIT_OBJECT_0) {
14858 fplAtomicFetchAndAddS32(&semaphore->internalHandle.win32.value, -1);
14859 result = true;
14860 }
14861 return(result);
14862}
14863
14865 FPL__CheckArgumentNull(semaphore, false);
14866 if (!semaphore->isValid) {
14867 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
14868 return false;
14869 }
14870 int32_t result = fplAtomicLoadS32(&semaphore->internalHandle.win32.value);
14871 return(result);
14872}
14873
14875 FPL__CheckArgumentNull(semaphore, false);
14876 if (!semaphore->isValid) {
14877 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
14878 return false;
14879 }
14880 bool result = true;
14881 int32_t prevValue = fplAtomicFetchAndAddS32(&semaphore->internalHandle.win32.value, 1);
14882 if (ReleaseSemaphore(semaphore->internalHandle.win32.handle, 1, fpl_null) == FALSE) {
14883 // Restore value when it fails
14884 FPL__ERROR(FPL__MODULE_THREADING, "Failed releasing the semaphore '%p'", semaphore);
14885 fplAtomicStoreS32(&semaphore->internalHandle.win32.value, prevValue);
14886 result = false;
14887 }
14888 return(result);
14889}
14890
14891//
14892// Win32 Console
14893//
14894fpl_platform_api void fplConsoleOut(const char *text) {
14895 DWORD charsToWrite = (DWORD)fplGetStringLength(text);
14896 DWORD writtenChars = 0;
14897 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
14898 wchar_t wideBuffer[FPL_MAX_BUFFER_LENGTH];
14899 fplUTF8StringToWideString(text, charsToWrite, wideBuffer, fplArrayCount(wideBuffer));
14900 WriteConsoleW(handle, wideBuffer, charsToWrite, &writtenChars, fpl_null);
14901}
14902
14903fpl_platform_api void fplConsoleError(const char *text) {
14904 DWORD charsToWrite = (DWORD)fplGetStringLength(text);
14905 DWORD writtenChars = 0;
14906 HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
14907 wchar_t wideBuffer[FPL_MAX_BUFFER_LENGTH];
14908 fplUTF8StringToWideString(text, charsToWrite, wideBuffer, fplArrayCount(wideBuffer));
14909 WriteConsoleW(handle, wideBuffer, charsToWrite, &writtenChars, fpl_null);
14910}
14911
14913 HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
14914 DWORD savedMode;
14915 GetConsoleMode(handle, &savedMode);
14916 SetConsoleMode(handle, ENABLE_PROCESSED_INPUT);
14917 char result = 0;
14918 if (WaitForSingleObject(handle, INFINITE) == WAIT_OBJECT_0) {
14919 DWORD charsRead = 0;
14920 char inputBuffer[2] = fplZeroInit;
14921 if (ReadFile(handle, inputBuffer, 1, &charsRead, fpl_null) != 0) {
14922 result = inputBuffer[0];
14923 }
14924 }
14925 SetConsoleMode(handle, savedMode);
14926 return (result);
14927}
14928
14929//
14930// Win32 Memory
14931//
14932fpl_platform_api void *fplMemoryAllocate(const size_t size) {
14933 FPL__CheckArgumentZero(size, fpl_null);
14934 void *result = VirtualAlloc(fpl_null, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
14935 if (result == fpl_null) {
14936 FPL__ERROR(FPL__MODULE_MEMORY, "Failed allocating memory of %xu bytes", size);
14937 }
14938 return(result);
14939}
14940
14941fpl_platform_api void fplMemoryFree(void *ptr) {
14942 FPL__CheckArgumentNullNoRet(ptr);
14943 VirtualFree(ptr, 0, MEM_RELEASE);
14944}
14945
14946//
14947// Win32 Files
14948//
14949fpl_internal const uint64_t FPL__WIN32_TICKS_PER_SEC = 10000000ULL;
14950fpl_internal const uint64_t FPL__WIN32_UNIX_EPOCH_DIFFERENCE = 11644473600ULL;
14951
14952fpl_internal fplFileTimeStamp fpl__Win32ConvertFileTimeToUnixTimestamp(const FILETIME *fileTime) {
14953 // Ticks are defined in 100 ns = 10000000 secs
14954 // Windows ticks starts at 1601-01-01T00:00:00Z
14955 // Unix secs starts at 1970-01-01T00:00:00Z
14956 fplFileTimeStamp result = 0;
14957 if (fileTime != fpl_null && (fileTime->dwLowDateTime > 0 || fileTime->dwHighDateTime > 0)) {
14958 // Convert to SYSTEMTIME and remove milliseconds
14959 SYSTEMTIME sysTime;
14960 FileTimeToSystemTime(fileTime, &sysTime);
14961 sysTime.wMilliseconds = 0; // Really important, because unix-timestamps does not support milliseconds
14962
14963 // Reconvert to FILETIME to account for removed milliseconds
14964 FILETIME withoutMSecs;
14965 SystemTimeToFileTime(&sysTime, &withoutMSecs);
14966
14967 // Convert to large integer so we can access U64 directly
14968 ULARGE_INTEGER ticks;
14969 ticks.LowPart = withoutMSecs.dwLowDateTime;
14970 ticks.HighPart = withoutMSecs.dwHighDateTime;
14971
14972 // Final conversion from ticks to unix-timestamp
14973 result = (ticks.QuadPart / FPL__WIN32_TICKS_PER_SEC) - FPL__WIN32_UNIX_EPOCH_DIFFERENCE;
14974 }
14975 return(result);
14976}
14977
14978fpl_internal FILETIME fpl__Win32ConvertUnixTimestampToFileTime(const fplFileTimeStamp unixTimeStamp) {
14979 // Ticks are defined in 100 ns = 10000000 secs
14980 // 100 ns = milliseconds * 10000
14981 // Windows ticks starts at 1601-01-01T00:00:00Z
14982 // Unix secs starts at 1970-01-01T00:00:00Z
14983 if (unixTimeStamp > 0) {
14984 uint64_t ticks = (unixTimeStamp + FPL__WIN32_UNIX_EPOCH_DIFFERENCE) * FPL__WIN32_TICKS_PER_SEC;
14985 FILETIME result;
14986 result.dwLowDateTime = (DWORD)ticks;
14987 result.dwHighDateTime = ticks >> 32;
14988 return(result);
14989 }
14990 FILETIME empty = fplZeroInit;
14991 return(empty);
14992}
14993
14994fpl_platform_api bool fplFileOpenBinary(const char *filePath, fplFileHandle *outHandle) {
14995 FPL__CheckArgumentNull(outHandle, false);
14996 if (filePath != fpl_null) {
14997 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
14998 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
14999 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
15000 if (win32FileHandle != INVALID_HANDLE_VALUE) {
15001 fplClearStruct(outHandle);
15002 outHandle->isValid = true;
15003 outHandle->internalHandle.win32FileHandle = (void *)win32FileHandle;
15004 return true;
15005 }
15006 }
15007 return false;
15008}
15009
15010fpl_platform_api bool fplFileCreateBinary(const char *filePath, fplFileHandle *outHandle) {
15011 FPL__CheckArgumentNull(outHandle, false);
15012 if (filePath != fpl_null) {
15013 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
15014 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
15015 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_WRITE, FILE_SHARE_WRITE, fpl_null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, fpl_null);
15016 if (win32FileHandle != INVALID_HANDLE_VALUE) {
15017 fplClearStruct(outHandle);
15018 outHandle->isValid = true;
15019 outHandle->internalHandle.win32FileHandle = (void *)win32FileHandle;
15020 return true;
15021 }
15022 }
15023 return false;
15024}
15025
15026fpl_platform_api uint32_t fplFileReadBlock32(const fplFileHandle *fileHandle, const uint32_t sizeToRead, void *targetBuffer, const uint32_t maxTargetBufferSize) {
15027 FPL__CheckArgumentNull(fileHandle, 0);
15028 FPL__CheckArgumentZero(sizeToRead, 0);
15029 FPL__CheckArgumentNull(targetBuffer, 0);
15030 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
15031 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for reading");
15032 return 0;
15033 }
15034 uint32_t result = 0;
15035 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
15036 DWORD bytesRead = 0;
15037 if (ReadFile(win32FileHandle, targetBuffer, (DWORD)sizeToRead, &bytesRead, fpl_null) == TRUE) {
15038 result = bytesRead;
15039 }
15040 return(result);
15041}
15042
15043fpl_platform_api uint64_t fplFileReadBlock64(const fplFileHandle *fileHandle, const uint64_t sizeToRead, void *targetBuffer, const uint64_t maxTargetBufferSize) {
15044 FPL__CheckArgumentNull(fileHandle, 0);
15045 FPL__CheckArgumentZero(sizeToRead, 0);
15046 FPL__CheckArgumentNull(targetBuffer, 0);
15047 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
15048 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for reading");
15049 return 0;
15050 }
15051 // @NOTE(final): There is no ReadFile64 function in win32, so we have to read it in chunks
15052 uint64_t result = 0;
15053 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
15054 uint64_t remainingSize = sizeToRead;
15055 uint64_t bufferPos = 0;
15056 const uint64_t MaxDWORD = (uint64_t)(DWORD)-1;
15057 while (remainingSize > 0) {
15058 DWORD bytesRead = 0;
15059 uint8_t *target = (uint8_t *)targetBuffer + bufferPos;
15060 uint64_t size = fplMin(remainingSize, MaxDWORD);
15061 fplAssert(size <= MaxDWORD);
15062 if (ReadFile(win32FileHandle, target, (DWORD)size, &bytesRead, fpl_null) == TRUE) {
15063 result = bytesRead;
15064 } else {
15065 break;
15066 }
15067 remainingSize -= bytesRead;
15068 bufferPos += bytesRead;
15069 }
15070 return(result);
15071}
15072
15073fpl_platform_api uint32_t fplFileWriteBlock32(const fplFileHandle *fileHandle, void *sourceBuffer, const uint32_t sourceSize) {
15074 FPL__CheckArgumentNull(fileHandle, 0);
15075 FPL__CheckArgumentZero(sourceSize, 0);
15076 FPL__CheckArgumentNull(sourceBuffer, 0);
15077 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
15078 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for writing");
15079 return 0;
15080 }
15081 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
15082 uint32_t result = 0;
15083 DWORD bytesWritten = 0;
15084 if (WriteFile(win32FileHandle, sourceBuffer, (DWORD)sourceSize, &bytesWritten, fpl_null) == TRUE) {
15085 result = bytesWritten;
15086 }
15087 return(result);
15088}
15089
15090fpl_platform_api uint64_t fplFileWriteBlock64(const fplFileHandle *fileHandle, void *sourceBuffer, const uint64_t sourceSize) {
15091 FPL__CheckArgumentNull(fileHandle, 0);
15092 FPL__CheckArgumentZero(sourceSize, 0);
15093 FPL__CheckArgumentNull(sourceBuffer, 0);
15094 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
15095 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for writing");
15096 return 0;
15097 }
15098 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
15099 uint64_t result = 0;
15100 uint64_t bufferPos = 0;
15101 uint64_t remainingSize = sourceSize;
15102 const uint64_t MaxDWORD = (uint64_t)(DWORD)-1;
15103 while (remainingSize > 0) {
15104 uint8_t *source = (uint8_t *)sourceBuffer + bufferPos;
15105 uint64_t size = fplMin(remainingSize, MaxDWORD);
15106 fplAssert(size <= MaxDWORD);
15107 DWORD bytesWritten = 0;
15108 if (WriteFile(win32FileHandle, source, (DWORD)size, &bytesWritten, fpl_null) == TRUE) {
15109 result = bytesWritten;
15110 } else {
15111 break;
15112 }
15113 remainingSize -= bytesWritten;
15114 bufferPos += bytesWritten;
15115 }
15116 return(result);
15117}
15118
15119fpl_platform_api uint32_t fplFileSetPosition32(const fplFileHandle *fileHandle, const int32_t position, const fplFilePositionMode mode) {
15120 FPL__CheckArgumentNull(fileHandle, 0);
15121 uint32_t result = 0;
15122 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15123 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15124 DWORD moveMethod = FILE_BEGIN;
15125 if (mode == fplFilePositionMode_Current) {
15126 moveMethod = FILE_CURRENT;
15127 } else if (mode == fplFilePositionMode_End) {
15128 moveMethod = FILE_END;
15129 }
15130 DWORD r = 0;
15131 r = SetFilePointer(win32FileHandle, (LONG)position, fpl_null, moveMethod);
15132 result = (uint32_t)r;
15133 }
15134 return(result);
15135}
15136
15137fpl_platform_api uint64_t fplFileSetPosition64(const fplFileHandle *fileHandle, const int64_t position, const fplFilePositionMode mode) {
15138 FPL__CheckArgumentNull(fileHandle, 0);
15139 uint64_t result = 0;
15140 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15141 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15142 DWORD moveMethod = FILE_BEGIN;
15143 if (mode == fplFilePositionMode_Current) {
15144 moveMethod = FILE_CURRENT;
15145 } else if (mode == fplFilePositionMode_End) {
15146 moveMethod = FILE_END;
15147 }
15148 LARGE_INTEGER r = fplZeroInit;
15149 LARGE_INTEGER li;
15150 li.QuadPart = position;
15151 if (SetFilePointerEx(win32FileHandle, li, &r, moveMethod) == TRUE) {
15152 result = (uint64_t)r.QuadPart;
15153 }
15154 }
15155 return(result);
15156}
15157
15158fpl_platform_api uint32_t fplFileGetPosition32(const fplFileHandle *fileHandle) {
15159 FPL__CheckArgumentNull(fileHandle, 0);
15160 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15161 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15162 DWORD filePosition = SetFilePointer(win32FileHandle, 0L, fpl_null, FILE_CURRENT);
15163 if (filePosition != INVALID_SET_FILE_POINTER) {
15164 return filePosition;
15165 }
15166 }
15167 return 0;
15168}
15169
15170fpl_platform_api uint64_t fplFileGetPosition64(const fplFileHandle *fileHandle) {
15171 FPL__CheckArgumentNull(fileHandle, 0);
15172 uint64_t result = 0;
15173 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15174 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15175 LARGE_INTEGER r = fplZeroInit;
15176 LARGE_INTEGER li;
15177 li.QuadPart = 0;
15178 if (SetFilePointerEx(win32FileHandle, li, &r, FILE_CURRENT) == TRUE) {
15179 result = (uint64_t)r.QuadPart;
15180 }
15181 }
15182 return 0;
15183}
15184
15186 FPL__CheckArgumentNull(fileHandle, false);
15187 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15188 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15189 bool result = FlushFileBuffers(win32FileHandle) == TRUE;
15190 return(result);
15191 }
15192 return(false);
15193}
15194
15196 if ((fileHandle != fpl_null) && (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE)) {
15197 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15198 CloseHandle(win32FileHandle);
15199 fplClearStruct(fileHandle);
15200 }
15201}
15202
15203fpl_platform_api uint32_t fplFileGetSizeFromPath32(const char *filePath) {
15204 uint32_t result = 0;
15205 if (filePath != fpl_null) {
15206 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
15207 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
15208 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
15209 if (win32FileHandle != INVALID_HANDLE_VALUE) {
15210 DWORD fileSize = GetFileSize(win32FileHandle, fpl_null);
15211 result = (uint32_t)fileSize;
15212 CloseHandle(win32FileHandle);
15213 }
15214 }
15215 return(result);
15216}
15217
15218fpl_platform_api uint64_t fplFileGetSizeFromPath64(const char *filePath) {
15219 uint64_t result = 0;
15220 if (filePath != fpl_null) {
15221 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
15222 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
15223 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
15224 if (win32FileHandle != INVALID_HANDLE_VALUE) {
15225 LARGE_INTEGER li = fplZeroInit;
15226 if (GetFileSizeEx(win32FileHandle, &li) == TRUE) {
15227 result = (uint64_t)li.QuadPart;
15228 }
15229 CloseHandle(win32FileHandle);
15230 }
15231 }
15232 return(result);
15233}
15234
15235fpl_platform_api uint32_t fplFileGetSizeFromHandle32(const fplFileHandle *fileHandle) {
15236 FPL__CheckArgumentNull(fileHandle, 0);
15237 uint32_t result = 0;
15238 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15239 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15240 DWORD fileSize = GetFileSize(win32FileHandle, fpl_null);
15241 result = (uint32_t)fileSize;
15242 }
15243 return(result);
15244}
15245
15246fpl_platform_api uint64_t fplFileGetSizeFromHandle64(const fplFileHandle *fileHandle) {
15247 FPL__CheckArgumentNull(fileHandle, 0);
15248 uint64_t result = 0;
15249 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15250 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15251 LARGE_INTEGER li = fplZeroInit;
15252 if (GetFileSizeEx(win32FileHandle, &li) == TRUE) {
15253 result = (uint64_t)li.QuadPart;
15254 }
15255 }
15256 return(result);
15257}
15258
15259fpl_platform_api bool fplFileGetTimestampsFromPath(const char *filePath, fplFileTimeStamps *outStamps) {
15260 FPL__CheckArgumentNull(outStamps, false);
15261 if (filePath != fpl_null) {
15262 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
15263 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
15264 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
15265 bool result = false;
15266 if (win32FileHandle != INVALID_HANDLE_VALUE) {
15267 FILETIME times[3];
15268 if (GetFileTime(win32FileHandle, &times[0], &times[1], &times[2]) == TRUE) {
15269 fplClearStruct(outStamps);
15270 outStamps->creationTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[0]);
15271 outStamps->lastAccessTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[1]);
15272 outStamps->lastModifyTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[2]);
15273 result = true;
15274 }
15275 CloseHandle(win32FileHandle);
15276 }
15277 return(result);
15278 }
15279 return(false);
15280}
15281
15283 FPL__CheckArgumentNull(fileHandle, 0);
15284 FPL__CheckArgumentNull(outStamps, 0);
15285 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15286 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
15287 FILETIME times[3];
15288 if (GetFileTime(win32FileHandle, &times[0], &times[1], &times[2]) == TRUE) {
15289 fplClearStruct(outStamps);
15290 outStamps->creationTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[0]);
15291 outStamps->lastAccessTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[1]);
15292 outStamps->lastModifyTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[2]);
15293 return(true);
15294 }
15295 }
15296 return(false);
15297}
15298
15299fpl_platform_api bool fplFileExists(const char *filePath) {
15300 bool result = false;
15301 if (filePath != fpl_null) {
15302 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
15303 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
15304 WIN32_FIND_DATAW findData;
15305 HANDLE searchHandle = FindFirstFileW(filePathWide, &findData);
15306 if (searchHandle != INVALID_HANDLE_VALUE) {
15307 result = !(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
15308 FindClose(searchHandle);
15309 }
15310 }
15311 return(result);
15312}
15313
15314fpl_platform_api bool fplFileCopy(const char *sourceFilePath, const char *targetFilePath, const bool overwrite) {
15315 FPL__CheckArgumentNull(sourceFilePath, false);
15316 FPL__CheckArgumentNull(targetFilePath, false);
15317 wchar_t sourceFilePathWide[FPL_MAX_PATH_LENGTH];
15318 wchar_t targetFilePathWide[FPL_MAX_PATH_LENGTH];
15319 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), sourceFilePathWide, fplArrayCount(sourceFilePathWide));
15320 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), targetFilePathWide, fplArrayCount(targetFilePathWide));
15321 bool result = (CopyFileW(sourceFilePathWide, targetFilePathWide, !overwrite) == TRUE);
15322 return(result);
15323}
15324
15325fpl_platform_api bool fplFileMove(const char *sourceFilePath, const char *targetFilePath) {
15326 FPL__CheckArgumentNull(sourceFilePath, false);
15327 FPL__CheckArgumentNull(targetFilePath, false);
15328 wchar_t sourceFilePathWide[FPL_MAX_PATH_LENGTH];
15329 wchar_t targetFilePathWide[FPL_MAX_PATH_LENGTH];
15330 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), sourceFilePathWide, fplArrayCount(sourceFilePathWide));
15331 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), targetFilePathWide, fplArrayCount(targetFilePathWide));
15332 bool result = (MoveFileW(sourceFilePathWide, targetFilePathWide) == TRUE);
15333 return(result);
15334}
15335
15336fpl_platform_api bool fplFileDelete(const char *filePath) {
15337 FPL__CheckArgumentNull(filePath, false);
15338 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
15339 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
15340 bool result = (DeleteFileW(filePathWide) == TRUE);
15341 return(result);
15342}
15343
15344fpl_platform_api bool fplDirectoryExists(const char *path) {
15345 bool result = false;
15346 if (path != fpl_null) {
15347 wchar_t pathWide[FPL_MAX_PATH_LENGTH];
15348 fplUTF8StringToWideString(path, fplGetStringLength(path), pathWide, fplArrayCount(pathWide));
15349 WIN32_FIND_DATAW findData;
15350 HANDLE searchHandle = FindFirstFileW(pathWide, &findData);
15351 if (searchHandle != INVALID_HANDLE_VALUE) {
15352 result = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0;
15353 FindClose(searchHandle);
15354 }
15355 }
15356 return(result);
15357}
15358
15359fpl_platform_api bool fplDirectoriesCreate(const char *path) {
15360 FPL__CheckArgumentNull(path, false);
15361 wchar_t pathWide[FPL_MAX_PATH_LENGTH];
15362 fplUTF8StringToWideString(path, fplGetStringLength(path), pathWide, fplArrayCount(pathWide));
15363 bool result = CreateDirectoryW(pathWide, fpl_null) > 0;
15364 return(result);
15365}
15366fpl_platform_api bool fplDirectoryRemove(const char *path) {
15367 FPL__CheckArgumentNull(path, false);
15368 wchar_t pathWide[FPL_MAX_PATH_LENGTH];
15369 fplUTF8StringToWideString(path, fplGetStringLength(path), pathWide, fplArrayCount(pathWide));
15370 bool result = RemoveDirectoryW(pathWide) > 0;
15371 return(result);
15372}
15373fpl_internal void fpl__Win32FillFileEntry(const char *rootPath, const WIN32_FIND_DATAW *findData, fplFileEntry *entry) {
15374 fplAssert(findData != fpl_null);
15375 fplAssert(entry != fpl_null);
15376 fplWideStringToUTF8String(findData->cFileName, lstrlenW(findData->cFileName), entry->name, fplArrayCount(entry->name));
15378 if (findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
15380 } else if (
15381 (findData->dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
15382 (findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
15383 (findData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) ||
15384 (findData->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ||
15385 (findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) {
15386 entry->type = fplFileEntryType_File;
15387 }
15388
15389 // @TODO(final/Win32): Win32 Read ACL for full permission detection!
15391 entry->permissions.umask = 0;
15392 if (findData->dwFileAttributes & FILE_ATTRIBUTE_NORMAL) {
15394 } else {
15395 if (findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
15397 }
15398 if (findData->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
15400 }
15401 if (findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
15403 }
15407 if ((findData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) || (findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) {
15409 }
15410 }
15411 if (entry->type == fplFileEntryType_File) {
15412 ULARGE_INTEGER ul;
15413 ul.LowPart = findData->nFileSizeLow;
15414 ul.HighPart = findData->nFileSizeHigh;
15415 entry->size = (size_t)ul.QuadPart;
15416 } else {
15417 entry->size = 0;
15418 }
15419 entry->timeStamps.creationTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&findData->ftCreationTime);
15420 entry->timeStamps.lastAccessTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&findData->ftLastAccessTime);
15421 entry->timeStamps.lastModifyTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&findData->ftLastWriteTime);
15422}
15423fpl_platform_api bool fplDirectoryListBegin(const char *path, const char *filter, fplFileEntry *entry) {
15424 FPL__CheckArgumentNull(path, false);
15425 FPL__CheckArgumentNull(entry, false);
15426 if (fplGetStringLength(filter) == 0) {
15427 filter = "*";
15428 }
15429 char pathAndFilter[MAX_PATH + 1] = fplZeroInit;
15430 fplCopyString(path, pathAndFilter, fplArrayCount(pathAndFilter));
15431 fplEnforcePathSeparatorLen(pathAndFilter, fplArrayCount(pathAndFilter));
15432 fplStringAppend(filter, pathAndFilter, fplArrayCount(pathAndFilter));
15433 wchar_t pathAndFilterWide[MAX_PATH + 1];
15434 fplUTF8StringToWideString(pathAndFilter, fplGetStringLength(pathAndFilter), pathAndFilterWide, fplArrayCount(pathAndFilterWide));
15435 WIN32_FIND_DATAW findData;
15436 HANDLE searchHandle = FindFirstFileW(pathAndFilterWide, &findData);
15437 bool result = false;
15438 if (searchHandle != INVALID_HANDLE_VALUE) {
15439 fplClearStruct(entry);
15440 entry->internalHandle.win32FileHandle = searchHandle;
15441 entry->internalRoot.rootPath = path;
15442 entry->internalRoot.filter = filter;
15443 bool foundFirst = true;
15444 while (foundFirst) {
15445 if (lstrcmpW(findData.cFileName, L".") == 0 || lstrcmpW(findData.cFileName, L"..") == 0) {
15446 foundFirst = FindNextFileW(searchHandle, &findData) == TRUE;
15447 } else {
15448 result = true;
15449 fpl__Win32FillFileEntry(path, &findData, entry);
15450 break;
15451 }
15452 }
15453 }
15454 return(result);
15455}
15457 FPL__CheckArgumentNull(entry, false);
15458 bool result = false;
15459 if (entry->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15460 HANDLE searchHandle = entry->internalHandle.win32FileHandle;
15461 WIN32_FIND_DATAW findData;
15462 bool foundNext;
15463 do {
15464 foundNext = FindNextFileW(searchHandle, &findData) == TRUE;
15465 if (foundNext) {
15466 if (lstrcmpW(findData.cFileName, L".") == 0 || lstrcmpW(findData.cFileName, L"..") == 0) {
15467 continue;
15468 }
15469 fpl__Win32FillFileEntry(entry->internalRoot.rootPath, &findData, entry);
15470 result = true;
15471 break;
15472 }
15473 } while (foundNext);
15474 }
15475 return(result);
15476}
15478 FPL__CheckArgumentNullNoRet(entry);
15479 if (entry->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
15480 HANDLE searchHandle = entry->internalHandle.win32FileHandle;
15481 FindClose(searchHandle);
15482 fplClearStruct(entry);
15483 }
15484}
15485
15486//
15487// Win32 Path/Directories
15488//
15489fpl_platform_api size_t fplGetExecutableFilePath(char *destPath, const size_t maxDestLen) {
15490 wchar_t modulePath[MAX_PATH];
15491 GetModuleFileNameW(fpl_null, modulePath, MAX_PATH);
15492 size_t modulePathLen = lstrlenW(modulePath);
15493 size_t result = fplWideStringToUTF8String(modulePath, modulePathLen, destPath, maxDestLen);
15494 return(result);
15495}
15496
15497fpl_platform_api size_t fplGetHomePath(char *destPath, const size_t maxDestLen) {
15498 FPL__CheckPlatform(0);
15499 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
15500 wchar_t homePath[MAX_PATH];
15501 wapi->shell.SHGetFolderPathW(fpl_null, CSIDL_PROFILE, fpl_null, 0, homePath);
15502 size_t homePathLen = lstrlenW(homePath);
15503 size_t result = fplWideStringToUTF8String(homePath, homePathLen, destPath, maxDestLen);
15504 return(result);
15505}
15506
15507//
15508// Win32 Timings
15509//
15511 const fpl__Win32InitState *initState = &fpl__global__InitState.win32;
15512 fplTimestamp result = fplZeroInit;
15513 if (initState->qpf.QuadPart > 0) {
15514 LARGE_INTEGER time;
15515 QueryPerformanceCounter(&time);
15516 result.win32.qpc.QuadPart = time.QuadPart;
15517 } else {
15518 result.win32.ticks = GetTickCount64();
15519 }
15520 return(result);
15521}
15522
15524 const fpl__Win32InitState *initState = &fpl__global__InitState.win32;
15525 fplSeconds result;
15526 LARGE_INTEGER freq = initState->qpf;
15527 if (freq.QuadPart > 0) {
15528 uint64_t delta = finish.win32.qpc.QuadPart - start.win32.qpc.QuadPart;
15529 result = (fplSeconds)(delta / (double)freq.QuadPart);
15530 } else {
15531 uint64_t delta = finish.win32.ticks - start.win32.ticks;
15532 result = (fplSeconds)(delta / 1000.0);
15533 }
15534 return(result);
15535}
15536
15538 fplMilliseconds result = (fplMilliseconds)GetTickCount64();
15539 return(result);
15540}
15541
15542//
15543// Win32 Strings
15544//
15545fpl_platform_api size_t fplWideStringToUTF8String(const wchar_t *wideSource, const size_t wideSourceLen, char *utf8Dest, const size_t maxUtf8DestLen) {
15546 FPL__CheckArgumentNull(wideSource, 0);
15547 FPL__CheckArgumentZero(wideSourceLen, 0);
15548 size_t result = WideCharToMultiByte(CP_UTF8, 0, wideSource, (int)wideSourceLen, fpl_null, 0, fpl_null, fpl_null);
15549 if (utf8Dest != fpl_null) {
15550 size_t minRequiredLen = result + 1;
15551 FPL__CheckArgumentMin(maxUtf8DestLen, minRequiredLen, 0);
15552 WideCharToMultiByte(CP_UTF8, 0, wideSource, (int)wideSourceLen, utf8Dest, (int)maxUtf8DestLen, fpl_null, fpl_null);
15553 utf8Dest[result] = 0;
15554 }
15555 return(result);
15556}
15557fpl_platform_api size_t fplUTF8StringToWideString(const char *utf8Source, const size_t utf8SourceLen, wchar_t *wideDest, const size_t maxWideDestLen) {
15558 FPL__CheckArgumentNull(utf8Source, 0);
15559 FPL__CheckArgumentZero(utf8SourceLen, 0);
15560 size_t result = MultiByteToWideChar(CP_UTF8, 0, utf8Source, (int)utf8SourceLen, fpl_null, 0);
15561 if (wideDest != fpl_null) {
15562 size_t minRequiredLen = result + 1;
15563 FPL__CheckArgumentMin(maxWideDestLen, minRequiredLen, 0);
15564 MultiByteToWideChar(CP_UTF8, 0, utf8Source, (int)utf8SourceLen, wideDest, (int)maxWideDestLen);
15565 wideDest[result] = 0;
15566 }
15567 return(result);
15568}
15569
15570//
15571// Win32 Library
15572//
15573fpl_platform_api bool fplDynamicLibraryLoad(const char *libraryFilePath, fplDynamicLibraryHandle *outHandle) {
15574 bool result = false;
15575 if (libraryFilePath != fpl_null && outHandle != fpl_null) {
15576 wchar_t libraryFilePathWide[FPL_MAX_PATH_LENGTH];
15577 fplUTF8StringToWideString(libraryFilePath, fplGetStringLength(libraryFilePath), libraryFilePathWide, fplArrayCount(libraryFilePathWide));
15578 HMODULE libModule = LoadLibraryW(libraryFilePathWide);
15579 if (libModule != fpl_null) {
15580 fplClearStruct(outHandle);
15581 outHandle->internalHandle.win32LibraryHandle = libModule;
15582 outHandle->isValid = true;
15583 result = true;
15584 }
15585 }
15586 return(result);
15587}
15588fpl_platform_api void *fplGetDynamicLibraryProc(const fplDynamicLibraryHandle *handle, const char *name) {
15589 if ((handle != fpl_null) && (handle->internalHandle.win32LibraryHandle != fpl_null) && (name != fpl_null)) {
15590 HMODULE libModule = (HMODULE)handle->internalHandle.win32LibraryHandle;
15591 return (void *)GetProcAddress(libModule, name);
15592 }
15593 return fpl_null;
15594}
15596 if ((handle != fpl_null) && (handle->internalHandle.win32LibraryHandle != fpl_null)) {
15597 HMODULE libModule = (HMODULE)handle->internalHandle.win32LibraryHandle;
15598 FreeLibrary(libModule);
15599 fplClearStruct(handle);
15600 }
15601}
15602
15603#if defined(FPL__ENABLE_WINDOW)
15604//
15605// Win32 Window
15606//
15608 FPL__CheckArgumentNull(outSize, false);
15609 FPL__CheckPlatform(false);
15610 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
15611 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15612 const fpl__Win32Api *wapi = &appState->winApi;
15613 bool result = false;
15614 RECT windowRect;
15615 if (wapi->user.GetClientRect(windowState->windowHandle, &windowRect)) {
15616 outSize->width = windowRect.right - windowRect.left;
15617 outSize->height = windowRect.bottom - windowRect.top;
15618 result = true;
15619 }
15620 return(result);
15621}
15622
15623fpl_platform_api void fplSetWindowSize(const uint32_t width, const uint32_t height) {
15624 FPL__CheckPlatformNoRet();
15625 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
15626 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15627 const fpl__Win32Api *wapi = &appState->winApi;
15628 RECT clientRect, windowRect;
15629 if (wapi->user.GetClientRect(windowState->windowHandle, &clientRect) &&
15630 wapi->user.GetWindowRect(windowState->windowHandle, &windowRect)) {
15631 int borderWidth = (windowRect.right - windowRect.left) - (clientRect.right - clientRect.left);
15632 int borderHeight = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
15633 int newWidth = width + borderWidth;
15634 int newHeight = height + borderHeight;
15635 wapi->user.SetWindowPos(windowState->windowHandle, fpl_null, 0, 0, newWidth, newHeight, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
15636 }
15637}
15638
15640 FPL__CheckPlatform(false);
15641 const fpl__PlatformAppState *appState = fpl__global__AppState;
15642 bool result = appState->currentSettings.window.isResizable != 0;
15643 return(result);
15644}
15645
15646fpl_platform_api void fplSetWindowResizeable(const bool value) {
15647 FPL__CheckPlatformNoRet();
15648 fpl__PlatformAppState *appState = fpl__global__AppState;
15649 const fpl__Win32WindowState *windowState = &appState->window.win32;
15650 if (!appState->currentSettings.window.isFullscreen && appState->currentSettings.window.isDecorated) {
15651 DWORD style = fpl__win32_GetWindowLong(windowState->windowHandle, GWL_STYLE);
15652 DWORD exStyle = fpl__win32_GetWindowLong(windowState->windowHandle, GWL_EXSTYLE);
15653 if (value) {
15654 style |= (WS_MAXIMIZEBOX | WS_THICKFRAME);
15655 } else {
15656 style &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
15657 }
15658 fpl__win32_SetWindowLong(windowState->windowHandle, GWL_STYLE, style);
15659 appState->currentSettings.window.isResizable = value;
15660 }
15661}
15662
15664 FPL__CheckPlatform(false);
15665 const fpl__PlatformAppState *appState = fpl__global__AppState;
15666 bool result = appState->currentSettings.window.isDecorated != 0;
15667 return(result);
15668}
15669
15670fpl_platform_api void fplSetWindowDecorated(const bool value) {
15671 FPL__CheckPlatformNoRet();
15672 fpl__PlatformAppState *appState = fpl__global__AppState;
15673 const fpl__Win32WindowState *windowState = &appState->window.win32;
15674 const fpl__Win32Api *wapi = &appState->win32.winApi;
15675 if (!appState->currentSettings.window.isFullscreen) {
15676 HWND windowHandle = windowState->windowHandle;
15677 DWORD style = fpl__win32_GetWindowLong(windowHandle, GWL_STYLE);
15678 DWORD exStyle = fpl__win32_GetWindowLong(windowHandle, GWL_EXSTYLE);
15679 if (value) {
15680 style &= ~WS_POPUP;
15681 style |= WS_OVERLAPPEDWINDOW;
15682 if (!appState->currentSettings.window.isResizable) {
15683 style &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
15684 }
15685 } else {
15686 style &= ~WS_OVERLAPPEDWINDOW;
15687 style |= WS_POPUP;
15688 }
15689 fpl__win32_SetWindowLong(windowHandle, GWL_STYLE, style);
15690 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
15691 appState->currentSettings.window.isDecorated = value;
15692 }
15693}
15694
15696 FPL__CheckPlatform(false);
15697 const fpl__PlatformAppState *appState = fpl__global__AppState;
15698 bool result = appState->currentSettings.window.isFloating != 0;
15699 return(result);
15700}
15701
15702fpl_platform_api void fplSetWindowFloating(const bool value) {
15703 FPL__CheckPlatformNoRet();
15704 fpl__PlatformAppState *appState = fpl__global__AppState;
15705 const fpl__Win32WindowState *windowState = &appState->window.win32;
15706 const fpl__Win32Api *wapi = &appState->win32.winApi;
15707 if (!appState->currentSettings.window.isFullscreen) {
15708 if (value) {
15709 wapi->user.SetWindowPos(windowState->windowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
15710 } else {
15711 wapi->user.SetWindowPos(windowState->windowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
15712 }
15713 appState->currentSettings.window.isFloating = value;
15714 }
15715}
15716
15718 FPL__CheckPlatform(false);
15719 fpl__PlatformAppState *appState = fpl__global__AppState;
15720 bool result = appState->currentSettings.window.isFullscreen != 0;
15721 return(result);
15722}
15723
15724
15725fpl_platform_api bool fplSetWindowFullscreenSize(const bool value, const uint32_t fullscreenWidth, const uint32_t fullscreenHeight, const uint32_t refreshRate) {
15726 bool result = fpl__Win32SetWindowFullscreen(value, INT32_MAX, INT32_MAX, fullscreenWidth, fullscreenHeight, refreshRate, true);
15727 return(result);
15728}
15729
15730fpl_platform_api bool fplSetWindowFullscreenRect(const bool value, const int32_t x, const int32_t y, const int32_t width, const int32_t height) {
15731 bool result = fpl__Win32SetWindowFullscreen(value, x, y, width, height, 0, false);
15732 return(result);
15733}
15734
15736 bool result = fpl__Win32SetWindowFullscreen(true, INT32_MAX, INT32_MAX, 0, 0, 0, false);
15737 return(result);
15738}
15739
15741 bool result = fpl__Win32SetWindowFullscreen(false, 0, 0, 0, 0, 0, false);
15742 return(result);
15743}
15744
15746 FPL__CheckArgumentNull(outPos, false);
15747 FPL__CheckPlatform(false);
15748 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
15749 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15750 const fpl__Win32Api *wapi = &appState->winApi;
15751 bool result = false;
15752 WINDOWPLACEMENT placement = fplZeroInit;
15753 placement.length = sizeof(WINDOWPLACEMENT);
15754 if (wapi->user.GetWindowPlacement(windowState->windowHandle, &placement) == TRUE) {
15755 switch (placement.showCmd) {
15756 case SW_MAXIMIZE:
15757 {
15758 outPos->left = placement.ptMaxPosition.x;
15759 outPos->top = placement.ptMaxPosition.y;
15760 } break;
15761 case SW_MINIMIZE:
15762 {
15763 outPos->left = placement.ptMinPosition.x;
15764 outPos->top = placement.ptMinPosition.y;
15765 } break;
15766 default:
15767 {
15768 outPos->left = placement.rcNormalPosition.left;
15769 outPos->top = placement.rcNormalPosition.top;
15770 } break;
15771 }
15772 result = true;
15773 }
15774 return(result);
15775}
15776
15777fpl_platform_api void fplSetWindowTitle(const char *title) {
15778 FPL__CheckPlatformNoRet();
15779 fpl__PlatformAppState *appState = fpl__global__AppState;
15780 const fpl__Win32AppState *win32AppState = &fpl__global__AppState->win32;
15781 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15782 const fpl__Win32Api *wapi = &win32AppState->winApi;
15783 HWND handle = windowState->windowHandle;
15784 fplCopyString(title, appState->currentSettings.window.title, fplArrayCount(appState->currentSettings.window.title));
15785 wchar_t titleWide[FPL_MAX_BUFFER_LENGTH];
15786 fplUTF8StringToWideString(title, fplGetStringLength(title), titleWide, fplArrayCount(titleWide));
15787 wapi->user.SetWindowTextW(handle, titleWide);
15788}
15789
15790fpl_platform_api void fplSetWindowPosition(const int32_t left, const int32_t top) {
15791 FPL__CheckPlatformNoRet();
15792 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
15793 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15794 const fpl__Win32Api *wapi = &appState->winApi;
15795 WINDOWPLACEMENT placement = fplZeroInit;
15796 placement.length = sizeof(WINDOWPLACEMENT);
15797 RECT windowRect;
15798 if (wapi->user.GetWindowPlacement(windowState->windowHandle, &placement) &&
15799 wapi->user.GetWindowRect(windowState->windowHandle, &windowRect)) {
15800 switch (placement.showCmd) {
15801 case SW_NORMAL:
15802 case SW_SHOW:
15803 {
15804 placement.rcNormalPosition.left = left;
15805 placement.rcNormalPosition.top = top;
15806 placement.rcNormalPosition.right = placement.rcNormalPosition.left + (windowRect.right - windowRect.left);
15807 placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (windowRect.bottom - windowRect.top);
15808 wapi->user.SetWindowPlacement(windowState->windowHandle, &placement);
15809 } break;
15810 }
15811 }
15812}
15813
15815 FPL__CheckPlatform(fplWindowState_Unknown);
15816 const fpl__PlatformAppState *appState = fpl__global__AppState;
15817 const fpl__Win32AppState *win32AppState = &appState->win32;
15818 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15819 const fpl__Win32Api *wapi = &win32AppState->winApi;
15820 HWND windowHandle = windowState->windowHandle;
15821 fplWindowState result;
15822 if (appState->currentSettings.window.isFullscreen) {
15824 } else {
15825 bool isMaximized = !!wapi->user.IsZoomed(windowHandle);
15826 bool isMinimized = !!wapi->user.IsIconic(windowHandle);
15827 if (isMinimized) {
15828 result = fplWindowState_Iconify;
15829 } else if (isMaximized) {
15830 result = fplWindowState_Maximize;
15831 } else {
15832 result = fplWindowState_Normal;
15833 }
15834 }
15835 return(result);
15836}
15837
15839 FPL__CheckPlatform(false);
15840 const fpl__PlatformAppState *appState = fpl__global__AppState;
15841 const fpl__Win32AppState *win32AppState = &appState->win32;
15842 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15843 const fpl__Win32Api *wapi = &win32AppState->winApi;
15844 HWND windowHandle = windowState->windowHandle;
15845 bool result = false;
15846 switch (newState) {
15848 {
15849 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
15850 result = true;
15851 } break;
15852
15854 {
15855 if (!appState->currentSettings.window.isFullscreen && appState->currentSettings.window.isResizable) {
15856 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
15857 result = true;
15858 }
15859 } break;
15860
15862 {
15863 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
15864 result = true;
15865 } break;
15866
15868 {
15869 if (!appState->currentSettings.window.isFullscreen)
15870 result = fpl__Win32SetWindowFullscreen(true, INT32_MAX, INT32_MAX, 0, 0, 0, false);
15871 else
15872 result = true;
15873 } break;
15874
15876 default:
15877 break;
15878 }
15879 return(result);
15880}
15881
15882fpl_platform_api void fplSetWindowCursorEnabled(const bool value) {
15883 FPL__CheckPlatformNoRet();
15884 fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15885 windowState->isCursorActive = value;
15886}
15887
15888fpl_internal bool fpl__Win32ProcessNextEvent(const fpl__Win32Api *wapi, fpl__PlatformAppState *appState, fpl__Win32WindowState *windowState) {
15889 bool result = false;
15890 if (windowState->windowHandle != 0) {
15891 MSG msg;
15892 if (wapi->user.PeekMessageW(&msg, windowState->windowHandle, 0, 0, PM_REMOVE) == TRUE) {
15893 fpl__Win32HandleMessage(wapi, appState, windowState, &msg);
15894 result = true;
15895 }
15896 }
15897 return(result);
15898}
15899
15901 FPL__CheckPlatform(false);
15902 fpl__PlatformAppState *appState = fpl__global__AppState;
15903 fpl__Win32AppState *win32AppState = &appState->win32;
15904 fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15905 const fpl__Win32InitState *win32InitState = &fpl__global__InitState.win32;
15906 const fpl__Win32Api *wapi = &win32AppState->winApi;
15907
15908 // Poll next event from the internal queue first
15909 if (fpl__PollInternalEvent(ev)) {
15910 return(true);
15911 }
15912
15913 // Create new event from the OS message queue
15914 if (!fpl__Win32ProcessNextEvent(wapi, appState, windowState)) {
15915 return(false);
15916 }
15917
15918 // Poll the first event from the internal queue
15919 if (fpl__PollInternalEvent(ev)) {
15920 return(true);
15921 }
15922
15923 // No events left
15924 return(false);
15925}
15926
15928 FPL__CheckPlatformNoRet();
15929 fpl__PlatformAppState *appState = fpl__global__AppState;
15930 fpl__Win32AppState *win32AppState = &appState->win32;
15931 fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15932 const fpl__Win32InitState *win32InitState = &fpl__global__InitState.win32;
15933 const fpl__Win32Api *wapi = &win32AppState->winApi;
15934 if (windowState->windowHandle != 0) {
15935 if (windowState->mainFiber != fpl_null && windowState->messageFiber != fpl_null) {
15936 SwitchToFiber(windowState->messageFiber);
15937 } else {
15938 MSG msg;
15939 while (wapi->user.PeekMessageW(&msg, windowState->windowHandle, 0, 0, PM_REMOVE)) {
15940 fpl__Win32HandleMessage(wapi, appState, windowState, &msg);
15941 }
15942 }
15943 }
15944 fpl__ClearInternalEvents();
15945}
15946
15948 FPL__CheckPlatform(false);
15949 fpl__PlatformAppState *appState = fpl__global__AppState;
15950 fpl__Win32AppState *win32AppState = &appState->win32;
15951 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15952 const fpl__Win32InitState *win32InitState = &fpl__global__InitState.win32;
15953 const fpl__Win32Api *wapi = &win32AppState->winApi;
15954 fpl__ClearInternalEvents();
15955 if ((!appState->currentSettings.input.disabledEvents) && (appState->initFlags & fplInitFlags_GameController)) {
15956 fpl__Win32UpdateGameControllers(&appState->currentSettings, win32InitState, &win32AppState->xinput);
15957 }
15958 bool result = appState->window.isRunning != 0;
15959 return(result);
15960}
15961
15963 FPL__CheckPlatform(false);
15964 bool result = fpl__global__AppState->window.isRunning != 0;
15965 return(result);
15966}
15967
15969 FPL__CheckPlatformNoRet();
15970 fpl__PlatformAppState *appState = fpl__global__AppState;
15971 const fpl__Win32AppState *win32AppState = &appState->win32;
15972 if (appState->window.isRunning) {
15973 appState->window.isRunning = false;
15974 const fpl__Win32Api *wapi = &win32AppState->winApi;
15975 wapi->user.PostQuitMessage(0);
15976 }
15977}
15978
15979fpl_platform_api bool fplGetClipboardText(char *dest, const uint32_t maxDestLen) {
15980 FPL__CheckPlatform(false);
15981 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
15982 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
15983 const fpl__Win32Api *wapi = &appState->winApi;
15984 bool result = false;
15985 if (wapi->user.OpenClipboard(windowState->windowHandle)) {
15986 if (wapi->user.IsClipboardFormatAvailable(CF_UNICODETEXT)) {
15987 HGLOBAL dataHandle = wapi->user.GetClipboardData(CF_UNICODETEXT);
15988 if (dataHandle != fpl_null) {
15989 const wchar_t *stringValue = (const wchar_t *)GlobalLock(dataHandle);
15990 fplWideStringToUTF8String(stringValue, lstrlenW(stringValue), dest, maxDestLen);
15991 GlobalUnlock(dataHandle);
15992 result = true;
15993 }
15994 }
15995 wapi->user.CloseClipboard();
15996 }
15997 return(result);
15998}
15999
16000fpl_platform_api bool fplSetClipboardText(const char *text) {
16001 FPL__CheckPlatform(false);
16002 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16003 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16004 const fpl__Win32Api *wapi = &appState->winApi;
16005 bool result = false;
16006 if (wapi->user.OpenClipboard(windowState->windowHandle)) {
16007 const size_t textLen = fplGetStringLength(text);
16008 const size_t bufferLen = textLen + 1;
16009 HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)bufferLen * sizeof(wchar_t));
16010 if (handle != fpl_null) {
16011 wchar_t *target = (wchar_t *)GlobalLock(handle);
16012 fplUTF8StringToWideString(text, textLen, target, bufferLen);
16013 GlobalUnlock(handle);
16014 wapi->user.EmptyClipboard();
16015 wapi->user.SetClipboardData(CF_UNICODETEXT, handle);
16016 result = true;
16017 }
16018 wapi->user.CloseClipboard();
16019 }
16020 return(result);
16021}
16022
16024 FPL__CheckArgumentNull(outState, false);
16025 FPL__CheckPlatform(false);
16026 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16027 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16028 const fpl__Win32Api *wapi = &appState->winApi;
16029 fplClearStruct(outState);
16030 outState->modifiers = fpl__Win32GetKeyboardModifiers(wapi);
16031 for (uint32_t keyCode = 0; keyCode < 256; ++keyCode) {
16032 int k = wapi->user.MapVirtualKeyW(MAPVK_VSC_TO_VK, keyCode);
16033 if (k == 0) {
16034 k = (int)keyCode;
16035 }
16036 bool down = fpl__Win32IsKeyDown(wapi, k);
16037 fplKey key = fpl__GetMappedKey(&fpl__global__AppState->window, keyCode);
16038 outState->keyStatesRaw[keyCode] = down;
16039 outState->buttonStatesMapped[(int)key] = down ? fplButtonState_Press : fplButtonState_Release;
16040 }
16041 return(true);
16042}
16043
16045 FPL__CheckArgumentNull(outStates, false);
16046 FPL__CheckPlatform(false);
16047 fpl__PlatformAppState *platformAppState = fpl__global__AppState;
16048 if (platformAppState->initFlags & fplInitFlags_GameController) {
16049 fpl__Win32AppState *appState = &platformAppState->win32;
16050 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16051 const fpl__Win32Api *wapi = &appState->winApi;
16052 fpl__Win32XInputState *xinputState = &appState->xinput;
16053 fplAssert(xinputState != fpl_null);
16054 if (xinputState->xinputApi.XInputGetState != fpl_null) {
16055 // @TODO(final): fpl__Win32UpdateGameControllers() uses duplicate code
16056 QueryPerformanceCounter(&xinputState->lastDeviceSearchTime);
16057
16058 fplClearStruct(outStates);
16059 for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; ++controllerIndex) {
16060 XINPUT_STATE controllerState = fplZeroInit;
16061 if (xinputState->xinputApi.XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) {
16062 if (!xinputState->isConnected[controllerIndex]) {
16063 xinputState->isConnected[controllerIndex] = true;
16064 fplStringFormat(xinputState->deviceNames[controllerIndex], fplArrayCount(xinputState->deviceNames[controllerIndex]), "XInput-Device [%d]", controllerIndex);
16065 }
16066 const XINPUT_GAMEPAD *newPadState = &controllerState.Gamepad;
16067 fplGamepadState *targetPadState = &outStates->deviceStates[controllerIndex];
16068 fpl__Win32XInputGamepadToGamepadState(newPadState, targetPadState);
16069 targetPadState->deviceName = xinputState->deviceNames[controllerIndex];
16070 } else {
16071 if (xinputState->isConnected[controllerIndex]) {
16072 xinputState->isConnected[controllerIndex] = false;
16073 }
16074 }
16075 }
16076 return(true);
16077 }
16078 }
16079 return(false);
16080}
16081
16082fpl_platform_api bool fplQueryCursorPosition(int32_t *outX, int32_t *outY) {
16083 FPL__CheckArgumentNull(outX, false);
16084 FPL__CheckArgumentNull(outY, false);
16085 FPL__CheckPlatform(false);
16086 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16087 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16088 const fpl__Win32Api *wapi = &appState->winApi;
16089 POINT p;
16090 if (wapi->user.GetCursorPos(&p) == TRUE) {
16091#if 0
16092 HMONITOR monitor = wapi->user.MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
16093 if (monitor != fpl_null) {
16094 MONITORINFOEXW info = fplZeroInit;
16095 info.cbSize = sizeof(info);
16096 if (wapi->user.GetMonitorInfoW(monitor, (LPMONITORINFO)&info) != 0) {
16097 *outX = p.x - info.rcMonitor.left;
16098 *outY = p.y - info.rcMonitor.top;
16099 return(true);
16100 }
16101 }
16102#else
16103 *outX = p.x;
16104 *outY = p.y;
16105 return(true);
16106#endif
16107 }
16108 return(false);
16109}
16110
16112 FPL__CheckArgumentNull(outState, false);
16113 FPL__CheckPlatform(false);
16114 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16115 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16116 const fpl__Win32Api *wapi = &appState->winApi;
16117 POINT p;
16118 if ((wapi->user.GetCursorPos(&p) == TRUE) && (wapi->user.ScreenToClient(windowState->windowHandle, &p))) {
16119 fplClearStruct(outState);
16120 outState->x = p.x;
16121 outState->y = p.y;
16122
16123 bool leftDown = fpl__Win32IsKeyDown(wapi, VK_LBUTTON);
16124 bool rightDown = fpl__Win32IsKeyDown(wapi, VK_RBUTTON);
16125 bool middleDown = fpl__Win32IsKeyDown(wapi, VK_MBUTTON);
16129
16130 return(true);
16131 }
16132 return(false);
16133}
16134
16135fpl_internal BOOL WINAPI fpl__Win32MonitorCountEnumProc(HMONITOR monitorHandle, HDC hdc, LPRECT rect, LPARAM userData) {
16136 size_t *count = (size_t *)(uintptr_t)userData;
16137 *count = *count + 1;
16138 return(TRUE);
16139}
16140
16142 FPL__CheckPlatform(0);
16143 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16144 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16145 const fpl__Win32Api *wapi = &appState->winApi;
16146 size_t result = 0;
16147 LPARAM param = (LPARAM)&result;
16148 wapi->user.EnumDisplayMonitors(fpl_null, fpl_null, fpl__Win32MonitorCountEnumProc, param);
16149 return(result);
16150}
16151fpl_internal void fpl__Win32FillDisplayInfo(const MONITORINFOEXW *info, fplDisplayInfo *outInfo) {
16152 fplAssert(info != fpl_null);
16153 fplAssert(outInfo != fpl_null);
16154 size_t idLen = lstrlenW(info->szDevice);
16155 fplWideStringToUTF8String(info->szDevice, idLen, outInfo->id, fplArrayCount(outInfo->id));
16156 outInfo->virtualPosition.left = info->rcMonitor.left;
16157 outInfo->virtualPosition.top = info->rcMonitor.top;
16158 outInfo->virtualSize.width = info->rcMonitor.right - info->rcMonitor.left;
16159 outInfo->virtualSize.height = info->rcMonitor.bottom - info->rcMonitor.top;
16160 outInfo->physicalSize.width = (info->rcMonitor.right > info->rcMonitor.left) ? (info->rcMonitor.right - info->rcMonitor.left) : (info->rcMonitor.left - info->rcMonitor.right);
16161 outInfo->physicalSize.height = (info->rcMonitor.bottom > info->rcMonitor.top) ? (info->rcMonitor.bottom - info->rcMonitor.top) : (info->rcMonitor.top - info->rcMonitor.bottom);
16162 outInfo->isPrimary = (info->dwFlags & MONITORINFOF_PRIMARY) ? 1 : 0;
16163}
16164
16165typedef struct fpl__Win32DisplayEnumState {
16166 fplDisplayInfo *baseInfo;
16167 const fpl__Win32Api *wapi;
16168 size_t count;
16169 size_t maxCount;
16170} fpl__Win32DisplayEnumState;
16171
16172fpl_internal BOOL WINAPI fpl__Win32MonitorInfoEnumProc(HMONITOR monitorHandle, HDC hdc, LPRECT rect, LPARAM userData) {
16173 fpl__Win32DisplayEnumState *enumState = (fpl__Win32DisplayEnumState *)(uintptr_t)userData;
16174 const fpl__Win32Api *wapi = enumState->wapi;
16175 if (enumState->count < enumState->maxCount) {
16176 fplDisplayInfo *targetInfo = enumState->baseInfo + enumState->count;
16177 fplClearStruct(targetInfo);
16178 MONITORINFOEXW info = fplZeroInit;
16179 info.cbSize = sizeof(info);
16180 if (wapi->user.GetMonitorInfoW(monitorHandle, (LPMONITORINFO)&info) != 0) {
16181 fpl__Win32FillDisplayInfo(&info, targetInfo);
16182 }
16183 ++enumState->count;
16184 BOOL result = enumState->count < enumState->maxCount;
16185 return(result);
16186 } else {
16187 return(FALSE);
16188 }
16189}
16190
16191fpl_platform_api size_t fplGetDisplays(fplDisplayInfo *displays, const size_t maxDisplayCount) {
16192 FPL__CheckArgumentNull(displays, 0);
16193 FPL__CheckArgumentZero(maxDisplayCount, 0);
16194 FPL__CheckPlatform(0);
16195 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16196 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16197 const fpl__Win32Api *wapi = &appState->winApi;
16198 fpl__Win32DisplayEnumState enumState = fplZeroInit;
16199 enumState.baseInfo = displays;
16200 enumState.maxCount = maxDisplayCount;
16201 enumState.wapi = wapi;
16202 LPARAM param = (LPARAM)&enumState;
16203 wapi->user.EnumDisplayMonitors(fpl_null, fpl_null, fpl__Win32MonitorInfoEnumProc, param);
16204 return(enumState.count);
16205}
16206
16207fpl_internal BOOL WINAPI fpl__Win32PrimaryMonitorEnumProc(HMONITOR monitorHandle, HDC hdc, LPRECT rect, LPARAM userData) {
16208 fpl__Win32DisplayEnumState *enumState = (fpl__Win32DisplayEnumState *)(uintptr_t)userData;
16209 const fpl__Win32Api *wapi = enumState->wapi;
16210 MONITORINFOEXW info = fplZeroInit;
16211 info.cbSize = sizeof(info);
16212 if (wapi->user.GetMonitorInfoW(monitorHandle, (LPMONITORINFO)&info) != 0) {
16213 if (info.dwFlags & MONITORINFOF_PRIMARY) {
16214 fplClearStruct(enumState->baseInfo);
16215 fpl__Win32FillDisplayInfo(&info, enumState->baseInfo);
16216 enumState->count = 1;
16217 return(FALSE);
16218 }
16219 }
16220 return(TRUE);
16221}
16222
16224 FPL__CheckArgumentNull(display, false);
16225 FPL__CheckPlatform(false);
16226 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16227 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16228 const fpl__Win32Api *wapi = &appState->winApi;
16229 fpl__Win32DisplayEnumState enumState = fplZeroInit;
16230 enumState.baseInfo = display;
16231 enumState.maxCount = 1;
16232 enumState.wapi = wapi;
16233 LPARAM param = (LPARAM)&enumState;
16234 wapi->user.EnumDisplayMonitors(fpl_null, fpl_null, fpl__Win32PrimaryMonitorEnumProc, param);
16235 bool result = (enumState.count == 1);
16236 return(result);
16237}
16238
16240 FPL__CheckArgumentNull(outDisplay, false);
16241 FPL__CheckPlatform(false);
16242 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16243 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16244 const fpl__Win32Api *wapi = &appState->winApi;
16245 HMONITOR foundMonitor = wapi->user.MonitorFromWindow(windowState->windowHandle, MONITOR_DEFAULTTONULL);
16246 bool result = false;
16247 if (foundMonitor != fpl_null) {
16248 MONITORINFOEXW info = fplZeroInit;
16249 info.cbSize = sizeof(info);
16250 if (wapi->user.GetMonitorInfoW(foundMonitor, (LPMONITORINFO)&info) != 0) {
16251 fplClearStruct(outDisplay);
16252 fpl__Win32FillDisplayInfo(&info, outDisplay);
16253 result = true;
16254 }
16255 }
16256 return(result);
16257}
16258
16259fpl_platform_api bool fplGetDisplayFromPosition(const int32_t x, const int32_t y, fplDisplayInfo *outDisplay) {
16260 FPL__CheckArgumentNull(outDisplay, false);
16261 FPL__CheckPlatform(false);
16262 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16263 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16264 const fpl__Win32Api *wapi = &appState->winApi;
16265 POINT pt;
16266 pt.x = x;
16267 pt.y = y;
16268 bool result = false;
16269 HMONITOR foundMonitor = wapi->user.MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
16270 if (foundMonitor != fpl_null) {
16271 MONITORINFOEXW info = fplZeroInit;
16272 info.cbSize = sizeof(info);
16273 if (wapi->user.GetMonitorInfoW(foundMonitor, (LPMONITORINFO)&info) != 0) {
16274 fplClearStruct(outDisplay);
16275 fpl__Win32FillDisplayInfo(&info, outDisplay);
16276 result = true;
16277 }
16278 }
16279 return(result);
16280}
16281
16282fpl_platform_api size_t fplGetDisplayModes(const char *id, fplDisplayMode *modes, const size_t maxDisplayModeCount) {
16283 FPL__CheckArgumentNull(id, 0);
16284 FPL__CheckPlatform(0);
16285 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16286 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
16287 const fpl__Win32Api *wapi = &appState->winApi;
16288 wchar_t deviceName[CCHDEVICENAME + 1];
16289 fplUTF8StringToWideString(id, fplGetStringLength(id), deviceName, fplArrayCount(deviceName));
16290 size_t result = 0;
16291 DEVMODEW devMode;
16292 while (wapi->user.EnumDisplaySettingsW(deviceName, (DWORD)result, &devMode)) {
16293 if (modes != fpl_null) {
16294 if (result == maxDisplayModeCount) {
16295 break;
16296 }
16297 fplDisplayMode *outMode = modes + result;
16298 fplClearStruct(outMode);
16299 outMode->width = devMode.dmPelsWidth;
16300 outMode->height = devMode.dmPelsHeight;
16301 outMode->colorBits = devMode.dmBitsPerPel;
16302 outMode->refreshRate = devMode.dmDisplayFrequency;
16303 }
16304 ++result;
16305 }
16306 return(result);
16307}
16308
16309#endif // FPL__ENABLE_WINDOW
16310
16311fpl_internal LCTYPE fpl__Win32GetLocaleLCIDFromFormat(const fplLocaleFormat format) {
16312 switch (format) {
16314 return LOCALE_SNAME;
16315 default:
16316 return LOCALE_SABBREVLANGNAME;
16317 }
16318}
16319
16320fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
16321 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
16322 LCTYPE lcType = fpl__Win32GetLocaleLCIDFromFormat(targetFormat);
16323 wchar_t bufferWide[FPL_MAX_BUFFER_LENGTH];
16324 int r = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, lcType, bufferWide, fplArrayCount(bufferWide));
16325 size_t result = fplWideStringToUTF8String(bufferWide, lstrlenW(bufferWide), buffer, maxBufferLen);
16326 return(result);
16327}
16328
16329fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
16330 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
16331 LCTYPE lcType = fpl__Win32GetLocaleLCIDFromFormat(targetFormat);
16332 wchar_t bufferWide[FPL_MAX_BUFFER_LENGTH];
16333 int r = GetLocaleInfoW(LOCALE_USER_DEFAULT, lcType, bufferWide, fplArrayCount(bufferWide));
16334 size_t result = fplWideStringToUTF8String(bufferWide, lstrlenW(bufferWide), buffer, maxBufferLen);
16335 return(result);
16336}
16337
16338fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
16339 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
16340 FPL__CheckPlatform(false);
16341 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
16342 const fpl__Win32Api *wapi = &appState->winApi;
16343 HKL kbLayout = wapi->user.GetKeyboardLayout(GetCurrentThreadId());
16344 LCID langId = (DWORD)(intptr_t)kbLayout & 0xFFFF;
16345 LCTYPE lcType = fpl__Win32GetLocaleLCIDFromFormat(targetFormat);
16346 wchar_t bufferWide[FPL_MAX_BUFFER_LENGTH];
16347 int r = GetLocaleInfoW(langId, lcType, bufferWide, fplArrayCount(bufferWide));
16348 size_t result = fplWideStringToUTF8String(bufferWide, lstrlenW(bufferWide), buffer, maxBufferLen);
16349 return(result);
16350}
16351#endif // FPL_PLATFORM_WINDOWS
16352
16353// ############################################################################
16354//
16355// > POSIX_SUBPLATFORM (Linux, Unix)
16356//
16357// ############################################################################
16358#if defined(FPL_SUBPLATFORM_POSIX)
16359fpl_internal void fpl__PosixReleaseSubplatform(fpl__PosixAppState *appState) {
16360 fpl__PThreadUnloadApi(&appState->pthreadApi);
16361}
16362
16363fpl_internal bool fpl__PosixInitSubplatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PosixInitState *initState, fpl__PosixAppState *appState) {
16364 fpl__PThreadApi *pthreadApi = &appState->pthreadApi;
16365
16366 if (!fpl__PThreadLoadApi(pthreadApi)) {
16367 FPL__ERROR(FPL__MODULE_POSIX, "Failed initializing PThread API");
16368 return false;
16369 }
16370
16371 pthread_t currentThreadHandle = pthreadApi->pthread_self();
16372 uint32_t mainThreadId = (uint32_t)currentThreadHandle;
16373 fplThreadHandle *mainThread = &fpl__global__ThreadState.mainThread;
16374 fplClearStruct(mainThread);
16375 mainThread->id = mainThreadId;
16376 mainThread->internalHandle.posixThread = currentThreadHandle;
16378
16379 return true;
16380}
16381
16382fpl_internal void fpl__InitWaitTimeSpec(const uint32_t milliseconds, struct timespec *outSpec) {
16383 time_t secs = milliseconds / 1000;
16384 uint64_t nanoSecs = (milliseconds - (secs * 1000)) * 1000000;
16385 if (nanoSecs >= 1000000000) {
16386 time_t addonSecs = (time_t)(nanoSecs / 1000000000);
16387 nanoSecs -= (addonSecs * 1000000000);
16388 secs += addonSecs;
16389 }
16390 clock_gettime(CLOCK_REALTIME, outSpec);
16391 outSpec->tv_sec += secs;
16392 outSpec->tv_nsec += nanoSecs;
16393}
16394
16395void *fpl__PosixThreadProc(void *data) {
16396 fplAssert(fpl__global__AppState != fpl_null);
16397 const fpl__PThreadApi *pthreadApi = &fpl__global__AppState->posix.pthreadApi;
16398 fplThreadHandle *thread = (fplThreadHandle *)data;
16399 fplAssert(thread != fpl_null);
16400 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Running);
16401
16402 fplThreadParameters parameters = thread->parameters;
16403 if (parameters.runFunc != fpl_null) {
16404 parameters.runFunc(thread, parameters.userData);
16405 }
16406
16407 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopping);
16408 thread->isValid = false;
16409 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
16410
16411 pthreadApi->pthread_exit(data);
16412 return 0;
16413}
16414
16415fpl_internal bool fpl__PosixMutexLock(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
16416 int lockRes;
16417 do {
16418 lockRes = pthreadApi->pthread_mutex_lock(handle);
16419 } while (lockRes == EAGAIN);
16420 bool result = (lockRes == 0);
16421 return(result);
16422}
16423
16424fpl_internal bool fpl__PosixMutexTryLock(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
16425 int lockRes;
16426 do {
16427 lockRes = pthreadApi->pthread_mutex_trylock(handle);
16428 } while (lockRes == EAGAIN);
16429 bool result = (lockRes == 0);
16430 return(result);
16431}
16432
16433fpl_internal bool fpl__PosixMutexUnlock(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
16434 int unlockRes;
16435 do {
16436 unlockRes = pthreadApi->pthread_mutex_unlock(handle);
16437 } while (unlockRes == EAGAIN);
16438 bool result = (unlockRes == 0);
16439 return(result);
16440}
16441
16442fpl_internal int fpl__PosixMutexCreate(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
16443 int mutexRes;
16444 do {
16445 mutexRes = pthreadApi->pthread_mutex_init(handle, fpl_null);
16446 } while (mutexRes == EAGAIN);
16447 return(mutexRes);
16448}
16449
16450fpl_internal bool fpl__PosixThreadWaitForMultiple(fplThreadHandle **threads, const uint32_t minCount, const uint32_t maxCount, const size_t stride, const fplTimeoutValue timeout) {
16451 FPL__CheckArgumentNull(threads, false);
16452 FPL__CheckArgumentMax(maxCount, FPL_MAX_THREAD_COUNT, false);
16453 const size_t actualStride = stride > 0 ? stride : sizeof(fplThreadHandle *);
16454 for (uint32_t index = 0; index < maxCount; ++index) {
16455 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
16456 if (thread == fpl_null) {
16457 FPL__ERROR(FPL__MODULE_THREADING, "Thread for index '%d' are not allowed to be null", index);
16458 return false;
16459 }
16460 }
16461
16462 uint32_t completeCount = 0;
16463 bool isRunning[FPL_MAX_THREAD_COUNT];
16464 for (uint32_t index = 0; index < maxCount; ++index) {
16465 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
16466 isRunning[index] = fplGetThreadState(thread) != fplThreadState_Stopped;
16467 if (!isRunning[index]) {
16468 ++completeCount;
16469 }
16470 }
16471
16473 bool result = false;
16474 while (completeCount < minCount) {
16475 for (uint32_t index = 0; index < maxCount; ++index) {
16476 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
16477 if (isRunning[index]) {
16478 fplThreadState state = fplGetThreadState(thread);
16479 if (state == fplThreadState_Stopped) {
16480 isRunning[index] = false;
16481 ++completeCount;
16482 if (completeCount >= minCount) {
16483 result = true;
16484 break;
16485 }
16486 }
16487 }
16488 fplThreadSleep(10);
16489 }
16490 if ((timeout != FPL_TIMEOUT_INFINITE) && (fplMillisecondsQuery() - startTime) >= timeout) {
16491 result = false;
16492 break;
16493 }
16494 }
16495 return(result);
16496}
16497
16498//
16499// POSIX Atomics
16500//
16501#if defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG) || defined(__GNUC__)
16502// @NOTE(final): See: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html#g_t_005f_005fsync-Builtins
16503// @NOTE(final): There is only one barrier in POSIX (read and write)
16505 __sync_synchronize();
16506}
16508 __sync_synchronize();
16509}
16511 __sync_synchronize();
16512}
16513
16514fpl_platform_api uint32_t fplAtomicExchangeU32(volatile uint32_t *target, const uint32_t value) {
16515 __sync_synchronize();
16516 uint32_t result = __sync_lock_test_and_set(target, value);
16517 return(result);
16518}
16519fpl_platform_api uint64_t fplAtomicExchangeU64(volatile uint64_t *target, const uint64_t value) {
16520 __sync_synchronize();
16521 uint64_t result = __sync_lock_test_and_set(target, value);
16522 return(result);
16523}
16524fpl_platform_api int32_t fplAtomicExchangeS32(volatile int32_t *target, const int32_t value) {
16525 __sync_synchronize();
16526 int32_t result = __sync_lock_test_and_set(target, value);
16527 return(result);
16528}
16529fpl_platform_api int64_t fplAtomicExchangeS64(volatile int64_t *target, const int64_t value) {
16530 __sync_synchronize();
16531 int64_t result = __sync_lock_test_and_set(target, value);
16532 return(result);
16533}
16534
16535fpl_platform_api uint32_t fplAtomicFetchAndAddU32(volatile uint32_t *value, const uint32_t addend) {
16536 fplAssert(value != fpl_null);
16537 uint32_t result = __sync_fetch_and_add(value, addend);
16538 return (result);
16539}
16540fpl_platform_api uint64_t fplAtomicFetchAndAddU64(volatile uint64_t *value, const uint64_t addend) {
16541 fplAssert(value != fpl_null);
16542 uint64_t result = __sync_fetch_and_add(value, addend);
16543 return (result);
16544}
16545fpl_platform_api int32_t fplAtomicFetchAndAddS32(volatile int32_t *value, const int32_t addend) {
16546 fplAssert(value != fpl_null);
16547 int32_t result = __sync_fetch_and_add(value, addend);
16548 return (result);
16549}
16550fpl_platform_api int64_t fplAtomicFetchAndAddS64(volatile int64_t *value, const int64_t addend) {
16551 fplAssert(value != fpl_null);
16552 int64_t result = __sync_fetch_and_add(value, addend);
16553 return (result);
16554}
16555
16556fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *value, const uint32_t addend) {
16557 fplAssert(value != fpl_null);
16558 uint32_t result = __sync_add_and_fetch(value, addend);
16559 return (result);
16560}
16561fpl_platform_api int32_t fplAtomicAddAndFetchS32(volatile int32_t *value, const int32_t addend) {
16562 fplAssert(value != fpl_null);
16563 int32_t result = __sync_add_and_fetch(value, addend);
16564 return (result);
16565}
16566fpl_platform_api uint64_t fplAtomicAddAndFetchU64(volatile uint64_t *value, const uint64_t addend) {
16567 fplAssert(value != fpl_null);
16568 uint64_t result = __sync_add_and_fetch(value, addend);
16569 return (result);
16570}
16571fpl_platform_api int64_t fplAtomicAddAndFetchS64(volatile int64_t *value, const int64_t addend) {
16572 fplAssert(value != fpl_null);
16573 int64_t result = __sync_add_and_fetch(value, addend);
16574 return (result);
16575}
16576
16577fpl_platform_api uint32_t fplAtomicIncrementU32(volatile uint32_t *value) {
16578 fplAssert(value != fpl_null);
16579 uint32_t result = __sync_add_and_fetch(value, 1);
16580 return (result);
16581}
16582fpl_platform_api uint64_t fplAtomicIncrementU64(volatile uint64_t *value) {
16583 fplAssert(value != fpl_null);
16584 uint64_t result = __sync_add_and_fetch(value, 1);
16585 return (result);
16586}
16587fpl_platform_api int32_t fplAtomicIncrementS32(volatile int32_t *value) {
16588 fplAssert(value != fpl_null);
16589 int32_t result = __sync_add_and_fetch(value, 1);
16590 return (result);
16591}
16592fpl_platform_api int64_t fplAtomicIncrementS64(volatile int64_t *value) {
16593 fplAssert(value != fpl_null);
16594 int64_t result = __sync_add_and_fetch(value, 1);
16595 return (result);
16596}
16597
16598fpl_platform_api uint32_t fplAtomicCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
16599 fplAssert(dest != fpl_null);
16600 uint32_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
16601 return (result);
16602}
16603fpl_platform_api uint64_t fplAtomicCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
16604 fplAssert(dest != fpl_null);
16605 uint64_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
16606 return (result);
16607}
16608fpl_platform_api int32_t fplAtomicCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
16609 fplAssert(dest != fpl_null);
16610 int32_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
16611 return (result);
16612}
16613fpl_platform_api int64_t fplAtomicCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
16614 fplAssert(dest != fpl_null);
16615 int64_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
16616 return (result);
16617}
16618
16619fpl_platform_api bool fplAtomicIsCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
16620 fplAssert(dest != fpl_null);
16621 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
16622 return (result);
16623}
16624fpl_platform_api bool fplAtomicIsCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
16625 fplAssert(dest != fpl_null);
16626 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
16627 return (result);
16628}
16629fpl_platform_api bool fplAtomicIsCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
16630 fplAssert(dest != fpl_null);
16631 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
16632 return (result);
16633}
16634fpl_platform_api bool fplAtomicIsCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
16635 fplAssert(dest != fpl_null);
16636 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
16637 return (result);
16638}
16639
16640fpl_platform_api uint32_t fplAtomicLoadU32(volatile uint32_t *source) {
16641 uint32_t result = __sync_add_and_fetch(source, 0);
16642 return(result);
16643}
16644fpl_platform_api uint64_t fplAtomicLoadU64(volatile uint64_t *source) {
16645 uint64_t result = __sync_add_and_fetch(source, 0);
16646 return(result);
16647}
16648fpl_platform_api int32_t fplAtomicLoadS32(volatile int32_t *source) {
16649 int32_t result = __sync_add_and_fetch(source, 0);
16650 return(result);
16651}
16652fpl_platform_api int64_t fplAtomicLoadS64(volatile int64_t *source) {
16653 int64_t result = __sync_add_and_fetch(source, 0);
16654 return(result);
16655}
16656
16657fpl_platform_api void fplAtomicStoreU32(volatile uint32_t *dest, const uint32_t value) {
16658 __sync_synchronize();
16659 __sync_lock_test_and_set(dest, value);
16660}
16661fpl_platform_api void fplAtomicStoreU64(volatile uint64_t *dest, const uint64_t value) {
16662 __sync_synchronize();
16663 __sync_lock_test_and_set(dest, value);
16664}
16665fpl_platform_api void fplAtomicStoreS32(volatile int32_t *dest, const int32_t value) {
16666 __sync_synchronize();
16667 __sync_lock_test_and_set(dest, value);
16668}
16669fpl_platform_api void fplAtomicStoreS64(volatile int64_t *dest, const int64_t value) {
16670 __sync_synchronize();
16671 __sync_lock_test_and_set(dest, value);
16672}
16673#else
16674# error "This POSIX compiler/platform is not supported!"
16675#endif
16676
16677//
16678// POSIX Timings
16679//
16681 fplTimestamp result = fplZeroInit;
16682 struct timespec t;
16683 clock_gettime(CLOCK_MONOTONIC, &t);
16684 result.posix.seconds = (uint64_t)t.tv_sec;
16685 result.posix.nanoSeconds = (int64_t)t.tv_nsec;
16686 return(result);
16687}
16688
16690 uint64_t deltaSeconds = finish.posix.seconds - start.posix.seconds;
16691 int64_t deltaNanos = finish.posix.nanoSeconds - start.posix.nanoSeconds;
16692 if (deltaNanos < 0) {
16693 --deltaSeconds;
16694 deltaNanos += 1000000000L;
16695 }
16696 fplSeconds result = (fplSeconds)deltaSeconds + ((fplSeconds)deltaNanos * 1e-9);
16697 return(result);
16698}
16699
16701 struct timeval tv;
16702 gettimeofday(&tv, fpl_null);
16703 fplMilliseconds result = (fplMilliseconds)(tv.tv_sec * 1000 + ((uint64_t)tv.tv_usec / 1000));
16704 return(result);
16705}
16706
16707//
16708// POSIX Threading
16709//
16711 FPL__CheckArgumentNull(thread, false);
16712 FPL__CheckPlatform(false);
16713 const fpl__PlatformAppState *appState = fpl__global__AppState;
16714 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
16715 if (thread->isValid && (fplGetThreadState(thread) != fplThreadState_Stopped)) {
16716 pthread_t threadHandle = thread->internalHandle.posixThread;
16717 if (pthreadApi->pthread_kill(threadHandle, 0) == 0) {
16718 pthreadApi->pthread_join(threadHandle, fpl_null);
16719 }
16720 thread->isValid = false;
16721 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
16722 return true;
16723 } else {
16724 return false;
16725 }
16726}
16727
16729 FPL__CheckPlatform(0);
16730 const fpl__PlatformAppState *appState = fpl__global__AppState;
16731 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
16732 pthread_t currentThread = pthreadApi->pthread_self();
16733 uint32_t result = (uint32_t)currentThread;
16734 return(result);
16735}
16736
16738 FPL__CheckArgumentNull(parameters, fpl_null);
16739 FPL__CheckPlatform(fpl_null);
16740 const fpl__PlatformAppState *appState = fpl__global__AppState;
16741 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
16742 fplThreadHandle *result = fpl_null;
16743 fplThreadHandle *thread = fpl__GetFreeThread();
16744 if (thread != fpl_null) {
16746 thread->parameters = *parameters;
16747 thread->isValid = false;
16748 thread->isStopping = false;
16749
16750 fplThreadPriority initialPriority = parameters->priority;
16751
16752 // Setup attributes
16753 pthread_attr_t *attrPtr = fpl_null;
16754 pthread_attr_t attr;
16755 if (pthreadApi->pthread_attr_init(&attr) == 0) {
16756 // Scheduler policy
16757 int scheduler = -1;
16758 if (initialPriority == fplThreadPriority_Idle) {
16759#if defined(SCHED_IDLE)
16760 if (pthreadApi->pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
16761 scheduler = SCHED_IDLE;
16762 }
16763#endif
16764 } else if (initialPriority >= fplThreadPriority_High) {
16765#if defined(SCHED_FIFO)
16766 if ((scheduler == -1) && (pthreadApi->pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0)) {
16767 scheduler = SCHED_FIFO;
16768 }
16769#endif
16770#if defined(SCHED_RR)
16771 if ((scheduler == -1) && (pthreadApi->pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0)) {
16772 scheduler = SCHED_RR;
16773 }
16774#endif
16775 } else {
16776 // @TODO(final): Is sched_getscheduler supported for all POSIX standards?
16777 scheduler = sched_getscheduler(0);
16778 }
16779
16780 // Stack size
16781 if (parameters->stackSize > 0) {
16782 pthreadApi->pthread_attr_setstacksize(&attr, parameters->stackSize);
16783 }
16784
16785 // Priority
16786 if (scheduler != -1) {
16787 struct sched_param sched;
16788 if (pthreadApi->pthread_attr_getschedparam(&attr, &sched) == 0) {
16789 int maxThreadPrioCount = (fplThreadPriority_Last - fplThreadPriority_First) + 1;
16790 fplAssert(maxThreadPrioCount > 0);
16791
16792 int minPrio = sched_get_priority_min(scheduler);
16793 int maxPrio = sched_get_priority_max(scheduler);
16794 int range = maxPrio - minPrio;
16795 int step = range / maxThreadPrioCount;
16796
16797 int priority;
16798 if (initialPriority == fplThreadPriority_Lowest) {
16799 priority = minPrio;
16800 } else if (initialPriority == fplThreadPriority_Highest) {
16801 priority = maxPrio;
16802 } else {
16803 int threadPrioNumber = (int)(initialPriority - fplThreadPriority_First) + 1;
16804 priority = minPrio + threadPrioNumber * step;
16805 if (priority < minPrio) {
16806 priority = minPrio;
16807 } else if (priority > maxPrio) {
16808 priority = maxPrio;
16809 }
16810 }
16811
16812 sched.sched_priority = priority;
16813 pthreadApi->pthread_attr_setschedparam(&attr, &sched);
16814 }
16815 }
16816
16817 attrPtr = &attr;
16818 }
16819
16820 // Create thread
16822 int threadRes;
16823 do {
16824 threadRes = pthreadApi->pthread_create(&thread->internalHandle.posixThread, attrPtr, fpl__PosixThreadProc, (void *)thread);
16825 } while (threadRes == EAGAIN);
16826 if (threadRes != 0) {
16827 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating thread, error code: %d", threadRes);
16828 }
16829 if (threadRes == 0) {
16830 thread->id = (uint32_t)thread->internalHandle.posixThread;
16831 thread->isValid = true;
16832 result = thread;
16833 } else {
16834 fplClearStruct(thread);
16835 }
16836 } else {
16837 FPL__ERROR(FPL__MODULE_THREADING, "All %d threads are in use, you cannot create until you free one", FPL_MAX_THREAD_COUNT);
16838 }
16839 return(result);
16840}
16841
16843 FPL__CheckArgumentNull(runFunc, fpl_null);
16844 fplThreadParameters parameters = fplZeroInit;
16845 parameters.runFunc = runFunc;
16846 parameters.userData = data;
16847 fplThreadHandle *result = fplThreadCreateWithParameters(&parameters);
16848 return(result);
16849}
16850
16852 FPL__CheckPlatform(fplThreadPriority_Unknown);
16853 const fpl__PlatformAppState *appState = fpl__global__AppState;
16854 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
16855
16856 pthread_t curThread = pthreadApi->pthread_self();
16857
16858 int currentSchedulerPolicy;
16859 struct sched_param params;
16860 if (pthreadApi->pthread_getschedparam(curThread, &currentSchedulerPolicy, &params) != 0) {
16861 FPL__ERROR(FPL__MODULE_THREADING, "Failed getting scheduler parameters for pthread '%d'", curThread);
16863 }
16864
16865 int maxThreadPrioCount = (fplThreadPriority_Last - fplThreadPriority_First) + 1;
16866 fplAssert(maxThreadPrioCount > 0);
16867
16868 int minPrio = sched_get_priority_min(currentSchedulerPolicy);
16869 int maxPrio = sched_get_priority_max(currentSchedulerPolicy);
16870 int range = maxPrio - minPrio;
16871 int step = range / maxThreadPrioCount;
16872
16873 int currentPrio = params.sched_priority;
16874
16875 fplThreadPriority result;
16876 if (minPrio == maxPrio || currentPrio == minPrio) {
16877 result = fplThreadPriority_Lowest;
16878 } else if (currentPrio == maxPrio) {
16880 } else {
16881 int index = (currentPrio - minPrio) / step;
16882 fplAssert(index >= 0 && index < maxThreadPrioCount);
16883 result = (fplThreadPriority)index;
16884 }
16885
16886 return(result);
16887}
16888
16890 if (newPriority == fplThreadPriority_Unknown) return(false);
16891 FPL__CheckPlatform(false);
16892 const fpl__PlatformAppState *appState = fpl__global__AppState;
16893 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
16894
16895 pthread_t curThread = pthreadApi->pthread_self();
16896
16897 int currentSchedulerPolicy;
16898 struct sched_param params;
16899 if (pthreadApi->pthread_getschedparam(curThread, &currentSchedulerPolicy, &params) != 0) {
16900 FPL__ERROR(FPL__MODULE_THREADING, "Failed getting scheduler parameters for pthread '%d'", curThread);
16901 return(false);
16902 }
16903
16904 // Build policy table
16905 int newSchedulerPolicies[3];
16906 int schedulerPolicyCount = 0;
16907 switch (newPriority) {
16911 newSchedulerPolicies[schedulerPolicyCount++] = currentSchedulerPolicy;
16912 break;
16914#if defined(SCHED_RR)
16915 newSchedulerPolicies[schedulerPolicyCount++] = SCHED_RR;
16916#endif
16917 newSchedulerPolicies[schedulerPolicyCount++] = currentSchedulerPolicy;
16918 break;
16920#if defined(SCHED_FIFO)
16921 newSchedulerPolicies[schedulerPolicyCount++] = SCHED_FIFO;
16922#endif
16923#if defined(SCHED_RR)
16924 newSchedulerPolicies[schedulerPolicyCount++] = SCHED_RR;
16925#endif
16926 newSchedulerPolicies[schedulerPolicyCount++] = currentSchedulerPolicy;
16927 break;
16928 default:
16929 break;
16930 }
16931
16932 int maxThreadPrioCount = (fplThreadPriority_Last - fplThreadPriority_First) + 1;
16933 fplAssert(maxThreadPrioCount > 0);
16934
16935 // Bring priority in range of 1-N
16936 int threadPrioNumber = (int)(newPriority - fplThreadPriority_First) + 1;
16937
16938 for (int i = 0; i < schedulerPolicyCount; ++i) {
16939 int policy = newSchedulerPolicies[i];
16940 int minPrio = sched_get_priority_min(policy);
16941 int maxPrio = sched_get_priority_max(policy);
16942 int range = maxPrio - minPrio;
16943 int step = range / maxThreadPrioCount;
16944
16945 int priority;
16946 if (newPriority == fplThreadPriority_Lowest) {
16947 priority = minPrio;
16948 } else if (newPriority == fplThreadPriority_Highest) {
16949 priority = maxPrio;
16950 } else {
16951 priority = minPrio + threadPrioNumber * step;
16952 if (priority < minPrio) {
16953 priority = minPrio;
16954 } else if (priority > maxPrio) {
16955 priority = maxPrio;
16956 }
16957 }
16958 params.sched_priority = priority;
16959 if (pthreadApi->pthread_setschedparam(curThread, policy, &params) == 0) {
16960 return(true); // Finally we found a policy and priority which is supported
16961 } else {
16962 FPL__WARNING(FPL__MODULE_THREADING, "Failed to set thread priority '%d' with policy '%d'", priority, policy);
16963 }
16964 }
16965
16966 return(false);
16967}
16968
16970 FPL__CheckPlatform(false);
16971 const fpl__PlatformAppState *appState = fpl__global__AppState;
16972 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
16973 bool result = false;
16974 if ((thread != fpl_null) && (fplGetThreadState(thread) != fplThreadState_Stopped)) {
16975 pthread_t threadHandle = thread->internalHandle.posixThread;
16976
16977 // @NOTE(final): We optionally use the GNU extension "pthread_timedjoin_np" to support joining with a timeout.
16978 int joinRes;
16979 if ((pthreadApi->pthread_timedjoin_np != fpl_null) && (timeout != FPL_TIMEOUT_INFINITE)) {
16980 struct timespec t;
16981 fpl__InitWaitTimeSpec(timeout, &t);
16982 joinRes = pthreadApi->pthread_timedjoin_np(threadHandle, fpl_null, &t);
16983 } else {
16984 joinRes = pthreadApi->pthread_join(threadHandle, fpl_null);
16985 }
16986
16987 result = (joinRes == 0);
16988 }
16989 return (result);
16990}
16991
16992fpl_platform_api bool fplThreadWaitForAll(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
16993 bool result = fpl__PosixThreadWaitForMultiple(threads, count, count, stride, timeout);
16994 return(result);
16995}
16996
16997fpl_platform_api bool fplThreadWaitForAny(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
16998 bool result = fpl__PosixThreadWaitForMultiple(threads, 1, count, stride, timeout);
16999 return(result);
17000}
17001
17003 FPL__CheckPlatform(false);
17004 const fpl__PlatformAppState *appState = fpl__global__AppState;
17005 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17006 bool result;
17007 if (pthreadApi->pthread_yield != fpl_null) {
17008 result = (pthreadApi->pthread_yield() == 0);
17009 } else {
17010 result = (sched_yield() == 0);
17011 }
17012 return(result);
17013}
17014
17015fpl_platform_api void fplThreadSleep(const uint32_t milliseconds) {
17016 uint32_t ms;
17017 uint32_t s;
17018 if (milliseconds > 1000) {
17019 s = milliseconds / 1000;
17020 ms = milliseconds % 1000;
17021 } else {
17022 s = 0;
17023 ms = milliseconds;
17024 }
17025 struct timespec input, output;
17026 input.tv_sec = s;
17027 input.tv_nsec = ms * 1000000;
17028 nanosleep(&input, &output);
17029}
17030
17032 FPL__CheckArgumentNull(mutex, false);
17033 if (mutex->isValid) {
17034 FPL__ERROR(FPL__MODULE_THREADING, "Mutex '%p' is already initialized", mutex);
17035 return false;
17036 }
17037 FPL__CheckPlatform(false);
17038 const fpl__PlatformAppState *appState = fpl__global__AppState;
17039 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17040 fplAssert(sizeof(fpl__POSIXMutexHandle) >= sizeof(pthread_mutex_t));
17041 pthread_mutex_t mutexHandle;
17042 int mutexRes = fpl__PosixMutexCreate(pthreadApi, &mutexHandle);
17043 if (mutexRes != 0) {
17044 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating POSIX condition");
17045 return false;
17046 }
17047 fplClearStruct(mutex);
17048 fplMemoryCopy(&mutexHandle, sizeof(mutexHandle), &mutex->internalHandle.posixMutex);
17049 mutex->isValid = true;
17050 return(true);
17051}
17052
17054 FPL__CheckPlatformNoRet();
17055 const fpl__PlatformAppState *appState = fpl__global__AppState;
17056 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17057 if ((mutex != fpl_null) && mutex->isValid) {
17058 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
17059 pthreadApi->pthread_mutex_destroy(handle);
17060 fplClearStruct(mutex);
17061 }
17062}
17063
17065 FPL__CheckArgumentNull(mutex, false);
17066 FPL__CheckPlatform(false);
17067 const fpl__PlatformAppState *appState = fpl__global__AppState;
17068 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17069 bool result = false;
17070 if (mutex->isValid) {
17071 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
17072 result = fpl__PosixMutexLock(pthreadApi, handle);
17073 }
17074 return (result);
17075}
17076
17078 FPL__CheckArgumentNull(mutex, false);
17079 FPL__CheckPlatform(false);
17080 const fpl__PlatformAppState *appState = fpl__global__AppState;
17081 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17082 bool result = false;
17083 if (mutex->isValid) {
17084 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
17085 result = fpl__PosixMutexTryLock(pthreadApi, handle);
17086 }
17087 return (result);
17088}
17089
17091 FPL__CheckArgumentNull(mutex, false);
17092 FPL__CheckPlatform(false);
17093 const fpl__PlatformAppState *appState = fpl__global__AppState;
17094 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17095 bool result = false;
17096 if (mutex->isValid) {
17097 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
17098 result = fpl__PosixMutexUnlock(pthreadApi, handle);
17099 }
17100 return (result);
17101}
17102
17104 FPL__CheckArgumentNull(condition, false);
17105 FPL__CheckPlatform(false);
17106 const fpl__PlatformAppState *appState = fpl__global__AppState;
17107 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17108 fplAssert(sizeof(fpl__POSIXConditionVariable) >= sizeof(pthread_cond_t));
17109 pthread_cond_t handle = PTHREAD_COND_INITIALIZER;
17110 int condRes;
17111 do {
17112 condRes = pthreadApi->pthread_cond_init(&handle, fpl_null);
17113 } while (condRes == EAGAIN);
17114 if (condRes == 0) {
17115 fplClearStruct(condition);
17116 fplMemoryCopy(&handle, sizeof(handle), &condition->internalHandle.posixCondition);
17117 condition->isValid = true;
17118 }
17119 return(condition->isValid);
17120}
17121
17123 FPL__CheckPlatformNoRet();
17124 const fpl__PlatformAppState *appState = fpl__global__AppState;
17125 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17126 if ((condition != fpl_null) && condition->isValid) {
17127 pthread_cond_t *handle = (pthread_cond_t *)&condition->internalHandle.posixCondition;
17128 pthreadApi->pthread_cond_destroy(handle);
17129 fplClearStruct(condition);
17130 }
17131}
17132
17134 FPL__CheckArgumentNull(condition, false);
17135 FPL__CheckArgumentNull(mutex, false);
17136 if (!condition->isValid) {
17137 FPL__ERROR(FPL__MODULE_THREADING, "Condition is not valid");
17138 return false;
17139 }
17140 if (!mutex->isValid) {
17141 FPL__ERROR(FPL__MODULE_THREADING, "Mutex is not valid");
17142 return false;
17143 }
17144 FPL__CheckPlatform(false);
17145 const fpl__PlatformAppState *appState = fpl__global__AppState;
17146 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17147 pthread_cond_t *cond = (pthread_cond_t *)&condition->internalHandle.posixCondition;
17148 pthread_mutex_t *mut = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
17149 bool result;
17150 if (timeout == FPL_TIMEOUT_INFINITE) {
17151 result = pthreadApi->pthread_cond_wait(cond, mut) == 0;
17152 } else {
17153 struct timespec t;
17154 fpl__InitWaitTimeSpec(timeout, &t);
17155 result = pthreadApi->pthread_cond_timedwait(cond, mut, &t) == 0;
17156 }
17157 return(result);
17158}
17159
17161 FPL__CheckArgumentNull(condition, false);
17162 if (!condition->isValid) {
17163 FPL__ERROR(FPL__MODULE_THREADING, "Condition is not valid");
17164 return false;
17165 }
17166 FPL__CheckPlatform(false);
17167 const fpl__PlatformAppState *appState = fpl__global__AppState;
17168 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17169 pthread_cond_t *handle = (pthread_cond_t *)&condition->internalHandle.posixCondition;
17170 bool result = pthreadApi->pthread_cond_signal(handle) == 0;
17171 return(result);
17172}
17173
17175 FPL__CheckArgumentNull(condition, false);
17176 if (!condition->isValid) {
17177 FPL__ERROR(FPL__MODULE_THREADING, "Condition is not valid");
17178 return false;
17179 }
17180 FPL__CheckPlatform(false);
17181 const fpl__PlatformAppState *appState = fpl__global__AppState;
17182 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17183 pthread_cond_t *handle = (pthread_cond_t *)&condition->internalHandle.posixCondition;
17184 bool result = pthreadApi->pthread_cond_broadcast(handle) == 0;
17185 return(result);
17186}
17187
17188fpl_platform_api bool fplSemaphoreInit(fplSemaphoreHandle *semaphore, const uint32_t initialValue) {
17189 FPL__CheckArgumentNull(semaphore, false);
17190 FPL__CheckArgumentMax(initialValue, UINT32_MAX, false);
17191 if (semaphore->isValid) {
17192 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is already initialized", semaphore);
17193 return false;
17194 }
17195 FPL__CheckPlatform(false);
17196
17197 const fpl__PlatformAppState *appState = fpl__global__AppState;
17198 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17199 sem_t handle;
17200 int res = pthreadApi->sem_init(&handle, 0, (int)initialValue);
17201 if (res < 0) {
17202 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating semaphore");
17203 return false;
17204 }
17205 fplClearStruct(semaphore);
17206 fplMemoryCopy(&handle, sizeof(handle), &semaphore->internalHandle.posixHandle);
17207 semaphore->isValid = true;
17208 return true;
17209}
17210
17212 FPL__CheckPlatformNoRet();
17213 const fpl__PlatformAppState *appState = fpl__global__AppState;
17214 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17215 if (semaphore != fpl_null) {
17216 if (semaphore->isValid) {
17217 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
17218 pthreadApi->sem_destroy(handle);
17219 }
17220 fplClearStruct(semaphore);
17221 }
17222}
17223
17225 FPL__CheckArgumentNull(semaphore, false);
17226 if (!semaphore->isValid) {
17227 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
17228 return false;
17229 }
17230 FPL__CheckPlatform(false);
17231 const fpl__PlatformAppState *appState = fpl__global__AppState;
17232 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17233 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
17234 int res;
17235 if (timeout == FPL_TIMEOUT_INFINITE) {
17236 res = pthreadApi->sem_wait(handle);
17237 } else {
17238 struct timespec t;
17239 fpl__InitWaitTimeSpec(timeout, &t);
17240 res = pthreadApi->sem_timedwait(handle, &t);
17241 }
17242 bool result = res == 0;
17243 return(result);
17244}
17245
17247 FPL__CheckArgumentNull(semaphore, false);
17248 if (!semaphore->isValid) {
17249 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
17250 return false;
17251 }
17252 FPL__CheckPlatform(false);
17253 const fpl__PlatformAppState *appState = fpl__global__AppState;
17254 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17255 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
17256 int res = pthreadApi->sem_trywait(handle);
17257 bool result = (res == 0);
17258 return(result);
17259}
17260
17262 FPL__CheckArgumentNull(semaphore, false);
17263 if (!semaphore->isValid) {
17264 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
17265 return false;
17266 }
17267 FPL__CheckPlatform(0);
17268 const fpl__PlatformAppState *appState = fpl__global__AppState;
17269 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17270 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
17271 int value = 0;
17272 int res = pthreadApi->sem_getvalue(handle, &value);
17273 if (res < 0) {
17274 return 0;
17275 }
17276 return((int32_t)value);
17277}
17278
17280 FPL__CheckArgumentNull(semaphore, false);
17281 if (!semaphore->isValid) {
17282 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
17283 return false;
17284 }
17285 FPL__CheckPlatform(0);
17286 const fpl__PlatformAppState *appState = fpl__global__AppState;
17287 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
17288 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
17289 int res = pthreadApi->sem_post(handle);
17290 bool result = (res == 0);
17291 return(result);
17292}
17293
17294//
17295// POSIX Library
17296//
17297fpl_platform_api bool fplDynamicLibraryLoad(const char *libraryFilePath, fplDynamicLibraryHandle *outHandle) {
17298 bool result = false;
17299 if (libraryFilePath != fpl_null && outHandle != fpl_null) {
17300 void *p = dlopen(libraryFilePath, FPL__POSIX_DL_LOADTYPE);
17301 if (p != fpl_null) {
17302 fplClearStruct(outHandle);
17303 outHandle->internalHandle.posixLibraryHandle = p;
17304 outHandle->isValid = true;
17305 result = true;
17306 }
17307 }
17308 return(result);
17309}
17310
17311fpl_platform_api void *fplGetDynamicLibraryProc(const fplDynamicLibraryHandle *handle, const char *name) {
17312 void *result = fpl_null;
17313 if ((handle != fpl_null) && (handle->internalHandle.posixLibraryHandle != fpl_null) && (name != fpl_null)) {
17314 void *p = handle->internalHandle.posixLibraryHandle;
17315 result = dlsym(p, name);
17316 }
17317 return(result);
17318}
17319
17321 if ((handle != fpl_null) && (handle->internalHandle.posixLibraryHandle != fpl_null)) {
17322 void *p = handle->internalHandle.posixLibraryHandle;
17323 dlclose(p);
17324 fplClearStruct(handle);
17325 }
17326}
17327
17328//
17329// POSIX Memory
17330//
17331fpl_platform_api void *fplMemoryAllocate(const size_t size) {
17332 FPL__CheckArgumentZero(size, fpl_null);
17333 // @NOTE(final): MAP_ANONYMOUS ensures that the memory is cleared to zero.
17334 // Allocate empty memory to hold the size + some arbitary padding + the actual data
17335 size_t newSize = sizeof(size_t) + FPL__MEMORY_PADDING + size;
17336 void *basePtr = mmap(fpl_null, newSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
17337 // Write the size at the beginning
17338 *(size_t *)basePtr = newSize;
17339 // The resulting address starts after the arbitary padding
17340 void *result = (uint8_t *)basePtr + sizeof(size_t) + FPL__MEMORY_PADDING;
17341 return(result);
17342}
17343
17344fpl_platform_api void fplMemoryFree(void *ptr) {
17345 FPL__CheckArgumentNullNoRet(ptr);
17346 // Free the base pointer which is stored to the left at the start of the size_t
17347 void *basePtr = (void *)((uint8_t *)ptr - (FPL__MEMORY_PADDING + sizeof(size_t)));
17348 size_t storedSize = *(size_t *)basePtr;
17349 munmap(basePtr, storedSize);
17350}
17351
17352//
17353// POSIX Files
17354//
17355fpl_platform_api bool fplFileOpenBinary(const char *filePath, fplFileHandle *outHandle) {
17356 FPL__CheckArgumentNull(outHandle, false);
17357 if (filePath != fpl_null) {
17358 int posixFileHandle;
17359 do {
17360 posixFileHandle = open(filePath, O_RDONLY);
17361 } while (posixFileHandle == -1 && errno == EINTR);
17362 if (posixFileHandle != -1) {
17363 fplClearStruct(outHandle);
17364 outHandle->isValid = true;
17365 outHandle->internalHandle.posixFileHandle = posixFileHandle;
17366 return true;
17367 }
17368 }
17369 return false;
17370}
17371
17372fpl_platform_api bool fplFileCreateBinary(const char *filePath, fplFileHandle *outHandle) {
17373 FPL__CheckArgumentNull(outHandle, false);
17374 if (filePath != fpl_null) {
17375 int posixFileHandle;
17376 do {
17377 posixFileHandle = open(filePath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
17378 } while (posixFileHandle == -1 && errno == EINTR);
17379 if (posixFileHandle != -1) {
17380 outHandle->isValid = true;
17381 outHandle->internalHandle.posixFileHandle = posixFileHandle;
17382 return true;
17383 }
17384 }
17385 return false;
17386}
17387
17388fpl_platform_api size_t fpl__PosixWriteFileBlock(const fplFileHandle *fileHandle, void *sourceBuffer, const size_t sourceSize) {
17389 FPL__CheckArgumentNull(fileHandle, 0);
17390 FPL__CheckArgumentZero(sourceSize, 0);
17391 FPL__CheckArgumentNull(sourceBuffer, 0);
17392 if (!fileHandle->internalHandle.posixFileHandle) {
17393 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for writing");
17394 return 0;
17395 }
17396 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17397 ssize_t res;
17398 do {
17399 res = write(posixFileHandle, sourceBuffer, sourceSize);
17400 } while (res == -1 && errno == EINTR);
17401 size_t result = 0;
17402 if (res != -1) {
17403 result = (size_t)res;
17404 }
17405 return(result);
17406}
17407
17408fpl_platform_api uint32_t fplFileReadBlock32(const fplFileHandle *fileHandle, const uint32_t sizeToRead, void *targetBuffer, const uint32_t maxTargetBufferSize) {
17409 FPL__CheckArgumentNull(fileHandle, 0);
17410 FPL__CheckArgumentZero(sizeToRead, 0);
17411 FPL__CheckArgumentNull(targetBuffer, 0);
17412 if (!fileHandle->internalHandle.posixFileHandle) {
17413 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for reading");
17414 return 0;
17415 }
17416 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17417 ssize_t res;
17418 do {
17419 res = read(posixFileHandle, targetBuffer, sizeToRead);
17420 } while (res == -1 && errno == EINTR);
17421 uint32_t result = 0;
17422 if (res != -1) {
17423 result = (uint32_t)res;
17424 }
17425 return(result);
17426}
17427
17428fpl_platform_api uint64_t fplFileReadBlock64(const fplFileHandle *fileHandle, const uint64_t sizeToRead, void *targetBuffer, const uint64_t maxTargetBufferSize) {
17429 FPL__CheckArgumentNull(fileHandle, 0);
17430 FPL__CheckArgumentZero(sizeToRead, 0);
17431 FPL__CheckArgumentNull(targetBuffer, 0);
17432 if (!fileHandle->internalHandle.posixFileHandle) {
17433 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for reading");
17434 return 0;
17435 }
17436 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17437 uint64_t result = 0;
17438 uint64_t remainingSize = sizeToRead;
17439 uint64_t bufferPos = 0;
17440 const uint64_t MaxValue = (uint64_t)(size_t)-1;
17441 while (remainingSize > 0) {
17442 uint8_t *target = (uint8_t *)targetBuffer + bufferPos;
17443 size_t size = fplMin(remainingSize, MaxValue);
17444 ssize_t res;
17445 do {
17446 res = read(posixFileHandle, target, size);
17447 } while (res == -1 && errno == EINTR);
17448 if (res != -1) {
17449 result += res;
17450 } else {
17451 break;
17452 }
17453 remainingSize -= res;
17454 bufferPos += res;
17455 }
17456 return(result);
17457}
17458
17459fpl_platform_api uint32_t fplFileWriteBlock32(const fplFileHandle *fileHandle, void *sourceBuffer, const uint32_t sourceSize) {
17460 FPL__CheckArgumentNull(fileHandle, 0);
17461 FPL__CheckArgumentZero(sourceSize, 0);
17462 FPL__CheckArgumentNull(sourceBuffer, 0);
17463 if (!fileHandle->internalHandle.posixFileHandle) {
17464 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for writing");
17465 return 0;
17466 }
17467 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17468 ssize_t res;
17469 do {
17470 res = write(posixFileHandle, sourceBuffer, sourceSize);
17471 } while (res == -1 && errno == EINTR);
17472 uint32_t result = 0;
17473 if (res != -1) {
17474 result = (uint32_t)res;
17475 }
17476 return(result);
17477}
17478
17479fpl_platform_api uint64_t fplFileWriteBlock64(const fplFileHandle *fileHandle, void *sourceBuffer, const uint64_t sourceSize) {
17480 FPL__CheckArgumentNull(fileHandle, 0);
17481 FPL__CheckArgumentZero(sourceSize, 0);
17482 FPL__CheckArgumentNull(sourceBuffer, 0);
17483 if (!fileHandle->internalHandle.posixFileHandle) {
17484 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for writing");
17485 return 0;
17486 }
17487 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17488 uint64_t result = 0;
17489 uint64_t remainingSize = sourceSize;
17490 uint64_t bufferPos = 0;
17491 const uint64_t MaxValue = (uint64_t)(size_t)-1;
17492 while (remainingSize > 0) {
17493 uint8_t *source = (uint8_t *)sourceBuffer + bufferPos;
17494 size_t size = fplMin(remainingSize, MaxValue);
17495 ssize_t res;
17496 do {
17497 res = write(posixFileHandle, source, size);
17498 } while (res == -1 && errno == EINTR);
17499 if (res != -1) {
17500 result += res;
17501 }
17502 remainingSize -= res;
17503 bufferPos += res;
17504 }
17505 return(result);
17506}
17507
17508fpl_platform_api uint32_t fplFileSetPosition32(const fplFileHandle *fileHandle, const int32_t position, const fplFilePositionMode mode) {
17509 FPL__CheckArgumentNull(fileHandle, 0);
17510 uint32_t result = 0;
17511 if (fileHandle->internalHandle.posixFileHandle) {
17512 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17513 int whence = SEEK_SET;
17514 if (mode == fplFilePositionMode_Current) {
17515 whence = SEEK_CUR;
17516 } else if (mode == fplFilePositionMode_End) {
17517 whence = SEEK_END;
17518 }
17519 off_t r = lseek(posixFileHandle, position, whence);
17520 result = (uint32_t)r;
17521 }
17522 return(result);
17523}
17524
17525fpl_platform_api uint64_t fplFileSetPosition64(const fplFileHandle *fileHandle, const int64_t position, const fplFilePositionMode mode) {
17526 FPL__CheckArgumentNull(fileHandle, 0);
17527 uint64_t result = 0;
17528 if (fileHandle->internalHandle.posixFileHandle) {
17529 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17530 int whence = SEEK_SET;
17531 if (mode == fplFilePositionMode_Current) {
17532 whence = SEEK_CUR;
17533 } else if (mode == fplFilePositionMode_End) {
17534 whence = SEEK_END;
17535 }
17536 fpl__off64_t r = fpl__lseek64(posixFileHandle, position, whence);
17537 result = (uint64_t)r;
17538 }
17539 return(result);
17540}
17541
17542fpl_platform_api uint32_t fplFileGetPosition32(const fplFileHandle *fileHandle) {
17543 FPL__CheckArgumentNull(fileHandle, 0);
17544 uint32_t result = 0;
17545 if (fileHandle->internalHandle.posixFileHandle) {
17546 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17547 off_t res = lseek(posixFileHandle, 0, SEEK_CUR);
17548 if (res != -1) {
17549 result = (uint32_t)res;
17550 }
17551 }
17552 return(result);
17553}
17554
17555fpl_platform_api uint64_t fplFileGetPosition64(const fplFileHandle *fileHandle) {
17556 FPL__CheckArgumentNull(fileHandle, 0);
17557 uint64_t result = 0;
17558 if (fileHandle->internalHandle.posixFileHandle) {
17559 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17560 fpl__off64_t r = fpl__lseek64(posixFileHandle, 0, SEEK_CUR);
17561 if (r != -1) {
17562 result = (uint64_t)r;
17563 }
17564 }
17565 return(result);
17566}
17567
17569 FPL__CheckArgumentNull(fileHandle, false);
17570 bool result = false;
17571 if (fileHandle->internalHandle.posixFileHandle) {
17572 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17573 result = fsync(posixFileHandle) == 0;
17574 }
17575 return(result);
17576}
17577
17579 if ((fileHandle != fpl_null) && fileHandle->internalHandle.posixFileHandle) {
17580 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17581 close(posixFileHandle);
17582 fplClearStruct(fileHandle);
17583 }
17584}
17585
17586fpl_platform_api uint32_t fplFileGetSizeFromPath32(const char *filePath) {
17587 uint32_t result = 0;
17588 if (filePath != fpl_null) {
17589 int posixFileHandle;
17590 do {
17591 posixFileHandle = open(filePath, O_RDONLY);
17592 } while (posixFileHandle == -1 && errno == EINTR);
17593 if (posixFileHandle != -1) {
17594 off_t res = lseek(posixFileHandle, 0, SEEK_END);
17595 if (res != -1) {
17596 result = (uint32_t)res;
17597 }
17598 close(posixFileHandle);
17599 }
17600 }
17601 return(result);
17602}
17603
17604fpl_platform_api uint64_t fplFileGetSizeFromPath64(const char *filePath) {
17605 uint64_t result = 0;
17606 if (filePath != fpl_null) {
17607 int posixFileHandle;
17608 do {
17609 posixFileHandle = open(filePath, O_RDONLY);
17610 } while (posixFileHandle == -1 && errno == EINTR);
17611 if (posixFileHandle != -1) {
17612 fpl__off64_t r = fpl__lseek64(posixFileHandle, 0, SEEK_END);
17613 if (r != -1) {
17614 result = (uint64_t)r;
17615 }
17616 close(posixFileHandle);
17617 }
17618 }
17619 return(result);
17620}
17621
17622fpl_platform_api uint32_t fplFileGetSizeFromHandle32(const fplFileHandle *fileHandle) {
17623 uint32_t result = 0;
17624 if (fileHandle != fpl_null && fileHandle->internalHandle.posixFileHandle) {
17625 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17626 off_t curPos = lseek(posixFileHandle, 0, SEEK_CUR);
17627 if (curPos != -1) {
17628 result = (uint32_t)lseek(posixFileHandle, 0, SEEK_END);
17629 lseek(posixFileHandle, curPos, SEEK_SET);
17630 }
17631 }
17632 return(result);
17633}
17634
17635fpl_platform_api uint64_t fplFileGetSizeFromHandle64(const fplFileHandle *fileHandle) {
17636 uint64_t result = 0;
17637 if (fileHandle != fpl_null && fileHandle->internalHandle.posixFileHandle) {
17638 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17639 fpl__off64_t curPos = fpl__lseek64(posixFileHandle, 0, SEEK_CUR);
17640 if (curPos != -1) {
17641 result = (uint64_t)fpl__lseek64(posixFileHandle, 0, SEEK_END);
17642 fpl__lseek64(posixFileHandle, curPos, SEEK_SET);
17643 }
17644 }
17645 return(result);
17646}
17647
17648fpl_internal uint64_t fpl__PosixConvertTimeToUnixTimeStamp(const time_t secs) {
17649 uint64_t result = (uint64_t)secs;
17650 return(result);
17651}
17652
17653fpl_platform_api bool fplFileGetTimestampsFromPath(const char *filePath, fplFileTimeStamps *outStamps) {
17654 FPL__CheckArgumentNull(outStamps, false);
17655 bool result = false;
17656 if (filePath != fpl_null) {
17657 struct stat statBuf;
17658 if (stat(filePath, &statBuf) != -1) {
17659 outStamps->creationTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_ctime);
17660 outStamps->lastAccessTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_atime);
17661 outStamps->lastModifyTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_mtime);
17662 result = true;
17663 }
17664 }
17665 return(result);
17666}
17667
17669 FPL__CheckArgumentNull(fileHandle, false);
17670 FPL__CheckArgumentNull(outStamps, false);
17671 bool result = false;
17672 if (fileHandle != fpl_null && fileHandle->internalHandle.posixFileHandle) {
17673 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
17674 struct stat statBuf;
17675 if (fstat(posixFileHandle, &statBuf) != -1) {
17676 outStamps->creationTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_ctime);
17677 outStamps->lastAccessTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_atime);
17678 outStamps->lastModifyTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_mtime);
17679 result = true;
17680 }
17681 }
17682 return(result);
17683}
17684
17685fpl_platform_api bool fplFileExists(const char *filePath) {
17686 bool result = false;
17687 if (filePath != fpl_null) {
17688 result = access(filePath, F_OK) != -1;
17689 }
17690 return(result);
17691}
17692
17693fpl_platform_api bool fplFileCopy(const char *sourceFilePath, const char *targetFilePath, const bool overwrite) {
17694 FPL__CheckArgumentNull(sourceFilePath, false);
17695 FPL__CheckArgumentNull(targetFilePath, false);
17696 if (access(sourceFilePath, F_OK) == -1) {
17697 FPL__ERROR(FPL__MODULE_FILES, "Source file '%s' does not exits", sourceFilePath);
17698 return false;
17699 }
17700 if (!overwrite && access(sourceFilePath, F_OK) != -1) {
17701 FPL__ERROR(FPL__MODULE_FILES, "Target file '%s' already exits", targetFilePath);
17702 return false;
17703 }
17704 int inputFileHandle;
17705 do {
17706 inputFileHandle = open(sourceFilePath, O_RDONLY);
17707 } while (inputFileHandle == -1 && errno == EINTR);
17708 if (inputFileHandle == -1) {
17709 FPL__ERROR(FPL__MODULE_FILES, "Failed open source file '%s', error code: %d", sourceFilePath, inputFileHandle);
17710 return false;
17711 }
17712 int outputFileHandle;
17713 do {
17714 outputFileHandle = open(targetFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
17715 } while (outputFileHandle == -1 && errno == EINTR);
17716 if (outputFileHandle == -1) {
17717 close(inputFileHandle);
17718 FPL__ERROR(FPL__MODULE_FILES, "Failed creating target file '%s', error code: %d", targetFilePath, inputFileHandle);
17719 return false;
17720 }
17721 uint8_t buffer[1024 * 10]; // 10 kb buffer
17722 for (;;) {
17723 ssize_t readbytes;
17724 do {
17725 readbytes = read(inputFileHandle, buffer, fplArrayCount(buffer));
17726 } while (readbytes == -1 && errno == EINTR);
17727 if (readbytes > 0) {
17728 ssize_t writtenBytes;
17729 do {
17730 writtenBytes = write(outputFileHandle, buffer, readbytes);
17731 } while (writtenBytes == -1 && errno == EINTR);
17732 if (writtenBytes <= 0) {
17733 break;
17734 }
17735 } else {
17736 break;
17737 }
17738 }
17739 close(outputFileHandle);
17740 close(inputFileHandle);
17741 return(true);
17742}
17743
17744fpl_platform_api bool fplFileMove(const char *sourceFilePath, const char *targetFilePath) {
17745 FPL__CheckArgumentNull(sourceFilePath, false);
17746 FPL__CheckArgumentNull(targetFilePath, false);
17747 bool result = rename(sourceFilePath, targetFilePath) == 0;
17748 return(result);
17749}
17750
17751fpl_platform_api bool fplFileDelete(const char *filePath) {
17752 FPL__CheckArgumentNull(filePath, false);
17753 bool result = unlink(filePath) == 0;
17754 return(result);
17755}
17756
17757fpl_platform_api bool fplDirectoryExists(const char *path) {
17758 bool result = false;
17759 if (path != fpl_null) {
17760 struct stat sb;
17761 result = (stat(path, &sb) == 0) && S_ISDIR(sb.st_mode);
17762 }
17763 return(result);
17764}
17765
17766fpl_platform_api bool fplDirectoriesCreate(const char *path) {
17767 FPL__CheckArgumentNull(path, false);
17768 bool result = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) == 0;
17769 return(result);
17770}
17771fpl_platform_api bool fplRemoveDirectory(const char *path) {
17772 FPL__CheckArgumentNull(path, false);
17773 bool result = rmdir(path) == 0;
17774 return(result);
17775}
17776
17777fpl_internal void fpl__PosixFillFileEntry(struct dirent *dp, fplFileEntry *entry) {
17778 fplAssert((dp != fpl_null) && (entry != fpl_null));
17779 fplCopyString(dp->d_name, entry->name, fplArrayCount(entry->name));
17782 entry->size = 0;
17783 entry->permissions.umask = 0;
17784 char fullPath[FPL_MAX_PATH_LENGTH];
17785 fplCopyString(entry->internalRoot.rootPath, fullPath, fplArrayCount(fullPath));
17786 fplEnforcePathSeparatorLen(fullPath, fplArrayCount(fullPath));
17787 fplStringAppend(dp->d_name, fullPath, fplArrayCount(fullPath));
17788 struct stat sb;
17789 if (stat(fullPath, &sb) == 0) {
17790 if (S_ISDIR(sb.st_mode)) {
17792 } else if (S_ISREG(sb.st_mode)) {
17793 entry->type = fplFileEntryType_File;
17794 }
17795 entry->size = (size_t)sb.st_size;
17796 if (dp->d_name[0] == '.') {
17797 // @NOTE(final): Any filename starting with dot is hidden in POSIX
17799 }
17800 if (sb.st_mode & S_IRUSR) {
17802 }
17803 if (sb.st_mode & S_IWUSR) {
17805 }
17806 if (sb.st_mode & S_IXUSR) {
17808 }
17809 if (sb.st_mode & S_IRGRP) {
17811 }
17812 if (sb.st_mode & S_IWGRP) {
17814 }
17815 if (sb.st_mode & S_IXGRP) {
17817 }
17818 if (sb.st_mode & S_IROTH) {
17820 }
17821 if (sb.st_mode & S_IWOTH) {
17823 }
17824 if (sb.st_mode & S_IXOTH) {
17826 }
17827 }
17828}
17829
17830fpl_platform_api bool fplDirectoryListBegin(const char *path, const char *filter, fplFileEntry *entry) {
17831 FPL__CheckArgumentNull(path, false);
17832 FPL__CheckArgumentNull(entry, false);
17833 DIR *dir = opendir(path);
17834 if (dir == fpl_null) {
17835 return false;
17836 }
17837 if (fplGetStringLength(filter) == 0) {
17838 filter = "*";
17839 }
17840 fplClearStruct(entry);
17841 entry->internalHandle.posixDirHandle = dir;
17842 entry->internalRoot.rootPath = path;
17843 entry->internalRoot.filter = filter;
17844 bool result = fplDirectoryListNext(entry);
17845 return(result);
17846}
17847
17849 FPL__CheckArgumentNull(entry, false);
17850 bool result = false;
17851 if (entry->internalHandle.posixDirHandle != fpl_null) {
17852 DIR *dirHandle = (DIR *)entry->internalHandle.posixDirHandle;
17853 struct dirent *dp = readdir(dirHandle);
17854 do {
17855 if (dp == fpl_null) {
17856 break;
17857 }
17858 bool isMatch = fplIsStringMatchWildcard(dp->d_name, entry->internalRoot.filter);
17859 if (isMatch) {
17860 break;
17861 }
17862 dp = readdir(dirHandle);
17863 } while (dp != fpl_null);
17864 if (dp == fpl_null) {
17865 closedir(dirHandle);
17866 fplClearStruct(entry);
17867 } else {
17868 fpl__PosixFillFileEntry(dp, entry);
17869 result = true;
17870 }
17871 }
17872 return(result);
17873}
17874
17876 FPL__CheckArgumentNullNoRet(entry);
17877 if (entry->internalHandle.posixDirHandle != fpl_null) {
17878 DIR *dirHandle = (DIR *)entry->internalHandle.posixDirHandle;
17879 closedir(dirHandle);
17880 fplClearStruct(entry);
17881 }
17882}
17883
17884//
17885// POSIX Operating System
17886//
17887fpl_platform_api size_t fplSessionGetUsername(char *nameBuffer, const size_t maxNameBufferLen) {
17888 uid_t uid = geteuid();
17889 struct passwd *pw = getpwuid(uid);
17890 if (pw != fpl_null) {
17891 size_t nameLen = fplGetStringLength(pw->pw_name);
17892 if (nameLen > 0 && nameBuffer != fpl_null) {
17893 size_t requiredLen = nameLen + 1;
17894 FPL__CheckArgumentMin(maxNameBufferLen, requiredLen, 0);
17895 fplCopyStringLen(pw->pw_name, nameLen, nameBuffer, maxNameBufferLen);
17896 }
17897 return(nameLen);
17898 }
17899 return(0);
17900}
17901
17903 size_t result = sysconf(_SC_NPROCESSORS_ONLN);
17904 return(result);
17905}
17906
17907fpl_platform_api size_t fplGetExecutableFilePath(char *destPath, const size_t maxDestLen) {
17908 const char *procNames[] = {
17909 "/proc/self/exe",
17910 "/proc/curproc/exe",
17911 "/proc/curproc/file",
17912 };
17913 char buf[FPL_MAX_PATH_LENGTH];
17914 size_t result = 0;
17915 for (int i = 0; i < fplArrayCount(procNames); ++i) {
17916 const char *procName = procNames[i];
17917 if (readlink(procName, buf, fplArrayCount(buf) - 1)) {
17918 int len = fplGetStringLength(buf);
17919 if (len > 0) {
17920 char *lastP = buf + (len - 1);
17921 char *p = lastP;
17922 while (p != buf) {
17923 if (*p == '/') {
17924 len = (lastP - buf) + 1;
17925 break;
17926 }
17927 --p;
17928 }
17929 result = len;
17930 if (destPath != fpl_null) {
17931 size_t requiredLen = len + 1;
17932 FPL__CheckArgumentMin(maxDestLen, requiredLen, 0);
17933 fplCopyStringLen(buf, len, destPath, maxDestLen);
17934 break;
17935 }
17936 }
17937 }
17938 }
17939 return(result);
17940}
17941
17942fpl_platform_api size_t fplGetHomePath(char *destPath, const size_t maxDestLen) {
17943 const char *homeDir = getenv("HOME");
17944 if (homeDir == fpl_null) {
17945 int userId = getuid();
17946 struct passwd *userPwd = getpwuid(userId);
17947 homeDir = userPwd->pw_dir;
17948 }
17949 size_t result = fplGetStringLength(homeDir);
17950 if (destPath != fpl_null) {
17951 size_t requiredLen = result + 1;
17952 FPL__CheckArgumentMin(maxDestLen, requiredLen, 0);
17953 fplCopyStringLen(homeDir, result, destPath, maxDestLen);
17954 }
17955 return(result);
17956}
17957
17958
17961 struct utsname nameInfos;
17962 if (uname(&nameInfos) == 0) {
17963 const char *machineName = nameInfos.machine;
17964 if (fplIsStringEqual("x86_64", machineName) || fplIsStringEqual("amd64", machineName)) {
17965 result = fplCPUArchType_x86_64;
17966 } else if (fplIsStringEqual("x86", machineName) || fplIsStringEqual("i386", machineName) || fplIsStringEqual("i686", machineName)) {
17967 result = fplCPUArchType_x86;
17968 } else if (fplIsStringEqual("ia64", machineName) || fplIsStringEqual("i686-64", machineName)) {
17969 result = fplCPUArchType_x64;
17970 } else {
17971 if (fplIsStringEqualLen("armv", 4, machineName, 4)) {
17972 const char *m = machineName + 4;
17973 const char *p = m;
17974 while (*p >= '0' && *p <= '9') {
17975 ++p;
17976 }
17977 size_t l = p - m;
17978 int32_t version = fplStringToS32Len(m, l);
17979 if (version == 6) {
17980 result = fplCPUArchType_Arm32;
17981 } else if (version >= 7) {
17982 result = fplCPUArchType_Arm64;
17983 }
17984 }
17985 }
17986 }
17987 return(result);
17988}
17989
17991 bool result = false;
17992 struct utsname nameInfos;
17993 if (uname(&nameInfos) == 0) {
17994 const char *kernelName = nameInfos.sysname;
17995 const char *kernelVersion = nameInfos.release;
17996 const char *systemName = nameInfos.version;
17997 fplCopyString(kernelName, outInfos->osName, fplArrayCount(outInfos->osName));
17998 fplCopyString(systemName, outInfos->distributionName, fplArrayCount(outInfos->distributionName));
17999 fpl__ParseVersionString(kernelVersion, &outInfos->osVersion);
18000
18001 // @TODO(final/POSIX): Get distro version
18002 // /etc/os-release
18003
18004 result = true;
18005 }
18006 return(result);
18007}
18008#endif // FPL_SUBPLATFORM_POSIX
18009
18010// ############################################################################
18011//
18012// > STD_STRINGS_SUBPLATFORM
18013//
18014// Strings Implementation using C Standard Library
18015//
18016// ############################################################################
18017#if defined(FPL_SUBPLATFORM_STD_STRINGS)
18018// @NOTE(final): stdio.h is already included
18019fpl_platform_api size_t fplWideStringToUTF8String(const wchar_t *wideSource, const size_t wideSourceLen, char *utf8Dest, const size_t maxUtf8DestLen) {
18020 // @NOTE(final): Expect locale to be UTF-8
18021 FPL__CheckArgumentNull(wideSource, 0);
18022 FPL__CheckArgumentZero(wideSourceLen, 0);
18023 size_t result = wcstombs(fpl_null, wideSource, wideSourceLen);
18024 if (utf8Dest != fpl_null) {
18025 size_t requiredLen = result + 1;
18026 FPL__CheckArgumentMin(maxUtf8DestLen, requiredLen, 0);
18027 wcstombs(utf8Dest, wideSource, wideSourceLen);
18028 utf8Dest[result] = 0;
18029 }
18030 return(result);
18031}
18032fpl_platform_api size_t fplUTF8StringToWideString(const char *utf8Source, const size_t utf8SourceLen, wchar_t *wideDest, const size_t maxWideDestLen) {
18033 // @NOTE(final): Expect locale to be UTF-8
18034 FPL__CheckArgumentNull(utf8Source, 0);
18035 FPL__CheckArgumentZero(utf8SourceLen, 0);
18036 size_t result = mbstowcs(fpl_null, utf8Source, utf8SourceLen);
18037 if (wideDest != fpl_null) {
18038 size_t requiredLen = result + 1;
18039 FPL__CheckArgumentMin(maxWideDestLen, requiredLen, 0);
18040 mbstowcs(wideDest, utf8Source, utf8SourceLen);
18041 wideDest[result] = 0;
18042 }
18043 return(result);
18044}
18045#endif // FPL_SUBPLATFORM_STD_STRINGS
18046
18047// ############################################################################
18048//
18049// > STD_CONSOLE_SUBPLATFORM
18050//
18051// Console Implementation using C Standard Library
18052//
18053// ############################################################################
18054#if defined(FPL_SUBPLATFORM_STD_CONSOLE)
18055// @NOTE(final): stdio.h is already included
18056fpl_platform_api void fplConsoleOut(const char *text) {
18057 if (text != fpl_null) {
18058 fprintf(stdout, "%s", text);
18059 }
18060}
18061fpl_platform_api void fplConsoleError(const char *text) {
18062 if (text != fpl_null) {
18063 fprintf(stderr, "%s", text);
18064 }
18065}
18067 int c = getchar();
18068 const char result = (c >= 0 && c < 256) ? (char)c : 0;
18069 return(result);
18070}
18071#endif // FPL_SUBPLATFORM_STD_CONSOLE
18072
18073// ############################################################################
18074//
18075// > X11_SUBPLATFORM
18076//
18077// ############################################################################
18078#if defined(FPL_SUBPLATFORM_X11)
18079
18080#define FPL__X11_DEFAULT_WINDOW_WIDTH 400
18081#define FPL__X11_DEFAULT_WINDOW_HEIGHT 400
18082
18083fpl_internal void fpl__X11ReleaseSubplatform(fpl__X11SubplatformState *subplatform) {
18084 fplAssert(subplatform != fpl_null);
18085 fpl__UnloadX11Api(&subplatform->api);
18086}
18087
18088fpl_internal bool fpl__X11InitSubplatform(fpl__X11SubplatformState *subplatform) {
18089 fplAssert(subplatform != fpl_null);
18090 if (!fpl__LoadX11Api(&subplatform->api)) {
18091 FPL__ERROR(FPL__MODULE_X11, "Failed loading x11 api");
18092 return false;
18093 }
18094 return true;
18095}
18096
18097fpl_internal void fpl__X11ReleaseWindow(const fpl__X11SubplatformState *subplatform, fpl__X11WindowState *windowState) {
18098 fplAssert((subplatform != fpl_null) && (windowState != fpl_null));
18099 const fpl__X11Api *x11Api = &subplatform->api;
18100 if (windowState->window) {
18101 FPL_LOG_DEBUG("X11", "Hide window '%d' from display '%p'", (int)windowState->window, windowState->display);
18102 x11Api->XUnmapWindow(windowState->display, windowState->window);
18103 FPL_LOG_DEBUG("X11", "Destroy window '%d' on display '%p'", (int)windowState->window, windowState->display);
18104 x11Api->XDestroyWindow(windowState->display, windowState->window);
18105 x11Api->XFlush(windowState->display);
18106 windowState->window = 0;
18107 }
18108 if (windowState->colorMap) {
18109 FPL_LOG_DEBUG("X11", "Release color map '%d' from display '%p'", (int)windowState->colorMap, windowState->display);
18110 x11Api->XFreeColormap(windowState->display, windowState->colorMap);
18111 windowState->colorMap = 0;
18112 }
18113 if (windowState->display) {
18114 FPL_LOG_DEBUG("X11", "Close display '%p'", windowState->display);
18115 x11Api->XCloseDisplay(windowState->display);
18116 windowState->display = fpl_null;
18117
18118#if 0
18119 FPL_LOG_DEBUG("X11", "Restore previous error handler '%p'", windowState->lastErrorHandler);
18120 x11Api->XSetErrorHandler(windowState->lastErrorHandler);
18121#endif
18122
18123 }
18124 fplClearStruct(windowState);
18125}
18126
18127fpl_internal fplKey fpl__X11TranslateKeySymbol(const KeySym keySym) {
18128 switch (keySym) {
18129 case XK_BackSpace:
18130 return fplKey_Backspace;
18131 case XK_Tab:
18132 return fplKey_Tab;
18133
18134 case XK_Return:
18135 return fplKey_Return;
18136
18137 case XK_Pause:
18138 return fplKey_Pause;
18139 case XK_Caps_Lock:
18140 return fplKey_CapsLock;
18141
18142 case XK_Escape:
18143 return fplKey_Escape;
18144 case XK_space:
18145 return fplKey_Space;
18146 case XK_Page_Up:
18147 return fplKey_PageUp;
18148 case XK_Page_Down:
18149 return fplKey_PageDown;
18150 case XK_End:
18151 return fplKey_End;
18152 case XK_Home:
18153 return fplKey_Home;
18154 case XK_Left:
18155 return fplKey_Left;
18156 case XK_Up:
18157 return fplKey_Up;
18158 case XK_Right:
18159 return fplKey_Right;
18160 case XK_Down:
18161 return fplKey_Down;
18162 case XK_Print:
18163 return fplKey_Print;
18164 case XK_Insert:
18165 return fplKey_Insert;
18166 case XK_Delete:
18167 return fplKey_Delete;
18168
18169 case XK_0:
18170 return fplKey_0;
18171 case XK_1:
18172 return fplKey_1;
18173 case XK_2:
18174 return fplKey_2;
18175 case XK_3:
18176 return fplKey_3;
18177 case XK_4:
18178 return fplKey_4;
18179 case XK_5:
18180 return fplKey_5;
18181 case XK_6:
18182 return fplKey_6;
18183 case XK_7:
18184 return fplKey_7;
18185 case XK_8:
18186 return fplKey_8;
18187 case XK_9:
18188 return fplKey_9;
18189
18190 case XK_a:
18191 return fplKey_A;
18192 case XK_b:
18193 return fplKey_B;
18194 case XK_c:
18195 return fplKey_C;
18196 case XK_d:
18197 return fplKey_D;
18198 case XK_e:
18199 return fplKey_E;
18200 case XK_f:
18201 return fplKey_F;
18202 case XK_g:
18203 return fplKey_G;
18204 case XK_h:
18205 return fplKey_H;
18206 case XK_i:
18207 return fplKey_I;
18208 case XK_j:
18209 return fplKey_J;
18210 case XK_k:
18211 return fplKey_K;
18212 case XK_l:
18213 return fplKey_L;
18214 case XK_m:
18215 return fplKey_M;
18216 case XK_n:
18217 return fplKey_N;
18218 case XK_o:
18219 return fplKey_O;
18220 case XK_p:
18221 return fplKey_P;
18222 case XK_q:
18223 return fplKey_Q;
18224 case XK_r:
18225 return fplKey_R;
18226 case XK_s:
18227 return fplKey_S;
18228 case XK_t:
18229 return fplKey_T;
18230 case XK_u:
18231 return fplKey_U;
18232 case XK_v:
18233 return fplKey_V;
18234 case XK_w:
18235 return fplKey_W;
18236 case XK_x:
18237 return fplKey_X;
18238 case XK_y:
18239 return fplKey_Y;
18240 case XK_z:
18241 return fplKey_Z;
18242
18243 case XK_Super_L:
18244 return fplKey_LeftSuper;
18245 case XK_Super_R:
18246 return fplKey_RightSuper;
18247
18248 case XK_KP_0:
18249 return fplKey_NumPad0;
18250 case XK_KP_1:
18251 return fplKey_NumPad1;
18252 case XK_KP_2:
18253 return fplKey_NumPad2;
18254 case XK_KP_3:
18255 return fplKey_NumPad3;
18256 case XK_KP_4:
18257 return fplKey_NumPad4;
18258 case XK_KP_5:
18259 return fplKey_NumPad5;
18260 case XK_KP_6:
18261 return fplKey_NumPad6;
18262 case XK_KP_7:
18263 return fplKey_NumPad7;
18264 case XK_KP_8:
18265 return fplKey_NumPad8;
18266 case XK_KP_9:
18267 return fplKey_NumPad9;
18268 case XK_KP_Multiply:
18269 return fplKey_Multiply;
18270 case XK_KP_Add:
18271 return fplKey_Add;
18272 case XK_KP_Subtract:
18273 return fplKey_Substract;
18274 case XK_KP_Delete:
18275 return fplKey_Decimal;
18276 case XK_KP_Divide:
18277 return fplKey_Divide;
18278 case XK_F1:
18279 return fplKey_F1;
18280 case XK_F2:
18281 return fplKey_F2;
18282 case XK_F3:
18283 return fplKey_F3;
18284 case XK_F4:
18285 return fplKey_F4;
18286 case XK_F5:
18287 return fplKey_F5;
18288 case XK_F6:
18289 return fplKey_F6;
18290 case XK_F7:
18291 return fplKey_F7;
18292 case XK_F8:
18293 return fplKey_F8;
18294 case XK_F9:
18295 return fplKey_F9;
18296 case XK_F10:
18297 return fplKey_F10;
18298 case XK_F11:
18299 return fplKey_F11;
18300 case XK_F12:
18301 return fplKey_F12;
18302 case XK_F13:
18303 return fplKey_F13;
18304 case XK_F14:
18305 return fplKey_F14;
18306 case XK_F15:
18307 return fplKey_F15;
18308 case XK_F16:
18309 return fplKey_F16;
18310 case XK_F17:
18311 return fplKey_F17;
18312 case XK_F18:
18313 return fplKey_F18;
18314 case XK_F19:
18315 return fplKey_F19;
18316 case XK_F20:
18317 return fplKey_F20;
18318 case XK_F21:
18319 return fplKey_F21;
18320 case XK_F22:
18321 return fplKey_F22;
18322 case XK_F23:
18323 return fplKey_F23;
18324 case XK_F24:
18325 return fplKey_F24;
18326
18327 case XK_Shift_L:
18328 return fplKey_LeftShift;
18329 case XK_Shift_R:
18330 return fplKey_RightShift;
18331 case XK_Control_L:
18332 return fplKey_LeftControl;
18333 case XK_Control_R:
18334 return fplKey_RightControl;
18335 case XK_Meta_L:
18336 case XK_Alt_L:
18337 return fplKey_LeftAlt;
18338 case XK_Mode_switch:
18339 case XK_ISO_Level3_Shift:
18340 case XK_Meta_R:
18341 case XK_Alt_R:
18342 return fplKey_RightAlt;
18343
18344 case XK_comma:
18345 return fplKey_OemComma;
18346 case XK_period:
18347 return fplKey_OemPeriod;
18348 case XK_minus:
18349 return fplKey_OemMinus;
18350 case XK_plus:
18351 return fplKey_OemPlus;
18352
18353 // @TODO(final/X11): X11 map OEM1-OEM8 key
18354
18355 default:
18356 return fplKey_None;
18357 }
18358}
18359
18360fpl_internal void fpl__X11LoadWindowIcon(const fpl__X11Api *x11Api, fpl__X11WindowState *x11WinState, fplWindowSettings *windowSettings) {
18361 // @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"
18362
18363 int iconSourceCount = 0;
18364 fplImageSource iconSources[2] = fplZeroInit;
18365
18366 if (windowSettings->icons[0].width > 0) {
18367 iconSources[iconSourceCount++] = windowSettings->icons[0];
18368 }
18369 if (windowSettings->icons[1].width > 0) {
18370 iconSources[iconSourceCount++] = windowSettings->icons[1];
18371 }
18372
18373 if (iconSourceCount > 0) {
18374 int targetSize = 0;
18375 for (int i = 0; i < iconSourceCount; ++i) {
18376 targetSize += 2 + iconSources[i].width * iconSources[i].height;
18377 }
18378
18379 long *data = (long *)fpl__AllocateTemporaryMemory(sizeof(long) * targetSize, 16);
18380 long *target = data;
18381
18382 for (int i = 0; i < iconSourceCount; ++i) {
18383 const fplImageSource *iconSource = iconSources + i;
18384 fplAssert(iconSource->type == fplImageType_RGBA);
18385 *target++ = (int32_t)iconSource->width;
18386 *target++ = (int32_t)iconSource->height;
18387 const uint32_t *source = (const uint32_t *)iconSource->data;
18388 for (int j = 0; j < iconSource->width * iconSource->height; ++j) {
18389 *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);
18390 }
18391 }
18392
18393 x11Api->XChangeProperty(x11WinState->display, x11WinState->window, x11WinState->netWMIcon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, targetSize);
18394
18395 fpl__ReleaseTemporaryMemory(data);
18396 } else {
18397 x11Api->XDeleteProperty(x11WinState->display, x11WinState->window, x11WinState->netWMIcon);
18398 }
18399
18400 x11Api->XFlush(x11WinState->display);
18401}
18402
18403#if 0
18404fpl_internal int fpl__X11ErrorHandler(Display *display, XErrorEvent *ev) {
18405 FPL__CheckPlatform(0);
18406 fpl__PlatformAppState *appState = fpl__global__AppState;
18407 const fpl__X11SubplatformState *subplatform = &appState->x11;
18408 const fpl__X11Api *x11Api = &subplatform->api;
18409 const fpl__X11WindowState *windowState = &appState->window.x11;
18410
18411 if (windowState->lastErrorHandler != fpl_null) {
18412 return windowState->lastErrorHandler(display, ev);
18413 }
18414
18415 return(0);
18416}
18417#endif
18418
18419fpl_internal bool fpl__X11InitWindow(const fplSettings *initSettings, fplWindowSettings *currentWindowSettings, fpl__PlatformAppState *appState, fpl__X11SubplatformState *subplatform, fpl__X11WindowState *windowState, const fpl__SetupWindowCallbacks *setupCallbacks) {
18420 fplAssert((initSettings != fpl_null) && (currentWindowSettings != fpl_null) && (appState != fpl_null) && (subplatform != fpl_null) && (windowState != fpl_null) && (setupCallbacks != fpl_null));
18421 const fpl__X11Api *x11Api = &subplatform->api;
18422
18423 FPL_LOG_DEBUG(FPL__MODULE_X11, "Set init threads");
18424 x11Api->XInitThreads();
18425
18426#if 0
18427 FPL_LOG_DEBUG("X11", "Enable error handler");
18428 windowState->lastErrorHandler = x11Api->XSetErrorHandler(fpl__X11ErrorHandler);
18429#endif
18430
18431 const fplWindowSettings *initWindowSettings = &initSettings->window;
18432
18433 FPL_LOG_DEBUG(FPL__MODULE_X11, "Open default Display");
18434 windowState->display = x11Api->XOpenDisplay(fpl_null);
18435 if (windowState->display == fpl_null) {
18436 FPL__ERROR(FPL__MODULE_X11, "Failed opening default Display!");
18437 return false;
18438 }
18439 FPL_LOG_DEBUG(FPL__MODULE_X11, "Successfully opened default Display: %p", windowState->display);
18440
18441 FPL_LOG_DEBUG(FPL__MODULE_X11, "Get default screen from display '%p'", windowState->display);
18442 windowState->screen = x11Api->XDefaultScreen(windowState->display);
18443 FPL_LOG_DEBUG(FPL__MODULE_X11, "Got default screen from display '%p': %d", windowState->display, windowState->screen);
18444
18445 FPL_LOG_DEBUG(FPL__MODULE_X11, "Get root window from display '%p' and screen '%d'", windowState->display, windowState->screen);
18446 windowState->root = x11Api->XRootWindow(windowState->display, windowState->screen);
18447 FPL_LOG_DEBUG(FPL__MODULE_X11, "Got root window from display '%p' and screen '%d': %d", windowState->display, windowState->screen, (int)windowState->root);
18448
18449 bool usePreSetupWindow = false;
18450 if (setupCallbacks->preSetup != fpl_null) {
18451 FPL_LOG_DEBUG(FPL__MODULE_X11, "Call Pre-Setup for Window");
18452 setupCallbacks->preSetup(appState, appState->initFlags, initSettings);
18453 }
18454
18455 Visual *visual = windowState->visual;
18456 int colorDepth = windowState->colorDepth;
18457 Colormap colormap;
18458 if (visual != fpl_null && colorDepth > 0) {
18459 FPL_LOG_DEBUG(FPL__MODULE_X11, "Got visual '%p' and color depth '%d' from pre-setup", visual, colorDepth);
18460 windowState->colorMap = colormap = x11Api->XCreateColormap(windowState->display, windowState->root, visual, AllocNone);
18461 } else {
18462 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using default visual, color depth, colormap");
18463 windowState->visual = visual = x11Api->XDefaultVisual(windowState->display, windowState->screen);
18464 windowState->colorDepth = colorDepth = x11Api->XDefaultDepth(windowState->display, windowState->screen);
18465 windowState->colorMap = colormap = x11Api->XDefaultColormap(windowState->display, windowState->screen);
18466 }
18467
18468 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using visual: %p", visual);
18469 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using color depth: %d", colorDepth);
18470 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using color map: %d", (int)colormap);
18471
18472 int flags = CWColormap | CWBorderPixel | CWEventMask | CWBitGravity | CWWinGravity;
18473
18474 // Window background, either as default or custom color
18475 unsigned long backgroundPixel;
18476 if (initWindowSettings->background.value == 0) {
18477 backgroundPixel = 0;
18478 } else {
18479 flags |= CWBackPixel;
18480 backgroundPixel = (unsigned long)((0xFF << 24) | (initWindowSettings->background.components.r << 16) | (initWindowSettings->background.components.g << 8) | initWindowSettings->background.components.b);
18481 }
18482
18483 XSetWindowAttributes swa = fplZeroInit;
18484 swa.colormap = colormap;
18485 swa.event_mask =
18486 StructureNotifyMask |
18487 ExposureMask | FocusChangeMask | VisibilityChangeMask |
18488 EnterWindowMask | LeaveWindowMask | PropertyChangeMask |
18489 KeyPressMask | KeyReleaseMask |
18490 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask;
18491 swa.background_pixel = backgroundPixel;
18492 swa.border_pixel = 0; // @NOTE(final): Use default X11 border
18493 swa.bit_gravity = NorthWestGravity;
18494 swa.win_gravity = NorthWestGravity;
18495
18496 int windowX = 0;
18497 int windowY = 0;
18498 int windowWidth;
18499 int windowHeight;
18500 if ((initSettings->window.windowSize.width > 0) &&
18501 (initSettings->window.windowSize.height > 0)) {
18502 windowWidth = initSettings->window.windowSize.width;
18503 windowHeight = initSettings->window.windowSize.height;
18504 } else {
18505 windowWidth = FPL__X11_DEFAULT_WINDOW_WIDTH;
18506 windowHeight = FPL__X11_DEFAULT_WINDOW_HEIGHT;
18507 }
18508
18509 windowState->lastWindowStateInfo.state = fplWindowState_Normal;
18510 windowState->lastWindowStateInfo.visibility = fplWindowVisibilityState_Show;
18511 windowState->lastWindowStateInfo.position = fplStructInit(fplWindowPosition, windowWidth, windowHeight);
18512 windowState->lastWindowStateInfo.size = fplStructInit(fplWindowSize, (uint32_t)windowX, (uint32_t)windowY);
18513
18514 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);
18515 windowState->window = x11Api->XCreateWindow(windowState->display,
18516 windowState->root,
18517 windowX,
18518 windowY,
18519 windowWidth,
18520 windowHeight,
18521 0,
18522 colorDepth,
18523 InputOutput,
18524 visual,
18525 flags,
18526 &swa);
18527 if (!windowState->window) {
18528 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);
18529 fpl__X11ReleaseWindow(subplatform, windowState);
18530 return false;
18531 }
18532 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);
18533
18534 // Type atoms
18535 windowState->utf8String = x11Api->XInternAtom(windowState->display, "UTF8_STRING", False);
18536
18537 // Window manager atoms
18538 windowState->wmDeleteWindow = x11Api->XInternAtom(windowState->display, "WM_DELETE_WINDOW", False);
18539 windowState->wmProtocols = x11Api->XInternAtom(windowState->display, "WM_PROTOCOLS", False);
18540 windowState->wmState = x11Api->XInternAtom(windowState->display, "WM_STATE", False);
18541 windowState->netWMPing = x11Api->XInternAtom(windowState->display, "_NET_WM_PING", False);
18542 windowState->netWMState = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE", False);
18543 windowState->netWMStateFocused = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_FOCUSED", False);
18544 windowState->netWMStateFullscreen = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_FULLSCREEN", False);
18545 windowState->netWMStateHidden = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_HIDDEN", False);
18546 windowState->netWMStateMaximizedVert = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
18547 windowState->netWMStateMaximizedHorz = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
18548 windowState->netWMPid = x11Api->XInternAtom(windowState->display, "_NET_WM_PID", False);
18549 windowState->netWMIcon = x11Api->XInternAtom(windowState->display, "_NET_WM_ICON", False);
18550 windowState->netWMName = x11Api->XInternAtom(windowState->display, "_NET_WM_NAME", False);
18551 windowState->netWMIconName = x11Api->XInternAtom(windowState->display, "_NET_WM_ICON_NAME", False);
18552 windowState->motifWMHints = x11Api->XInternAtom(windowState->display, "_MOTIF_WM_HINTS", False);
18553 // xdnd atoms
18554 windowState->xdndAware = x11Api->XInternAtom(windowState->display, "XdndAware", False);
18555 windowState->xdndEnter = x11Api->XInternAtom(windowState->display, "XdndEnter", False);
18556 windowState->xdndPosition = x11Api->XInternAtom(windowState->display, "XdndPosition", False);
18557 windowState->xdndStatus = x11Api->XInternAtom(windowState->display, "XdndStatus", False);
18558 windowState->xdndActionCopy = x11Api->XInternAtom(windowState->display, "XdndActionCopy", False);
18559 windowState->xdndDrop = x11Api->XInternAtom(windowState->display, "XdndDrop", False);
18560 windowState->xdndFinished = x11Api->XInternAtom(windowState->display, "XdndFinished", False);
18561 windowState->xdndSelection = x11Api->XInternAtom(windowState->display, "XdndSelection", False);
18562 windowState->xdndTypeList = x11Api->XInternAtom(windowState->display, "XdndTypeList", False);
18563 windowState->textUriList = x11Api->XInternAtom(windowState->display, "text/uri-list", False);
18564
18565 // Register window manager protocols
18566 {
18567 Atom protocols[] = {
18568 windowState->wmDeleteWindow,
18569 windowState->netWMPing
18570 };
18571 x11Api->XSetWMProtocols(windowState->display, windowState->window, protocols, fplArrayCount(protocols));
18572 }
18573
18574 // Declare our process id
18575 {
18576 const long pid = getpid();
18577 x11Api->XChangeProperty(windowState->display, windowState->window, windowState->netWMPid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
18578 }
18579
18580 char nameBuffer[FPL_MAX_NAME_LENGTH] = fplZeroInit;
18581 if (fplGetStringLength(initSettings->window.title) > 0) {
18582 fplCopyString(initSettings->window.title, nameBuffer, fplArrayCount(nameBuffer));
18583 } else {
18584 fplCopyString("Unnamed FPL X11 Window", nameBuffer, fplArrayCount(nameBuffer));
18585 }
18586 FPL_LOG_DEBUG(FPL__MODULE_X11, "Show window '%d' on display '%p' with title '%s'", (int)windowState->window, windowState->display, nameBuffer);
18587 fpl__X11LoadWindowIcon(x11Api, windowState, currentWindowSettings);
18588 fplSetWindowTitle(nameBuffer);
18589 x11Api->XMapWindow(windowState->display, windowState->window);
18590 x11Api->XFlush(windowState->display);
18591
18592 fplAssert(fplArrayCount(appState->window.keyMap) >= 256);
18593
18594 // @NOTE(final): Valid key range for XLib is 8 to 255
18595 FPL_LOG_DEBUG(FPL__MODULE_X11, "Build X11 Keymap");
18596 fplClearStruct(appState->window.keyMap);
18597 for (int keyCode = 8; keyCode <= 255; ++keyCode) {
18598 int dummy = 0;
18599 KeySym *keySyms = x11Api->XGetKeyboardMapping(windowState->display, keyCode, 1, &dummy);
18600 KeySym keySym = keySyms[0];
18601 fplKey mappedKey = fpl__X11TranslateKeySymbol(keySym);
18602 appState->window.keyMap[keyCode] = mappedKey;
18603 x11Api->XFree(keySyms);
18604 }
18605
18606 if (initSettings->window.isFullscreen) {
18608 }
18609
18610 // Announce support for Xdnd (drag and drop)
18611 {
18612 const Atom version = FPL__XDND_VERSION;
18613 x11Api->XChangeProperty(windowState->display, windowState->window, windowState->xdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&version, 1);
18614 }
18615
18616 appState->window.isRunning = true;
18617
18618 return true;
18619}
18620
18621fpl_internal fplKeyboardModifierFlags fpl__X11TranslateModifierFlags(const int state) {
18623 if (state & ShiftMask) {
18626 }
18627 if (state & ControlMask) {
18630 }
18631 if (state & Mod1Mask) {
18634 }
18635 if (state & Mod4Mask) {
18638 }
18639 return(result);
18640}
18641
18642fpl_internal unsigned long fpl__X11GetWindowProperty(const fpl__X11Api *x11Api, Display *display, Window window, Atom prop, Atom type, unsigned char **value) {
18643 Atom actualType;
18644 int actualFormat;
18645 unsigned long itemCount, bytesAfter;
18646 x11Api->XGetWindowProperty(display, window, prop, 0, LONG_MAX, False, type, &actualType, &actualFormat, &itemCount, &bytesAfter, value);
18647 return(itemCount);
18648}
18649
18650fpl_internal const int fpl__X11GetWMState(const fpl__X11Api *x11Api, fpl__X11WindowState *windowState) {
18651 struct { int state; Window icon; } *value = NULL;
18652 unsigned long numItems = fpl__X11GetWindowProperty(x11Api, windowState->display, windowState->window, windowState->wmState, windowState->wmState, (unsigned char **)&value);
18653 int state = WithdrawnState;
18654 if (value) {
18655 state = value->state;
18656 x11Api->XFree(value);
18657 }
18658 return state;
18659}
18660
18661#define fpl__X11NetWMStateHiddenFlag (1 << 0)
18662#define fpl__X11NetWMStateMaximizedFlag (1 << 1)
18663#define fpl__X11NetWMStateFullscreenFlag (1 << 2)
18664
18665fpl_internal unsigned int fpl__X11GetNetWMState(const fpl__X11Api *x11Api, fpl__X11WindowState *windowState) {
18666 Atom *atoms = NULL;
18667 unsigned long numItems = fpl__X11GetWindowProperty(x11Api, windowState->display, windowState->window, windowState->netWMState, XA_ATOM, (unsigned char **)&atoms);
18668 unsigned int flags = 0;
18669 if (atoms) {
18670 int i, maximized = 0;
18671 for (i = 0; i < numItems; ++i) {
18672 if (atoms[i] == windowState->netWMStateHidden) {
18673 flags |= fpl__X11NetWMStateHiddenFlag;
18674 } else if (atoms[i] == windowState->netWMStateMaximizedVert) {
18675 maximized |= 1;
18676 } else if (atoms[i] == windowState->netWMStateMaximizedHorz) {
18677 maximized |= 2;
18678 } else if (atoms[i] == windowState->netWMStateFullscreen) {
18679 flags |= fpl__X11NetWMStateFullscreenFlag;
18680 }
18681 }
18682 if (maximized == 3) {
18683 flags |= fpl__X11NetWMStateMaximizedFlag;
18684 }
18685 // additional visibility check for unmapped window
18686 {
18687 XWindowAttributes attr;
18688 fplMemorySet(&attr, 0, sizeof(attr));
18689 x11Api->XGetWindowAttributes(windowState->display, windowState->window, &attr);
18690 if (attr.map_state == IsUnmapped) {
18691 flags |= fpl__X11NetWMStateHiddenFlag;
18692 }
18693 }
18694 x11Api->XFree(atoms);
18695 }
18696 return flags;
18697}
18698
18699fpl_internal fpl__X11WindowStateInfo fpl__X11GetWindowStateInfo(const fpl__X11Api *x11Api, fpl__X11WindowState *windowState) {
18700 fpl__X11WindowStateInfo result = fplZeroInit;
18701 const int state = fpl__X11GetWMState(x11Api, windowState);
18702 unsigned int flags = fpl__X11GetNetWMState(x11Api, windowState);
18703 if (state == NormalState) {
18704 result.state = fplWindowState_Normal;
18705 } else if (state == IconicState) {
18706 result.state = fplWindowState_Iconify;
18707 }
18708 // reset visibility to default
18709 result.visibility = fplWindowVisibilityState_Show;
18710 if (flags & fpl__X11NetWMStateHiddenFlag) {
18711 result.visibility = fplWindowVisibilityState_Hide;
18712 }
18713 if (flags & fpl__X11NetWMStateFullscreenFlag) {
18714 result.state = fplWindowState_Fullscreen;
18715 } else if (state != IconicState && flags & fpl__X11NetWMStateMaximizedFlag) {
18716 result.state = fplWindowState_Maximize;
18717 }
18718 return result;
18719}
18720
18721fpl_internal fpl__X11WindowStateInfo fpl__X11ReconcilWindowStateInfo(fpl__X11WindowStateInfo *last, fpl__X11WindowStateInfo *next) {
18722 fpl__X11WindowStateInfo change = fplZeroInit;
18723 if (last->state != next->state) {
18724 change.state = next->state;
18725 }
18726 if (last->visibility != next->visibility) {
18727 change.visibility = next->visibility;
18728 }
18729 return change;
18730}
18731
18732fpl_internal void *fpl__X11ParseUriPaths(const char *text, size_t *size, int *count, int textLength) {
18733 const char *textCursor = text;
18734 const char *textEnd = text + textLength;
18735 int fileCount = 0;
18736 // count file entries
18737 while (*textCursor != '\0' || textCursor != textEnd) {
18738 if (*textCursor == '\r' && *(textCursor + 1) == '\n')
18739 ++fileCount;
18740 ++textCursor;
18741 }
18742 textCursor = text;
18743 size_t filesTableSize = fileCount * sizeof(char **);
18744 size_t maxFileStride = FPL_MAX_PATH_LENGTH + 1;
18745 size_t filesMemorySize = filesTableSize + FPL__ARBITARY_PADDING + maxFileStride * fileCount;
18746 void *filesTableMemory = fpl__AllocateDynamicMemory(filesMemorySize, 8);
18747 char **filesTable = (char **)filesTableMemory;
18748 for (int fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
18749 filesTable[fileIndex] = (char *)((uint8_t *)filesTableMemory + filesTableSize + FPL__ARBITARY_PADDING + fileIndex * maxFileStride);
18750 }
18751 for (int fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
18752 char *file = filesTable[fileIndex];
18753 const char *line = textCursor;
18754 // split on '\r\n' divider
18755 while (*textCursor != '\r' && (*textCursor != '\0' || textCursor != textEnd)) {
18756 ++textCursor;
18757 }
18758 // strip protocol
18759 if (fplIsStringEqualLen(line, 7, "file://", 7)) {
18760 line += 7;
18761 }
18762 fplCopyStringLen(line, (textCursor - line), file, maxFileStride);
18763 textCursor += 2;
18764 }
18765 *size = filesMemorySize;
18766 *count = fileCount;
18767 return filesTableMemory;
18768}
18769
18770fpl_internal void fpl__X11HandleTextInputEvent(const fpl__X11Api *x11Api, fpl__PlatformWindowState *winState, const uint64_t keyCode, XEvent *ev) {
18771 char buf[32];
18772 KeySym keysym = 0;
18773 if (x11Api->XLookupString(&ev->xkey, buf, 32, &keysym, NULL) != NoSymbol) {
18774 wchar_t wideBuffer[4] = fplZeroInit;
18775 fplUTF8StringToWideString(buf, fplGetStringLength(buf), wideBuffer, fplArrayCount(wideBuffer));
18776 uint32_t textCode = (uint32_t)wideBuffer[0];
18777 if (textCode > 0) {
18778 fpl__HandleKeyboardInputEvent(winState, keyCode, textCode);
18779 }
18780 }
18781}
18782
18783fpl_internal void fpl__X11HandleEvent(const fpl__X11SubplatformState *subplatform, fpl__PlatformAppState *appState, XEvent *ev) {
18784 fplAssert((subplatform != fpl_null) && (appState != fpl_null) && (ev != fpl_null));
18785 fpl__PlatformWindowState *winState = &appState->window;
18786 fpl__X11WindowState *x11WinState = &winState->x11;
18787 fpl__X11WindowStateInfo *lastX11WinInfo = &x11WinState->lastWindowStateInfo;
18788 const fpl__X11Api *x11Api = &appState->x11.api;
18789
18790 if (appState->currentSettings.window.callbacks.eventCallback != fpl_null) {
18791 appState->currentSettings.window.callbacks.eventCallback(fplGetPlatformType(), x11WinState, ev, appState->currentSettings.window.callbacks.eventUserData);
18792 }
18793
18794 switch (ev->type) {
18795 case ConfigureNotify:
18796 {
18797# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
18798 if (appState->currentSettings.video.backend == fplVideoBackendType_Software) {
18799 if (appState->initSettings.video.isAutoSize) {
18800 uint32_t w = (uint32_t)ev->xconfigure.width;
18801 uint32_t h = (uint32_t)ev->xconfigure.height;
18803 }
18804 }
18805# endif
18806 // Window resized
18807 if (ev->xconfigure.width != lastX11WinInfo->size.width || ev->xconfigure.height != lastX11WinInfo->size.height) {
18808 fpl__PushWindowSizeEvent(fplWindowEventType_Resized, (uint32_t)ev->xconfigure.width, (uint32_t)ev->xconfigure.height);
18809 lastX11WinInfo->size.width = (int32_t)ev->xconfigure.width;
18810 lastX11WinInfo->size.height = (int32_t)ev->xconfigure.height;
18811 }
18812
18813 // Window moved
18814 if (ev->xconfigure.x != lastX11WinInfo->position.left || ev->xconfigure.y != lastX11WinInfo->position.top) {
18815 fpl__PushWindowPositionEvent(fplWindowEventType_PositionChanged, (int32_t)ev->xconfigure.x, (int32_t)ev->xconfigure.y);
18816 lastX11WinInfo->position.left = (int32_t)ev->xconfigure.x;
18817 lastX11WinInfo->position.top = (int32_t)ev->xconfigure.y;
18818 }
18819 } break;
18820
18821 case ClientMessage:
18822 {
18823 if (ev->xclient.message_type == x11WinState->wmProtocols) {
18824 const Atom protocol = (Atom)ev->xclient.data.l[0];
18825 if (protocol != None) {
18826 if (protocol == x11WinState->wmDeleteWindow) {
18827 // Window asked for closing
18828 winState->isRunning = false;
18829 fpl__PushWindowStateEvent(fplWindowEventType_Closed);
18830 } else if (protocol == x11WinState->netWMPing) {
18831 // Window manager asks us if we are still alive
18832 XEvent reply = *ev;
18833 reply.xclient.window = x11WinState->root;
18834 x11Api->XSendEvent(x11WinState->display, x11WinState->root, False, SubstructureNotifyMask | SubstructureRedirectMask, &reply);
18835 }
18836 }
18837 } else if (ev->xclient.message_type == x11WinState->xdndEnter) {
18838 // A drag operation has entered the window
18839 unsigned long i, count;
18840 Atom *formats = NULL;
18841 bool list = ev->xclient.data.l[1] & 1;
18842 x11WinState->xdnd.source = ev->xclient.data.l[0];
18843 x11WinState->xdnd.version = ev->xclient.data.l[1] >> 24;
18844 x11WinState->xdnd.format = None;
18845 if (x11WinState->xdnd.version > FPL__XDND_VERSION) {
18846 return;
18847 }
18848 if (list) {
18849 count = fpl__X11GetWindowProperty(x11Api, x11WinState->display, x11WinState->xdnd.source, x11WinState->xdndTypeList, XA_ATOM, (unsigned char **)&formats);
18850 } else {
18851 count = 3;
18852 formats = (Atom *)ev->xclient.data.l + 2;
18853 }
18854 for (i = 0; i < count; ++i) {
18855 if (formats[i] == x11WinState->textUriList) {
18856 x11WinState->xdnd.format = x11WinState->textUriList;
18857 break;
18858 }
18859 }
18860 if (list && formats) {
18861 x11Api->XFree(formats);
18862 }
18863 } else if (ev->xclient.message_type == x11WinState->xdndDrop) {
18864 // The drag operation has finished by dropping on the window
18865 Time time = CurrentTime;
18866 if (x11WinState->xdnd.version > FPL__XDND_VERSION) {
18867 return;
18868 }
18869 if (x11WinState->xdnd.format) {
18870 if (x11WinState->xdnd.version >= 1) {
18871 time = ev->xclient.data.l[2];
18872 }
18873 // Request the chosen format from the source window
18874 x11Api->XConvertSelection(x11WinState->display, x11WinState->xdndSelection, x11WinState->xdnd.format, x11WinState->xdndSelection, x11WinState->window, time);
18875 } else if (x11WinState->xdnd.version >= 2) {
18876 XEvent reply;
18877 fplMemorySet(&reply, 0, sizeof(reply));
18878
18879 reply.type = ClientMessage;
18880 reply.xclient.window = x11WinState->xdnd.source;
18881 reply.xclient.message_type = x11WinState->xdndFinished;
18882 reply.xclient.format = 32;
18883 reply.xclient.data.l[0] = x11WinState->window;
18884 reply.xclient.data.l[1] = 0; // The drag was rejected
18885 reply.xclient.data.l[2] = None;
18886
18887 x11Api->XSendEvent(x11WinState->display, x11WinState->xdnd.source, False, NoEventMask, &reply);
18888 x11Api->XFlush(x11WinState->display);
18889 }
18890 } else if (ev->xclient.message_type == x11WinState->xdndPosition) {
18891 // The drag operation has moved over the window
18892 Window dummy;
18893 int xpos, ypos;
18894 const int xabs = (ev->xclient.data.l[2] >> 16) & 0xffff;
18895 const int yabs = (ev->xclient.data.l[2]) & 0xffff;
18896 if (x11WinState->xdnd.version > FPL__XDND_VERSION) {
18897 return;
18898 }
18899 XEvent reply;
18900 fplMemorySet(&reply, 0, sizeof(reply));
18901
18902 reply.type = ClientMessage;
18903 reply.xclient.window = x11WinState->xdnd.source;
18904 reply.xclient.message_type = x11WinState->xdndStatus;
18905 reply.xclient.format = 32;
18906 reply.xclient.data.l[0] = x11WinState->window;
18907 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
18908 reply.xclient.data.l[3] = 0;
18909
18910 if (x11WinState->xdnd.format) {
18911 // Reply that we are ready to copy the dragged data
18912 reply.xclient.data.l[1] = 1; // Accept with no rectangle
18913 if (x11WinState->xdnd.version >= 2)
18914 reply.xclient.data.l[4] = x11WinState->xdndActionCopy;
18915 }
18916 x11Api->XSendEvent(x11WinState->display, x11WinState->xdnd.source, False, NoEventMask, &reply);
18917 x11Api->XFlush(x11WinState->display);
18918 }
18919 } break;
18920
18921 case SelectionNotify:
18922 {
18923 if (ev->xselection.property == x11WinState->xdndSelection) {
18924 // The converted data from the drag operation has arrived
18925 char *data;
18926 const unsigned long result = fpl__X11GetWindowProperty(x11Api, x11WinState->display, ev->xselection.requestor, ev->xselection.property, ev->xselection.target, (unsigned char **)&data);
18927 if (result) {
18928 size_t filesTableSize;
18929 int fileCount;
18930 void *filesTable = fpl__X11ParseUriPaths(data, &filesTableSize, &fileCount, result);
18931 fplMemoryBlock memory = fplZeroInit;
18932 memory.size = filesTableSize;
18933 memory.base = filesTable;
18934 fpl__PushWindowDropFilesEvent(NULL, fileCount, (const char **)filesTable, &memory);
18935 }
18936 if (data) {
18937 x11Api->XFree(data);
18938 }
18939 if (x11WinState->xdnd.version >= 2) {
18940 XEvent reply;
18941 fplMemorySet(&reply, 0, sizeof(reply));
18942
18943 reply.type = ClientMessage;
18944 reply.xclient.window = x11WinState->xdnd.source;
18945 reply.xclient.message_type = x11WinState->xdndFinished;
18946 reply.xclient.format = 32;
18947 reply.xclient.data.l[0] = x11WinState->window;
18948 reply.xclient.data.l[1] = result;
18949 reply.xclient.data.l[2] = x11WinState->xdndActionCopy;
18950
18951 x11Api->XSendEvent(x11WinState->display, x11WinState->xdnd.source, False, NoEventMask, &reply);
18952 x11Api->XFlush(x11WinState->display);
18953 }
18954 }
18955 } break;
18956
18957 case KeyPress:
18958 {
18959 // Keyboard button down
18960 if (!appState->currentSettings.input.disabledEvents) {
18961 int keyState = ev->xkey.state;
18962 uint64_t keyCode = (uint64_t)ev->xkey.keycode;
18963 Time keyTime = ev->xkey.time;
18964 Time lastPressTime = winState->keyPressTimes[keyCode];
18965 Time diffTime = keyTime - lastPressTime;
18966 FPL_LOG_INFO("X11", "Diff for key '%llu', time: %lu, diff: %lu, last: %lu", keyCode, keyTime, diffTime, lastPressTime);
18967 if (diffTime == keyTime || (diffTime > 0 && diffTime < (1 << 31))) {
18968 if (keyCode) {
18969 fpl__HandleKeyboardButtonEvent(winState, (uint64_t)keyTime, keyCode, fpl__X11TranslateModifierFlags(keyState), fplButtonState_Press, false);
18970 fpl__X11HandleTextInputEvent(x11Api, winState, keyCode, ev);
18971 }
18972 winState->keyPressTimes[keyCode] = keyTime;
18973 }
18974 }
18975 } break;
18976
18977 case KeyRelease:
18978 {
18979 // Keyboard button up
18980 if (!appState->currentSettings.input.disabledEvents) {
18981 bool isRepeat = false;
18982 if (x11Api->XEventsQueued(x11WinState->display, QueuedAfterReading)) {
18983 XEvent nextEvent;
18984 x11Api->XPeekEvent(x11WinState->display, &nextEvent);
18985 if ((nextEvent.type == KeyPress) && (nextEvent.xkey.time == ev->xkey.time) && (nextEvent.xkey.keycode == ev->xkey.keycode)) {
18986 // Delete the peeked event, which is a key-repeat
18987 x11Api->XNextEvent(x11WinState->display, ev);
18988 isRepeat = true;
18989 }
18990 }
18991
18992 int keyState = ev->xkey.state;
18993 uint64_t keyCode = (uint64_t)ev->xkey.keycode;
18994
18995 if (isRepeat) {
18996 fpl__X11HandleTextInputEvent(x11Api, winState, keyCode, ev);
18997 fpl__HandleKeyboardButtonEvent(winState, (uint64_t)ev->xkey.time, (uint64_t)keyCode, fpl__X11TranslateModifierFlags(keyState), fplButtonState_Repeat, false);
18998 } else {
18999 fpl__HandleKeyboardButtonEvent(winState, (uint64_t)ev->xkey.time, (uint64_t)keyCode, fpl__X11TranslateModifierFlags(keyState), fplButtonState_Release, true);
19000 }
19001 }
19002 } break;
19003
19004 case ButtonPress:
19005 {
19006 int x = ev->xbutton.x;
19007 int y = ev->xbutton.y;
19008
19009 if (!appState->currentSettings.input.disabledEvents) {
19010 // Mouse button
19011 if (ev->xbutton.button == Button1) {
19012 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Left, fplButtonState_Press);
19013 } else if (ev->xbutton.button == Button2) {
19014 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Middle, fplButtonState_Press);
19015 } else if (ev->xbutton.button == Button3) {
19016 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Right, fplButtonState_Press);
19017 }
19018 }
19019
19020 // Mouse wheel
19021 if (ev->xbutton.button == Button4) {
19022 fpl__HandleMouseWheelEvent(winState, x, y, 1.0f);
19023 } else if (ev->xbutton.button == Button5) {
19024 fpl__HandleMouseWheelEvent(winState, x, y, -1.0f);
19025 }
19026 } break;
19027
19028 case ButtonRelease:
19029 {
19030 // Mouse up
19031 if (!appState->currentSettings.input.disabledEvents) {
19032 int x = ev->xbutton.x;
19033 int y = ev->xbutton.y;
19034 if (ev->xbutton.button == Button1) {
19035 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Left, fplButtonState_Release);
19036 } else if (ev->xbutton.button == Button2) {
19037 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Middle, fplButtonState_Release);
19038 } else if (ev->xbutton.button == Button3) {
19039 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Right, fplButtonState_Release);
19040 }
19041 }
19042 } break;
19043
19044 case MotionNotify:
19045 {
19046 // Mouse move
19047 if (!appState->currentSettings.input.disabledEvents) {
19048 fpl__HandleMouseMoveEvent(winState, ev->xmotion.x, ev->xmotion.y);
19049 }
19050 } break;
19051
19052 case Expose:
19053 {
19054 // Repaint
19055 if (appState->currentSettings.window.callbacks.exposedCallback != fpl_null) {
19056 appState->currentSettings.window.callbacks.exposedCallback(fplGetPlatformType(), x11WinState, ev, appState->currentSettings.window.callbacks.exposedUserData);
19057 }
19058 } break;
19059
19060 case FocusIn:
19061 {
19062 // Ignore focus events from popup indicator windows, window menu
19063 // key chords and window dragging
19064 if (ev->xfocus.mode == NotifyGrab || ev->xfocus.mode == NotifyUngrab) {
19065 return;
19066 }
19067 fpl__PushWindowStateEvent(fplWindowEventType_GotFocus);
19068 } break;
19069
19070 case FocusOut:
19071 {
19072 // Ignore focus events from popup indicator windows, window menu
19073 // key chords and window dragging
19074 if (ev->xfocus.mode == NotifyGrab || ev->xfocus.mode == NotifyUngrab) {
19075 return;
19076 }
19077 fpl__PushWindowStateEvent(fplWindowEventType_LostFocus);
19078 } break;
19079
19080 case PropertyNotify:
19081 {
19082 if (ev->xproperty.atom == x11WinState->netWMState || ev->xproperty.atom == x11WinState->wmState) {
19083 fpl__X11WindowStateInfo nextWindowStateInfo = fpl__X11GetWindowStateInfo(x11Api, x11WinState);
19084 fpl__X11WindowStateInfo changedWindowStateInfo = fpl__X11ReconcilWindowStateInfo(&x11WinState->lastWindowStateInfo, &nextWindowStateInfo);
19085 switch (changedWindowStateInfo.visibility) {
19087 fpl__PushWindowStateEvent(fplWindowEventType_Hidden);
19088 break;
19090 fpl__PushWindowStateEvent(fplWindowEventType_Shown);
19091 break;
19092 default:
19093 break;
19094 }
19095 switch (changedWindowStateInfo.state) {
19097 fpl__PushWindowStateEvent(fplWindowEventType_Minimized);
19098 break;
19100 fpl__PushWindowStateEvent(fplWindowEventType_Maximized);
19101 break;
19103 fpl__PushWindowStateEvent(fplWindowEventType_Restored);
19104 break;
19105 default:
19106 break;
19107 }
19108 x11WinState->lastWindowStateInfo.state = nextWindowStateInfo.state;
19109 x11WinState->lastWindowStateInfo.visibility = nextWindowStateInfo.visibility;
19110 }
19111 } break;
19112
19113 default:
19114 break;
19115 }
19116}
19117
19119 FPL__CheckPlatform(false);
19120 bool result = fpl__global__AppState->window.isRunning;
19121 return(result);
19122}
19123
19125 FPL__CheckPlatformNoRet();
19126 fpl__PlatformAppState *appState = fpl__global__AppState;
19127 if (appState->window.isRunning) {
19128 appState->window.isRunning = false;
19129 const fpl__X11SubplatformState *subplatform = &appState->x11;
19130 const fpl__X11Api *x11Api = &subplatform->api;
19131 const fpl__X11WindowState *windowState = &appState->window.x11;
19132 XEvent ev = fplZeroInit;
19133 ev.type = ClientMessage;
19134 ev.xclient.window = windowState->window;
19135 ev.xclient.message_type = windowState->wmProtocols;
19136 ev.xclient.format = 32;
19137 ev.xclient.data.l[0] = windowState->wmDeleteWindow;
19138 ev.xclient.data.l[1] = 0;
19139 x11Api->XSendEvent(windowState->display, windowState->root, False, SubstructureRedirectMask | SubstructureNotifyMask, &ev);
19140 }
19141}
19142
19143fpl_internal bool fpl__X11ProcessNextEvent(const fpl__X11SubplatformState *subplatform, fpl__PlatformAppState *appState) {
19144 bool result = false;
19145 const fpl__X11Api *x11Api = &subplatform->api;
19146 fpl__X11WindowState *windowState = &appState->window.x11;
19147 if (x11Api->XPending(windowState->display)) {
19148 XEvent ev;
19149 x11Api->XNextEvent(windowState->display, &ev);
19150 fpl__X11HandleEvent(subplatform, appState, &ev);
19151 result = true;
19152 }
19153 return(result);
19154}
19155
19157 FPL__CheckPlatform(false);
19158 fpl__PlatformAppState *appState = fpl__global__AppState;
19159 const fpl__X11SubplatformState *subplatform = &appState->x11;
19160 const fpl__X11Api *x11Api = &subplatform->api;
19161 const fpl__X11WindowState *windowState = &appState->window.x11;
19162
19163 // Poll next event from the internal queue first
19164 if (fpl__PollInternalEvent(ev)) {
19165 return(true);
19166 }
19167
19168 // Create new event(s) from the X11 event queue
19169 if (!fpl__X11ProcessNextEvent(subplatform, appState)) {
19170 return(false);
19171 }
19172
19173 // Poll the first event from the internal queue
19174 if (fpl__PollInternalEvent(ev)) {
19175 return(true);
19176 }
19177
19178 // No events left
19179 return(false);
19180}
19181
19183 FPL__CheckPlatformNoRet();
19184 fpl__PlatformAppState *appState = fpl__global__AppState;
19185 const fpl__X11SubplatformState *subplatform = &appState->x11;
19186 const fpl__X11Api *x11Api = &subplatform->api;
19187 const fpl__X11WindowState *windowState = &appState->window.x11;
19188 while (x11Api->XPending(windowState->display)) {
19189 XEvent ev;
19190 x11Api->XNextEvent(windowState->display, &ev);
19191 fpl__X11HandleEvent(subplatform, appState, &ev);
19192 }
19193 fpl__ClearInternalEvents();
19194}
19195
19197 FPL__CheckPlatform(false);
19198 fpl__PlatformAppState *appState = fpl__global__AppState;
19199 const fpl__X11SubplatformState *subplatform = &appState->x11;
19200 const fpl__X11Api *x11Api = &subplatform->api;
19201 const fpl__X11WindowState *windowState = &appState->window.x11;
19202
19203 fpl__ClearInternalEvents();
19204
19205 // Dont like this, maybe a callback would be better?
19206#if defined(FPL_PLATFORM_LINUX)
19207 if ((!appState->currentSettings.input.disabledEvents) && (appState->initFlags & fplInitFlags_GameController)) {
19208 fpl__LinuxAppState *linuxAppState = &appState->plinux;
19209 fpl__LinuxPollGameControllers(&appState->currentSettings, &linuxAppState->controllersState, true);
19210 }
19211#endif
19212
19213 bool result = appState->window.isRunning;
19214 return(result);
19215}
19216
19217fpl_platform_api void fplSetWindowCursorEnabled(const bool value) {
19218 // @IMPLEMENT(final/X11): fplSetWindowCursorEnabled
19219}
19220
19222 FPL__CheckArgumentNull(outSize, false);
19223 FPL__CheckPlatform(false);
19224 fpl__PlatformAppState *appState = fpl__global__AppState;
19225 const fpl__X11SubplatformState *subplatform = &appState->x11;
19226 const fpl__X11Api *x11Api = &subplatform->api;
19227 const fpl__X11WindowState *windowState = &appState->window.x11;
19228 XWindowAttributes attribs;
19229 x11Api->XGetWindowAttributes(windowState->display, windowState->window, &attribs);
19230 outSize->width = attribs.width;
19231 outSize->height = attribs.height;
19232 return(true);
19233}
19234
19235fpl_platform_api void fplSetWindowSize(const uint32_t width, const uint32_t height) {
19236 fpl__PlatformAppState *appState = fpl__global__AppState;
19237 FPL__CheckPlatformNoRet();
19238 const fpl__X11SubplatformState *subplatform = &appState->x11;
19239 const fpl__X11Api *x11Api = &subplatform->api;
19240 const fpl__X11WindowState *windowState = &appState->window.x11;
19241 x11Api->XResizeWindow(windowState->display, windowState->window, width, height);
19242 x11Api->XFlush(windowState->display);
19243}
19244
19246 // @IMPLEMENT(final/X11): fplIsWindowResizable
19247 return false;
19248}
19249
19250fpl_platform_api void fplSetWindowResizeable(const bool value) {
19251 // @IMPLEMENT(final/X11): fplSetWindowResizeable
19252}
19253
19255 FPL__CheckPlatform(false);
19256 fpl__PlatformAppState *appState = fpl__global__AppState;
19257 bool result = appState->currentSettings.window.isDecorated;
19258 return(result);
19259}
19260
19261#define FPL__MWM_HINTS_DECORATIONS (1L << 1)
19262#define FPL__MWM_HINTS_FUNCTIONS (1L << 0)
19263#define FPL__MWM_FUNC_ALL (1L<<0)
19264#define FPL__PROPERTY_MOTIF_WM_HINTS_ELEMENT_COUNT 5
19265
19266typedef struct {
19267 unsigned long flags;
19268 unsigned long functions;
19269 unsigned long decorations;
19270 long input_mode;
19271 unsigned long status;
19272} fpl__MotifWMHints;
19273
19274fpl_platform_api void fplSetWindowDecorated(const bool value) {
19275 FPL__CheckPlatformNoRet();
19276 fpl__PlatformAppState *appState = fpl__global__AppState;
19277 const fpl__X11SubplatformState *subplatform = &appState->x11;
19278 const fpl__X11Api *x11Api = &subplatform->api;
19279 const fpl__X11WindowState *windowState = &appState->window.x11;
19280
19281 fpl__MotifWMHints hints = fplZeroInit;
19282 hints.flags = FPL__MWM_HINTS_DECORATIONS | FPL__MWM_HINTS_FUNCTIONS;
19283 hints.decorations = value ? 1 : 0;
19284 hints.functions = value ? FPL__MWM_FUNC_ALL : 0;
19285
19286 x11Api->XChangeProperty(windowState->display, windowState->window,
19287 windowState->motifWMHints,
19288 windowState->motifWMHints, 32,
19289 PropModeReplace,
19290 (unsigned char *)&hints,
19291 FPL__PROPERTY_MOTIF_WM_HINTS_ELEMENT_COUNT);
19292
19293 appState->currentSettings.window.isDecorated = value;
19294}
19295
19297 // @IMPLEMENT(final/X11): fplIsWindowFloating
19298 return false;
19299}
19300
19301fpl_platform_api void fplSetWindowFloating(const bool value) {
19302 // @IMPLEMENT(final/X11): fplSetWindowFloating
19303}
19304
19306 // @IMPLEMENT(final/X11): fplGetWindowState
19307 return(fplWindowState_Unknown);
19308}
19309
19311 // @IMPLEMENT(final/X11): fplSetWindowState
19312 return(false);
19313}
19314
19316 // @IMPLEMENT(final/X11): fplGetDisplayCount
19317 return(0);
19318}
19319
19320fpl_platform_api size_t fplGetDisplays(fplDisplayInfo *displays, const size_t maxDisplayCount) {
19321 // @IMPLEMENT(final/X11): fplGetDisplays
19322 return(0);
19323}
19324
19326 // @IMPLEMENT(final/X11): fplGetPrimaryDisplay
19327 return(false);
19328}
19329
19331 // @IMPLEMENT(final/X11): fplGetWindowDisplay
19332 return(false);
19333}
19334
19335fpl_platform_api bool fplGetDisplayFromPosition(const int32_t x, const int32_t y, fplDisplayInfo *outDisplay) {
19336 // @IMPLEMENT(final/X11): fplGetDisplayFromPosition
19337 return(false);
19338}
19339
19340fpl_platform_api size_t fplGetDisplayModes(const char *id, fplDisplayMode *modes, const size_t maxDisplayModeCount) {
19341 // @IMPLEMENT(final/X11): fplGetDisplayModes
19342 return(0);
19343}
19344
19345fpl_platform_api bool fplSetWindowFullscreenSize(const bool value, const uint32_t fullscreenWidth, const uint32_t fullscreenHeight, const uint32_t refreshRate) {
19346 FPL__CheckPlatform(false);
19347 fpl__PlatformAppState *appState = fpl__global__AppState;
19348 const fpl__X11SubplatformState *subplatform = &appState->x11;
19349 const fpl__X11Api *x11Api = &subplatform->api;
19350 const fpl__X11WindowState *windowState = &appState->window.x11;
19351
19352 // https://stackoverflow.com/questions/10897503/opening-a-fullscreen-opengl-window
19353 XEvent xev = fplZeroInit;
19354 xev.type = ClientMessage;
19355 xev.xclient.window = windowState->window;
19356 xev.xclient.message_type = windowState->netWMState;
19357 xev.xclient.format = 32;
19358 xev.xclient.data.l[0] = value ? 1 : 0; // 1 = Add, 0 = Remove
19359 xev.xclient.data.l[1] = windowState->netWMStateFullscreen; // _NET_WM_STATE_FULLSCREEN
19360 xev.xclient.data.l[3] = 1l; // Application source
19361
19362 // @TODO(final/X11): Support for changing the display resolution + refresh rate in X11
19363
19364 bool result = x11Api->XSendEvent(windowState->display, windowState->root, 0, SubstructureRedirectMask | SubstructureNotifyMask, &xev) != 0;
19365 if (result) {
19366 appState->currentSettings.window.isFullscreen = value;
19367 }
19368 return(result);
19369}
19370
19371fpl_platform_api bool fplSetWindowFullscreenRect(const bool value, const int32_t x, const int32_t y, const int32_t width, const int32_t height) {
19372 // @IMPLEMENT(final/X11): fplSetWindowFullscreenRect
19373 return(false);
19374}
19375
19377 bool result = fplSetWindowFullscreenSize(true, 0, 0, 0);
19378 return(result);
19379}
19380
19382 bool result = fplSetWindowFullscreenSize(false, 0, 0, 0);
19383 return(result);
19384}
19385
19387 FPL__CheckPlatform(false);
19388 fpl__PlatformAppState *appState = fpl__global__AppState;
19389 bool result = appState->currentSettings.window.isFullscreen;
19390 return(result);
19391}
19392
19394 FPL__CheckArgumentNull(outPos, false);
19395 FPL__CheckPlatform(false);
19396 fpl__PlatformAppState *appState = fpl__global__AppState;
19397 const fpl__X11SubplatformState *subplatform = &appState->x11;
19398 const fpl__X11Api *x11Api = &subplatform->api;
19399 const fpl__X11WindowState *windowState = &appState->window.x11;
19400 XWindowAttributes attribs;
19401 x11Api->XGetWindowAttributes(windowState->display, windowState->window, &attribs);
19402 outPos->left = attribs.x;
19403 outPos->top = attribs.y;
19404 return(true);
19405}
19406
19407fpl_platform_api void fplSetWindowPosition(const int32_t left, const int32_t top) {
19408 FPL__CheckPlatformNoRet();
19409 fpl__PlatformAppState *appState = fpl__global__AppState;
19410 const fpl__X11SubplatformState *subplatform = &appState->x11;
19411 const fpl__X11Api *x11Api = &subplatform->api;
19412 const fpl__X11WindowState *windowState = &appState->window.x11;
19413 x11Api->XMoveWindow(windowState->display, windowState->window, left, top);
19414}
19415
19416fpl_platform_api void fplSetWindowTitle(const char *title) {
19417 // @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.
19418
19419 FPL__CheckArgumentNullNoRet(title);
19420 FPL__CheckPlatformNoRet();
19421 fpl__PlatformAppState *appState = fpl__global__AppState;
19422 const fpl__X11SubplatformState *subplatform = &appState->x11;
19423 const fpl__X11Api *x11Api = &subplatform->api;
19424 const fpl__X11WindowState *windowState = &appState->window.x11;
19425
19426 fplCopyString(title, appState->currentSettings.window.title, fplArrayCount(appState->currentSettings.window.title));
19427
19428 x11Api->XChangeProperty(windowState->display, windowState->window,
19429 windowState->netWMName, windowState->utf8String, 8,
19430 PropModeReplace,
19431 (unsigned char *)title, (int)fplGetStringLength(title));
19432
19433 x11Api->XChangeProperty(windowState->display, windowState->window,
19434 windowState->netWMIconName, windowState->utf8String, 8,
19435 PropModeReplace,
19436 (unsigned char *)title, (int)fplGetStringLength(title));
19437
19438 x11Api->XFlush(windowState->display);
19439}
19440
19441fpl_platform_api bool fplGetClipboardText(char *dest, const uint32_t maxDestLen) {
19442 // @IMPLEMENT(final/X11): fplGetClipboardText
19443 return false;
19444}
19445
19446fpl_platform_api bool fplSetClipboardText(const char *text) {
19447 // @IMPLEMENT(final/X11): fplSetClipboardText
19448 return false;
19449}
19450
19452 FPL__CheckPlatform(false);
19453 FPL__CheckArgumentNull(outState, false);
19454 fpl__PlatformAppState *appState = fpl__global__AppState;
19455 const fpl__X11SubplatformState *subplatform = &appState->x11;
19456 const fpl__X11Api *x11Api = &subplatform->api;
19457 const fpl__X11WindowState *windowState = &appState->window.x11;
19458 bool result = false;
19459 char keysReturn[32] = fplZeroInit;
19460 if (x11Api->XQueryKeymap(windowState->display, keysReturn)) {
19461 fplClearStruct(outState);
19462 for (uint64_t keyCode = 0; keyCode < 256; ++keyCode) {
19463 bool isDown = (keysReturn[keyCode / 8] & (1 << (keyCode % 8))) != 0;
19464 outState->keyStatesRaw[keyCode] = isDown ? 1 : 0;
19465 fplKey mappedKey = fpl__GetMappedKey(&appState->window, keyCode);
19466 if (outState->buttonStatesMapped[(int)mappedKey] == fplButtonState_Release) {
19467 outState->buttonStatesMapped[(int)mappedKey] = isDown ? fplButtonState_Press : fplButtonState_Release;
19468 }
19469 }
19473 }
19476 }
19479 }
19482 }
19485 }
19488 }
19491 }
19494 }
19495 // @FINISH(fina/X11): Get caps states (Capslock, Numlock, Scrolllock)
19496
19497 result = true;
19498 }
19499 return(result);
19500}
19501
19503 FPL__CheckPlatform(false);
19504 FPL__CheckArgumentNull(outState, false);
19505 fpl__PlatformAppState *appState = fpl__global__AppState;
19506 const fpl__X11SubplatformState *subplatform = &appState->x11;
19507 const fpl__X11Api *x11Api = &subplatform->api;
19508 const fpl__X11WindowState *windowState = &appState->window.x11;
19509 bool result = false;
19510 Window root, child;
19511 int rootx, rooty, winx, winy;
19512 unsigned int mask;
19513 if (x11Api->XQueryPointer(windowState->display, windowState->window, &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
19514 outState->x = winx;
19515 outState->y = winy;
19519 result = true;
19520 }
19521 return(result);
19522}
19523
19524fpl_platform_api bool fplQueryCursorPosition(int32_t *outX, int32_t *outY) {
19525 // @IMPLEMENT(final/X11) fplQueryCursorPosition
19526 return(false);
19527}
19528#endif // FPL_SUBPLATFORM_X11
19529
19530// ############################################################################
19531//
19532// > LINUX_PLATFORM
19533//
19534// ############################################################################
19535#if defined(FPL_PLATFORM_LINUX)
19536# include <locale.h> // setlocale
19537# include <sys/eventfd.h> // eventfd
19538# include <sys/epoll.h> // epoll_create, epoll_ctl, epoll_wait
19539# include <sys/select.h> // select
19540# include <linux/joystick.h> // js_event, axis_state, etc.
19541
19542fpl_internal void fpl__LinuxReleasePlatform(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
19543#if defined(FPL__ENABLE_WINDOW)
19544 if (appState->initFlags & fplInitFlags_GameController) {
19545 fpl__LinuxFreeGameControllers(&appState->plinux.controllersState);
19546 }
19547#endif
19548}
19549
19550fpl_internal bool fpl__LinuxInitPlatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
19551 setlocale(LC_ALL, "");
19552 return true;
19553}
19554
19555//
19556// Linux OS
19557//
19558
19559
19560//
19561// Linux Input
19562//
19563#if defined(FPL__ENABLE_WINDOW)
19564fpl_internal void fpl__LinuxFreeGameControllers(fpl__LinuxGameControllersState *controllersState) {
19565 for (int controllerIndex = 0; controllerIndex < fplArrayCount(controllersState->controllers); ++controllerIndex) {
19566 fpl__LinuxGameController *controller = controllersState->controllers + controllerIndex;
19567 if (controller->fd > 0) {
19568 close(controller->fd);
19569 controller->fd = 0;
19570 }
19571 }
19572}
19573
19574fpl_internal float fpl__LinuxJoystickProcessStickValue(const int16_t value, const int16_t deadZoneThreshold) {
19575 float result = 0;
19576 if (value < -deadZoneThreshold) {
19577 result = (float)((value + deadZoneThreshold) / (32768.0f - deadZoneThreshold));
19578 } else if (value > deadZoneThreshold) {
19579 result = (float)((value - deadZoneThreshold) / (32767.0f - deadZoneThreshold));
19580 }
19581 return(result);
19582}
19583
19584fpl_internal void fpl__LinuxPushGameControllerStateUpdateEvent(const struct js_event *event, fpl__LinuxGameController *controller) {
19585 fplGamepadState *padState = &controller->state;
19586
19587 // @TODO(final): Use a static offset table instead of a pointer mapping table
19588 fplGamepadButton *buttonMappingTable[12] = fplZeroInit;
19589 buttonMappingTable[0] = &padState->actionA;
19590 buttonMappingTable[1] = &padState->actionB;
19591 buttonMappingTable[2] = &padState->actionX;
19592 buttonMappingTable[3] = &padState->actionY;
19593 buttonMappingTable[4] = &padState->leftShoulder;
19594 buttonMappingTable[5] = &padState->rightShoulder;
19595 buttonMappingTable[6] = &padState->back;
19596 buttonMappingTable[7] = &padState->start;
19597 // 8 = XBox-Button
19598 buttonMappingTable[9] = &padState->leftThumb;
19599 buttonMappingTable[10] = &padState->rightThumb;
19600
19601 const int16_t deadZoneThresholdLeftStick = 5000;
19602 const int16_t deadZoneThresholdRightStick = 5000;
19603
19604 switch (event->type & ~JS_EVENT_INIT) {
19605 case JS_EVENT_AXIS:
19606 {
19607 switch (event->number) {
19608 // Left stick
19609 case 0:
19610 {
19611 padState->leftStickX = fpl__LinuxJoystickProcessStickValue(event->value, deadZoneThresholdLeftStick);
19612 } break;
19613 case 1:
19614 {
19615 padState->leftStickY = fpl__LinuxJoystickProcessStickValue(-event->value, deadZoneThresholdLeftStick);
19616 } break;
19617
19618 // Right stick
19619 case 3:
19620 {
19621 padState->rightStickX = fpl__LinuxJoystickProcessStickValue(event->value, deadZoneThresholdRightStick);
19622 } break;
19623 case 4:
19624 {
19625 padState->rightStickY = fpl__LinuxJoystickProcessStickValue(-event->value, deadZoneThresholdRightStick);
19626 } break;
19627
19628 // Left/right trigger
19629 case 2:
19630 {
19631 padState->leftTrigger = (float)((event->value + 32768) >> 8) / 255.0f;
19632 } break;
19633 case 5:
19634 {
19635 padState->rightTrigger = (float)((event->value + 32768) >> 8) / 255.0f;
19636 } break;
19637
19638 // DPad X-Axis
19639 case 6:
19640 {
19641 if (event->value == -32767) {
19642 padState->dpadLeft.isDown = true;
19643 padState->dpadRight.isDown = false;
19644 } else if (event->value == 32767) {
19645 padState->dpadLeft.isDown = false;
19646 padState->dpadRight.isDown = true;
19647 } else {
19648 padState->dpadLeft.isDown = false;
19649 padState->dpadRight.isDown = false;
19650 }
19651 } break;
19652
19653 // DPad Y-Axis
19654 case 7:
19655 {
19656 if (event->value == -32767) {
19657 padState->dpadUp.isDown = true;
19658 padState->dpadDown.isDown = false;
19659 } else if (event->value == 32767) {
19660 padState->dpadUp.isDown = false;
19661 padState->dpadDown.isDown = true;
19662 } else {
19663 padState->dpadUp.isDown = false;
19664 padState->dpadDown.isDown = false;
19665 }
19666 } break;
19667
19668 default:
19669 break;
19670 }
19671 } break;
19672
19673 case JS_EVENT_BUTTON:
19674 {
19675 if ((event->number >= 0) && (event->number < fplArrayCount(buttonMappingTable))) {
19676 fplGamepadButton *mappedButton = buttonMappingTable[event->number];
19677 if (mappedButton != fpl_null) {
19678 mappedButton->isDown = event->value != 0;
19679 }
19680 }
19681 } break;
19682
19683 default:
19684 break;
19685 }
19686}
19687
19688fpl_internal void fpl__LinuxPollGameControllers(const fplSettings *settings, fpl__LinuxGameControllersState *controllersState, const bool useEvents) {
19689 // https://github.com/underdoeg/ofxGamepad
19690 // https://github.com/elanthis/gamepad
19691 // https://gist.github.com/jasonwhite/c5b2048c15993d285130
19692 // https://github.com/Tasssadar/libenjoy/blob/master/src/libenjoy_linux.c
19693
19694 if (((controllersState->lastCheckTime == 0) || ((fplMillisecondsQuery() - controllersState->lastCheckTime) >= settings->input.controllerDetectionFrequency)) || !useEvents) {
19695 controllersState->lastCheckTime = fplMillisecondsQuery();
19696
19697 //
19698 // Detect new controllers
19699 //
19700 const char *deviceNames[] = {
19701 "/dev/input/js0",
19702 };
19703 for (int deviceNameIndex = 0; deviceNameIndex < fplArrayCount(deviceNames); ++deviceNameIndex) {
19704 const char *deviceName = deviceNames[deviceNameIndex];
19705 bool alreadyFound = false;
19706 int freeIndex = -1;
19707 for (uint32_t controllerIndex = 0; controllerIndex < fplArrayCount(controllersState->controllers); ++controllerIndex) {
19708 fpl__LinuxGameController *controller = controllersState->controllers + controllerIndex;
19709 if ((controller->fd > 0) && fplIsStringEqual(deviceName, controller->deviceName)) {
19710 alreadyFound = true;
19711 break;
19712 }
19713 if (controller->fd == 0) {
19714 if (freeIndex == -1) {
19715 freeIndex = controllerIndex;
19716 }
19717 }
19718 }
19719 if (!alreadyFound && freeIndex >= 0) {
19720 int fd = open(deviceName, O_RDONLY);
19721 if (fd < 0) {
19722 FPL_LOG_ERROR(FPL__MODULE_LINUX, "Failed opening joystick device '%s'", deviceName);
19723 continue;
19724 }
19725 uint8_t numAxis = 0;
19726 uint8_t numButtons = 0;
19727 ioctl(fd, JSIOCGAXES, &numAxis);
19728 ioctl(fd, JSIOCGBUTTONS, &numButtons);
19729 if (numAxis == 0 || numButtons == 0) {
19730 FPL_LOG_ERROR(FPL__MODULE_LINUX, "Joystick device '%s' does not have enough buttons/axis to map to a XInput controller!", deviceName);
19731 close(fd);
19732 continue;
19733 }
19734
19735 // @NOTE(final): We do not want to detect devices which are not proper joysticks, such as gaming keyboards
19736 struct js_event msg;
19737 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))) {
19738 // No joystick message
19739 close(fd);
19740 continue;
19741 }
19742
19743 fpl__LinuxGameController *controller = controllersState->controllers + freeIndex;
19744 fplClearStruct(controller);
19745 controller->fd = fd;
19746 controller->axisCount = numAxis;
19747 controller->buttonCount = numButtons;
19748 fplCopyString(deviceName, controller->deviceName, fplArrayCount(controller->deviceName));
19749 ioctl(fd, JSIOCGNAME(fplArrayCount(controller->displayName)), controller->displayName);
19750 fcntl(fd, F_SETFL, O_NONBLOCK);
19751
19752 if (useEvents) {
19753 // Push connected event
19754 fplEvent ev = fplZeroInit;
19757 ev.gamepad.deviceIndex = (uint32_t)freeIndex;
19758 ev.gamepad.deviceName = controller->deviceName;
19759 fpl__PushInternalEvent(&ev);
19760 }
19761 }
19762 }
19763 }
19764
19765 // Update controller states
19766 for (uint32_t controllerIndex = 0; controllerIndex < fplArrayCount(controllersState->controllers); ++controllerIndex) {
19767 fpl__LinuxGameController *controller = controllersState->controllers + controllerIndex;
19768 if (controller->fd > 0) {
19769 // Update button/axis state
19770 struct js_event event;
19771 bool wasDisconnected = false;
19772 for (;;) {
19773 errno = 0;
19774 if (read(controller->fd, &event, sizeof(event)) < 0) {
19775 if (errno == ENODEV) {
19776 close(controller->fd);
19777 controller->fd = 0;
19778 fplClearStruct(&controller->state);
19779 wasDisconnected = true;
19780 if (useEvents) {
19781 // Push disconnected event
19782 fplEvent ev = fplZeroInit;
19785 ev.gamepad.deviceIndex = controllerIndex;
19786 ev.gamepad.deviceName = controller->deviceName;
19787 fpl__PushInternalEvent(&ev);
19788 }
19789 }
19790 break;
19791 }
19792 fpl__LinuxPushGameControllerStateUpdateEvent(&event, controller);
19793 }
19794
19795 controller->state.isActive = !fpl__IsZeroMemory(&controller->state, sizeof(fplGamepadState));
19796 controller->state.isConnected = !wasDisconnected;
19797 controller->state.deviceName = controller->deviceName;
19798
19799 if (controller->fd > 0) {
19800 if (useEvents) {
19801 // Push state event
19802 fplEvent ev = fplZeroInit;
19805 ev.gamepad.deviceIndex = controllerIndex;
19806 ev.gamepad.deviceName = controller->deviceName;
19807 ev.gamepad.state = controller->state;
19808 fpl__PushInternalEvent(&ev);
19809 }
19810 }
19811 }
19812 }
19813}
19814
19816 FPL__CheckPlatform(false);
19817 FPL__CheckArgumentNull(outStates, false);
19818 fpl__PlatformAppState *appState = fpl__global__AppState;
19819 if (appState->initFlags & fplInitFlags_GameController) {
19820#if defined(FPL_PLATFORM_LINUX)
19821 fpl__LinuxGameControllersState *controllersState = &appState->plinux.controllersState;
19822 fpl__LinuxPollGameControllers(&appState->currentSettings, controllersState, false);
19823
19824 fplAssert(fplArrayCount(controllersState->controllers) == fplArrayCount(outStates->deviceStates));
19825 for (int i = 0; i < fplArrayCount(controllersState->controllers); ++i) {
19826 outStates->deviceStates[i] = controllersState->controllers[i].state;
19827 }
19828 return(true);
19829#endif
19830 }
19831 return(false);
19832}
19833
19834#endif // FPL__ENABLE_WINDOW
19835
19836//
19837// Linux Threading
19838//
19839fpl_platform_api bool fplSignalInit(fplSignalHandle *signal, const fplSignalValue initialValue) {
19840 FPL__CheckArgumentNull(signal, false);
19841 if (signal->isValid) {
19842 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is already valid", signal);
19843 return false;
19844 }
19845 int linuxEventHandle = eventfd((initialValue == fplSignalValue_Set) ? 1 : 0, EFD_CLOEXEC);
19846 if (linuxEventHandle == -1) {
19847 FPL__ERROR(FPL__MODULE_THREADING, "Failed initializing signal '%p'", signal);
19848 return false;
19849 }
19850 fplClearStruct(signal);
19851 signal->isValid = true;
19852 signal->internalHandle.linuxEventHandle = linuxEventHandle;
19853 return(true);
19854}
19855
19857 if (signal != fpl_null && signal->isValid) {
19858 close(signal->internalHandle.linuxEventHandle);
19859 fplClearStruct(signal);
19860 }
19861}
19862
19864 FPL__CheckArgumentNull(signal, false);
19865 if (!signal->isValid) {
19866 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is not valid", signal);
19867 return(false);
19868 }
19869 int ev = signal->internalHandle.linuxEventHandle;
19870 if (timeout == FPL_TIMEOUT_INFINITE) {
19871 uint64_t value;
19872 read(ev, &value, sizeof(value));
19873 return true;
19874 } else {
19875 fd_set f;
19876 FD_ZERO(&f);
19877 FD_SET(ev, &f);
19878 struct timeval t = { 0, timeout * 1000 };
19879 int selectResult = select(1, &f, NULL, NULL, &t);
19880 if (selectResult == 0) {
19881 // Timeout
19882 return false;
19883 } else if (selectResult == -1) {
19884 // Error
19885 return false;
19886 } else {
19887 return true;
19888 }
19889 }
19890}
19891
19892fpl_internal bool fpl__LinuxSignalWaitForMultiple(fplSignalHandle *signals[], const uint32_t minCount, const uint32_t maxCount, const size_t stride, const fplTimeoutValue timeout) {
19893 FPL__CheckArgumentNull(signals, false);
19894 FPL__CheckArgumentMax(maxCount, FPL_MAX_SIGNAL_COUNT, false);
19895 const size_t actualStride = stride > 0 ? stride : sizeof(fplSignalHandle *);
19896 for (uint32_t index = 0; index < maxCount; ++index) {
19897 fplSignalHandle *signal = *(fplSignalHandle **)((uint8_t *)signals + index * actualStride);
19898 if (signal == fpl_null) {
19899 FPL__ERROR(FPL__MODULE_THREADING, "Signal for index '%d' are not allowed to be null", index);
19900 return false;
19901 }
19902 if (!signal->isValid) {
19903 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' for index '%d' is not valid", signal, index);
19904 return false;
19905 }
19906 }
19907
19908 int e = epoll_create(maxCount);
19909 fplAssert(e != 0);
19910
19911 // @MEMORY(final): This wastes a lof memory, use temporary memory allocation here
19912
19913 // Register events and map each to the array index
19914 struct epoll_event events[FPL_MAX_SIGNAL_COUNT];
19915 for (int index = 0; index < maxCount; index++) {
19916 events[index].events = EPOLLIN;
19917 events[index].data.u32 = index;
19918 fplSignalHandle *signal = *(fplSignalHandle **)((uint8_t *)signals + index * actualStride);
19919 int x = epoll_ctl(e, EPOLL_CTL_ADD, signal->internalHandle.linuxEventHandle, events + index);
19920 fplAssert(x == 0);
19921 }
19922
19923 // Wait
19924 int t = timeout == FPL_TIMEOUT_INFINITE ? -1 : timeout;
19925 int eventsResult = -1;
19926 int waiting = minCount;
19927 struct epoll_event revent[FPL_MAX_SIGNAL_COUNT];
19928 while (waiting > 0) {
19929 int ret = epoll_wait(e, revent, waiting, t);
19930 if (ret == 0) {
19931 if (minCount == maxCount) {
19932 eventsResult = -1;
19933 }
19934 break;
19935 }
19936 for (int eventIndex = 0; eventIndex < ret; eventIndex++) {
19937 uint32_t signalIndex = revent[eventIndex].data.u32;
19938 fplSignalHandle *signal = *(fplSignalHandle **)((uint8_t *)signals + signalIndex * actualStride);
19939 epoll_ctl(e, EPOLL_CTL_DEL, signal->internalHandle.linuxEventHandle, NULL);
19940 }
19941 eventsResult = revent[0].data.u32;
19942 waiting -= ret;
19943 }
19944 close(e);
19945 bool result = (waiting == 0);
19946 return(result);
19947}
19948
19949fpl_platform_api bool fplSignalWaitForAll(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
19950 bool result = fpl__LinuxSignalWaitForMultiple(signals, count, count, stride, timeout);
19951 return(result);
19952}
19953
19954fpl_platform_api bool fplSignalWaitForAny(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
19955 bool result = fpl__LinuxSignalWaitForMultiple(signals, 1, count, stride, timeout);
19956 return(result);
19957}
19958
19960 FPL__CheckArgumentNull(signal, false);
19961 if (!signal->isValid) {
19962 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is not valid", signal);
19963 return(false);
19964 }
19965 uint64_t value = 1;
19966 int writtenBytes = write(signal->internalHandle.linuxEventHandle, &value, sizeof(value));
19967 bool result = writtenBytes == sizeof(value);
19968 return(result);
19969}
19970
19971//
19972// Linux Hardware
19973//
19975 FPL__CheckArgumentNull(outInfos, false);
19976 bool result = false;
19977
19978 // https://git.i-scream.org/?p=libstatgrab.git;a=blob;f=src/libstatgrab/memory_stats.c;h=a6f6fb926b72d3b691848202e397e3db58255648;hb=HEAD
19979
19980 return(result);
19981}
19982
19983//
19984// Linux Paths
19985//
19986fpl_internal size_t fpl__LinuxLocaleToISO639(const char *source, char *target, const size_t maxTargetLen) {
19987 size_t result = fplGetStringLength(source);
19988 if (target != fpl_null) {
19989 fplCopyStringLen(source, result, target, maxTargetLen);
19990 char *p = target;
19991 while (*p) {
19992 if (*p == '_') {
19993 *p = '-'; // Replace underscore with minus
19994 } else if (*p == '.') {
19995 *p = '\0'; // Replace dot with zero char
19996 break;
19997 }
19998 ++p;
19999 }
20000 }
20001 return(result);
20002}
20003
20004//
20005// Linux Localization
20006//
20007fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
20008 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
20009 char *locale = setlocale(LC_CTYPE, NULL);
20010 size_t result = fpl__LinuxLocaleToISO639(locale, buffer, maxBufferLen);
20011 return(result);
20012}
20013
20014fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
20015 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
20016 char *locale = setlocale(LC_ALL, NULL);
20017 size_t result = fpl__LinuxLocaleToISO639(locale, buffer, maxBufferLen);
20018 return(result);
20019}
20020
20021fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
20022 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
20023 char *locale = setlocale(LC_ALL, NULL);
20024 size_t result = fpl__LinuxLocaleToISO639(locale, buffer, maxBufferLen);
20025 return(result);
20026}
20027
20028#endif // FPL_PLATFORM_LINUX
20029
20030// ############################################################################
20031//
20032// > UNIX_PLATFORM
20033//
20034// ############################################################################
20035#if defined(FPL_PLATFORM_UNIX)
20036fpl_internal void fpl__UnixReleasePlatform(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
20037}
20038
20039fpl_internal bool fpl__UnixInitPlatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
20040 return true;
20041}
20042
20043//
20044// Unix Hardware
20045//
20047 // @IMPLEMENT(final/Unix): fplMemoryGetInfos
20048 return(false);
20049}
20050
20051//
20052// Unix Localization
20053//
20054fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
20055 // @IMPLEMENT(final/Unix): fplGetSystemLocale
20056 return(0);
20057}
20058
20059fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
20060 // @IMPLEMENT(final/Unix): fplGetUserLocale
20061 return(0);
20062}
20063
20064fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
20065 // @IMPLEMENT(final/Unix): fplGetInputLocale
20066 return(0);
20067}
20068#endif // FPL_PLATFORM_UNIX
20069
20070// ****************************************************************************
20071//
20072// > VIDEO_BACKENDS
20073//
20074// ****************************************************************************
20075#if !defined(FPL__VIDEO_BACKENDS_IMPLEMENTED) && defined(FPL__ENABLE_VIDEO)
20076# define FPL__VIDEO_BACKENDS_IMPLEMENTED
20077
20078//
20079// Video Backend Abstraction
20080//
20081
20082// Video data used for every backend (Software backbuffer for now)
20083typedef struct fpl__VideoData {
20084#if defined(FPL__ENABLE_VIDEO_SOFTWARE)
20085 fplVideoBackBuffer backbuffer;
20086#endif
20087 uint64_t dummy;
20088} fpl__VideoData;
20089
20090struct fpl__VideoBackend;
20091
20092#define FPL__FUNC_VIDEO_BACKEND_LOAD(name) bool name(const fpl__PlatformAppState *appState, struct fpl__VideoBackend *backend)
20093typedef FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__func_VideoBackendLoad);
20094
20095#define FPL__FUNC_VIDEO_BACKEND_UNLOAD(name) void name(const fpl__PlatformAppState *appState, struct fpl__VideoBackend *backend)
20096typedef FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__func_VideoBackendUnload);
20097
20098#define FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(name) bool name(const fpl__PlatformAppState *appState, const fplVideoSettings *videoSettings, fpl__PlatformWindowState *windowState, struct fpl__VideoBackend *backend)
20099typedef FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__func_VideoBackendPrepareWindow);
20100
20101#define FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(name) bool name(const fpl__PlatformAppState *appState, const fplVideoSettings *videoSettings, fpl__PlatformWindowState *windowState, struct fpl__VideoBackend *backend)
20102typedef FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__func_VideoBackendFinalizeWindow);
20103
20104#define FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(name) void name(const fpl__PlatformAppState *appState, struct fpl__VideoBackend *backend)
20105typedef FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(fpl__func_VideoBackendDestroyedWindow);
20106
20107#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)
20108typedef FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__func_VideoBackendInitialize);
20109
20110#define FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(name) void name(const fpl__PlatformAppState *appState, const fpl__PlatformWindowState *windowState, struct fpl__VideoBackend *backend)
20111typedef FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__func_VideoBackendShutdown);
20112
20113#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)
20114typedef FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__func_VideoBackendPresent);
20115
20116#define FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(name) const void *name(const struct fpl__VideoBackend *backend, const char *procName)
20117typedef FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__func_VideoBackendGetProcedure);
20118
20119#define FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(name) bool name(fplVideoRequirements *requirements)
20120typedef FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(fpl__func_VideoBackendGetRequirements);
20121
20122typedef struct fpl__VideoContext {
20123 fpl__func_VideoBackendLoad *loadFunc;
20124 fpl__func_VideoBackendUnload *unloadFunc;
20125 fpl__func_VideoBackendInitialize *initializeFunc;
20126 fpl__func_VideoBackendShutdown *shutdownFunc;
20127 fpl__func_VideoBackendPrepareWindow *prepareWindowFunc;
20128 fpl__func_VideoBackendFinalizeWindow *finalizeWindowFunc;
20129 fpl__func_VideoBackendDestroyedWindow *destroyedWindowFunc;
20130 fpl__func_VideoBackendPresent *presentFunc;
20131 fpl__func_VideoBackendGetProcedure *getProcedureFunc;
20132 fpl__func_VideoBackendGetRequirements *getRequirementsFunc;
20133 fpl_b32 recreateOnResize;
20134} fpl__VideoContext;
20135
20136// Video context stubs
20137fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Load_Stub) { return(true); }
20138fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Unload_Stub) {}
20139fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_PrepareWindow_Stub) { return(true); }
20140fpl_internal FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__VideoBackend_FinalizeWindow_Stub) { return(true); }
20141fpl_internal FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(fpl__VideoBackend_DestroyedWindow_Stub) {}
20142fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Initialize_Stub) { return(false); }
20143fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Shutdown_Stub) {}
20144fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Present_Stub) {}
20145fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_GetProcedure_Stub) { return(fpl_null); }
20146fpl_internal FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(fpl__VideoBackend_GetRequirements_Stub) { return(false); }
20147
20148fpl_internal fpl__VideoContext fpl__StubVideoContext(void) {
20149 fpl__VideoContext result = fplZeroInit;
20150 result.loadFunc = fpl__VideoBackend_Load_Stub;
20151 result.unloadFunc = fpl__VideoBackend_Unload_Stub;
20152 result.prepareWindowFunc = fpl__VideoBackend_PrepareWindow_Stub;
20153 result.finalizeWindowFunc = fpl__VideoBackend_FinalizeWindow_Stub;
20154 result.destroyedWindowFunc = fpl__VideoBackend_DestroyedWindow_Stub;
20155 result.initializeFunc = fpl__VideoBackend_Initialize_Stub;
20156 result.shutdownFunc = fpl__VideoBackend_Shutdown_Stub;
20157 result.presentFunc = fpl__VideoBackend_Present_Stub;
20158 result.getProcedureFunc = fpl__VideoBackend_GetProcedure_Stub;
20159 result.getRequirementsFunc = fpl__VideoBackend_GetRequirements_Stub;
20160 return(result);
20161}
20162
20163// "VIDEOSYS" Video Backend Magic 8CC
20164#define FPL__VIDEOBACKEND_MAGIC (uint64_t)0x564944454f535953
20165
20166typedef struct fpl__VideoBackend {
20167 uint64_t magic;
20168 fplVideoSurface surface;
20169} fpl__VideoBackend;
20170
20171// ############################################################################
20172//
20173// > VIDEO_BACKEND_OPENGL_WIN32
20174//
20175// ############################################################################
20176#if defined(FPL__ENABLE_VIDEO_OPENGL) && defined(FPL_PLATFORM_WINDOWS)
20177
20178#define FPL__GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
20179#define FPL__GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
20180#define FPL__GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004
20181#define FPL__GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008
20182#define FPL__GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
20183#define FPL__GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
20184
20185#define FPL__WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
20186#define FPL__WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
20187#define FPL__WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
20188#define FPL__WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
20189#define FPL__WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
20190#define FPL__WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
20191#define FPL__WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
20192#define FPL__WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
20193#define FPL__WGL_CONTEXT_FLAGS_ARB 0x2094
20194#define FPL__WGL_DRAW_TO_WINDOW_ARB 0x2001
20195#define FPL__WGL_ACCELERATION_ARB 0x2003
20196#define FPL__WGL_SWAP_METHOD_ARB 0x2007
20197#define FPL__WGL_SUPPORT_OPENGL_ARB 0x2010
20198#define FPL__WGL_DOUBLE_BUFFER_ARB 0x2011
20199#define FPL__WGL_PIXEL_TYPE_ARB 0x2013
20200#define FPL__WGL_COLOR_BITS_ARB 0x2014
20201#define FPL__WGL_ALPHA_BITS_ARB 0x201B
20202#define FPL__WGL_DEPTH_BITS_ARB 0x2022
20203#define FPL__WGL_STENCIL_BITS_ARB 0x2023
20204#define FPL__WGL_FULL_ACCELERATION_ARB 0x2027
20205#define FPL__WGL_SWAP_EXCHANGE_ARB 0x2028
20206#define FPL__WGL_TYPE_RGBA_ARB 0x202B
20207#define FPL__WGL_SAMPLE_BUFFERS_ARB 0x2041
20208#define FPL__WGL_SAMPLES_ARB 0x2042
20209
20210#define FPL__FUNC_WGL_wglMakeCurrent(name) BOOL WINAPI name(HDC deviceContext, HGLRC renderingContext)
20211typedef FPL__FUNC_WGL_wglMakeCurrent(fpl__win32_func_wglMakeCurrent);
20212#define FPL__FUNC_WGL_wglGetProcAddress(name) PROC WINAPI name(LPCSTR procedure)
20213typedef FPL__FUNC_WGL_wglGetProcAddress(fpl__win32_func_wglGetProcAddress);
20214#define FPL__FUNC_WGL_wglDeleteContext(name) BOOL WINAPI name(HGLRC renderingContext)
20215typedef FPL__FUNC_WGL_wglDeleteContext(fpl__win32_func_wglDeleteContext);
20216#define FPL__FUNC_WGL_wglCreateContext(name) HGLRC WINAPI name(HDC deviceContext)
20217typedef FPL__FUNC_WGL_wglCreateContext(fpl__win32_func_wglCreateContext);
20218
20219#define FPL__FUNC_WGL_wglChoosePixelFormatARB(name) BOOL WINAPI name(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats)
20220typedef FPL__FUNC_WGL_wglChoosePixelFormatARB(fpl__win32_func_wglChoosePixelFormatARB);
20221#define FPL__FUNC_WGL_wglCreateContextAttribsARB(name) HGLRC WINAPI name(HDC hDC, HGLRC hShareContext, const int *attribList)
20222typedef FPL__FUNC_WGL_wglCreateContextAttribsARB(fpl__win32_func_wglCreateContextAttribsARB);
20223#define FPL__FUNC_WGL_wglSwapIntervalEXT(name) BOOL WINAPI name(int interval)
20224typedef FPL__FUNC_WGL_wglSwapIntervalEXT(fpl__win32_func_wglSwapIntervalEXT);
20225
20226typedef struct fpl__Win32OpenGLApi {
20227 HMODULE openglLibrary;
20228 fpl__win32_func_wglMakeCurrent *wglMakeCurrent;
20229 fpl__win32_func_wglGetProcAddress *wglGetProcAddress;
20230 fpl__win32_func_wglDeleteContext *wglDeleteContext;
20231 fpl__win32_func_wglCreateContext *wglCreateContext;
20232 fpl__win32_func_wglChoosePixelFormatARB *wglChoosePixelFormatARB;
20233 fpl__win32_func_wglCreateContextAttribsARB *wglCreateContextAttribsARB;
20234 fpl__win32_func_wglSwapIntervalEXT *wglSwapIntervalEXT;
20235} fpl__Win32OpenGLApi;
20236
20237fpl_internal void fpl__UnloadWin32OpenGLApi(fpl__Win32OpenGLApi *api) {
20238 if (api->openglLibrary != fpl_null) {
20239 FreeLibrary(api->openglLibrary);
20240 }
20241 fplClearStruct(api);
20242}
20243
20244fpl_internal bool fpl__LoadWin32OpenGLApi(fpl__Win32OpenGLApi *api, const char *libraryName) {
20245 if (fplGetStringLength(libraryName) == 0) {
20246 libraryName = "opengl32.dll";
20247 }
20248 bool result = false;
20249 fplClearStruct(api);
20250 do {
20251 HMODULE openglLibrary = fpl_null;
20252 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName);
20253 api->openglLibrary = openglLibrary;
20254 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglGetProcAddress, wglGetProcAddress);
20255 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglCreateContext, wglCreateContext);
20256 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglDeleteContext, wglDeleteContext);
20257 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglMakeCurrent, wglMakeCurrent);
20258 result = true;
20259 } while (0);
20260 if (!result) {
20261 fpl__UnloadWin32OpenGLApi(api);
20262 }
20263 return(result);
20264}
20265
20266typedef struct fpl__VideoBackendWin32OpenGL {
20267 fpl__VideoBackend base;
20268 fpl__Win32OpenGLApi api;
20269 HGLRC renderingContext;
20270} fpl__VideoBackendWin32OpenGL;
20271
20272fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_Win32OpenGL_GetProcedure) {
20273 const fpl__VideoBackendWin32OpenGL *nativeBackend = (const fpl__VideoBackendWin32OpenGL *)backend;
20274 void *result = (void *)GetProcAddress(nativeBackend->api.openglLibrary, procName);
20275 return(result);
20276}
20277
20278fpl_internal LRESULT CALLBACK fpl__Win32OpenGLTemporaryWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
20279 fpl__Win32AppState *appState = &fpl__global__AppState->win32;
20280 const fpl__Win32Api *wapi = &appState->winApi;
20281 switch (message) {
20282 case WM_CLOSE:
20283 wapi->user.PostQuitMessage(0);
20284 break;
20285 default:
20286 return wapi->user.DefWindowProcW(hWnd, message, wParam, lParam);
20287 }
20288 return 0;
20289}
20290
20291fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_Win32OpenGL_PrepareWindow) {
20292 const fpl__Win32AppState *nativeAppState = &appState->win32;
20293 const fpl__Win32Api *wapi = &nativeAppState->winApi;
20294 fpl__Win32WindowState *nativeWindowState = &windowState->win32;
20295
20296 nativeWindowState->pixelFormat = 0;
20297
20298 if (videoSettings->graphics.opengl.compabilityFlags != fplOpenGLCompabilityFlags_Legacy) {
20299 fpl__Win32OpenGLApi glApi;
20300 if (fpl__LoadWin32OpenGLApi(&glApi, videoSettings->graphics.opengl.libraryFile)) {
20301 // Register temporary window class
20302 WNDCLASSEXW windowClass = fplZeroInit;
20303 windowClass.cbSize = sizeof(windowClass);
20304 windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
20305 windowClass.lpfnWndProc = fpl__Win32OpenGLTemporaryWindowProc;
20306 windowClass.hInstance = GetModuleHandleW(fpl_null);
20307 windowClass.hCursor = fpl__win32_LoadCursor(fpl_null, IDC_ARROW);
20308 windowClass.lpszClassName = L"FPL_Temp_GL_Window";
20309 if (wapi->user.RegisterClassExW(&windowClass)) {
20310 // Create temporary window
20311 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);
20312 if (tempWindowHandle != fpl_null) {
20313 // Get temporary device context
20314 HDC tempDC = wapi->user.GetDC(tempWindowHandle);
20315 if (tempDC != fpl_null) {
20316 // Get legacy pixel format
20317 PIXELFORMATDESCRIPTOR fakePFD = fplZeroInit;
20318 fakePFD.nSize = sizeof(fakePFD);
20319 fakePFD.nVersion = 1;
20320 fakePFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
20321 fakePFD.iPixelType = PFD_TYPE_RGBA;
20322 fakePFD.cColorBits = 32;
20323 fakePFD.cAlphaBits = 8;
20324 fakePFD.cDepthBits = 24;
20325 int fakePFDID = wapi->gdi.ChoosePixelFormat(tempDC, &fakePFD);
20326 if (fakePFDID != 0) {
20327 if (wapi->gdi.SetPixelFormat(tempDC, fakePFDID, &fakePFD)) {
20328 // Create temporary rendering context
20329 HGLRC tempCtx = glApi.wglCreateContext(tempDC);
20330 if (tempCtx != fpl_null) {
20331 if (glApi.wglMakeCurrent(tempDC, tempCtx)) {
20332 glApi.wglChoosePixelFormatARB = (fpl__win32_func_wglChoosePixelFormatARB *)(void *)glApi.wglGetProcAddress("wglChoosePixelFormatARB");
20333 if (glApi.wglChoosePixelFormatARB != fpl_null) {
20334 int multisampleCount = (int)videoSettings->graphics.opengl.multiSamplingCount;
20335 const int pixelAttribs[] = {
20336 FPL__WGL_DRAW_TO_WINDOW_ARB, 1,
20337 FPL__WGL_SUPPORT_OPENGL_ARB, 1,
20338 FPL__WGL_DOUBLE_BUFFER_ARB, 1,
20339 FPL__WGL_PIXEL_TYPE_ARB, FPL__WGL_TYPE_RGBA_ARB,
20340 FPL__WGL_ACCELERATION_ARB, FPL__WGL_FULL_ACCELERATION_ARB,
20341 FPL__WGL_COLOR_BITS_ARB, 32,
20342 FPL__WGL_ALPHA_BITS_ARB, 8,
20343 FPL__WGL_DEPTH_BITS_ARB, 24,
20344 FPL__WGL_STENCIL_BITS_ARB, 8,
20345 FPL__WGL_SAMPLE_BUFFERS_ARB, (multisampleCount > 0) ? 1 : 0,
20346 FPL__WGL_SAMPLES_ARB, multisampleCount,
20347 0
20348 };
20349 int pixelFormat;
20350 UINT numFormats;
20351 if (glApi.wglChoosePixelFormatARB(tempDC, pixelAttribs, NULL, 1, &pixelFormat, &numFormats)) {
20352 nativeWindowState->pixelFormat = pixelFormat;
20353 }
20354 }
20355 glApi.wglMakeCurrent(fpl_null, fpl_null);
20356 }
20357 glApi.wglDeleteContext(tempCtx);
20358 }
20359 }
20360 }
20361 wapi->user.ReleaseDC(tempWindowHandle, tempDC);
20362 }
20363 wapi->user.DestroyWindow(tempWindowHandle);
20364 }
20365 }
20366 fpl__UnloadWin32OpenGLApi(&glApi);
20367 }
20368 }
20369 return(true);
20370}
20371
20372fpl_internal FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__VideoBackend_Win32OpenGL_FinalizeWindow) {
20373 const fpl__Win32AppState *nativeAppState = &appState->win32;
20374 const fpl__Win32Api *wapi = &nativeAppState->winApi;
20375 fpl__Win32WindowState *nativeWindowState = &windowState->win32;
20376
20377 //
20378 // Prepare window for OpenGL
20379 //
20380 HDC deviceContext = nativeWindowState->deviceContext;
20381 HWND handle = nativeWindowState->windowHandle;
20382 PIXELFORMATDESCRIPTOR pfd = fplZeroInit;
20383
20384 // We may got a pixel format from the pre-setup
20385 bool pixelFormatSet = false;
20386 if (nativeWindowState->pixelFormat != 0) {
20387 wapi->gdi.DescribePixelFormat(deviceContext, nativeWindowState->pixelFormat, sizeof(pfd), &pfd);
20388 pixelFormatSet = wapi->gdi.SetPixelFormat(deviceContext, nativeWindowState->pixelFormat, &pfd) == TRUE;
20389 if (!pixelFormatSet) {
20390 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed setting Pixelformat '%d' from pre setup", nativeWindowState->pixelFormat);
20391 }
20392 }
20393 if (!pixelFormatSet) {
20394 fplClearStruct(&pfd);
20395 pfd.nSize = sizeof(pfd);
20396 pfd.nVersion = 1;
20397 pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
20398 pfd.iPixelType = PFD_TYPE_RGBA;
20399 pfd.cColorBits = 32;
20400 pfd.cDepthBits = 24;
20401 pfd.cAlphaBits = 8;
20402 pfd.cStencilBits = 8;
20403 pfd.iLayerType = PFD_MAIN_PLANE;
20404 int pixelFormat = wapi->gdi.ChoosePixelFormat(deviceContext, &pfd);
20405 if (!pixelFormat) {
20406 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);
20407 return false;
20408 }
20409 wapi->gdi.DescribePixelFormat(deviceContext, pixelFormat, sizeof(pfd), &pfd);
20410 if (!wapi->gdi.SetPixelFormat(deviceContext, pixelFormat, &pfd)) {
20411 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);
20412 return false;
20413 }
20414 }
20415 return true;
20416}
20417
20418fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Win32OpenGL_Shutdown) {
20419 const fpl__Win32AppState *nativeAppState = &appState->win32;
20420
20421 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
20422 const fpl__Win32OpenGLApi *glapi = &nativeBackend->api;
20423
20424 if (nativeBackend->renderingContext) {
20425 glapi->wglMakeCurrent(fpl_null, fpl_null);
20426 glapi->wglDeleteContext(nativeBackend->renderingContext);
20427 nativeBackend->renderingContext = fpl_null;
20428 }
20429}
20430
20431fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Win32OpenGL_Initialize) {
20432 const fpl__Win32AppState *nativeAppState = &appState->win32;
20433 const fpl__Win32WindowState *nativeWindowState = &windowState->win32;
20434 const fpl__Win32Api *wapi = &nativeAppState->winApi;
20435
20436 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
20437
20438 fpl__Win32OpenGLApi *glapi = &nativeBackend->api;
20439
20440 //
20441 // Create opengl rendering context
20442 //
20443 HDC deviceContext = nativeWindowState->deviceContext;
20444 HGLRC legacyRenderingContext = glapi->wglCreateContext(deviceContext);
20445 if (!legacyRenderingContext) {
20446 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed creating Legacy OpenGL Rendering Context for DC '%x')", deviceContext);
20447 return false;
20448 }
20449 if (!glapi->wglMakeCurrent(deviceContext, legacyRenderingContext)) {
20450 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed activating Legacy OpenGL Rendering Context for DC '%x' and RC '%x')", deviceContext, legacyRenderingContext);
20451 glapi->wglDeleteContext(legacyRenderingContext);
20452 return false;
20453 }
20454
20455 // Load WGL Extensions
20456 glapi->wglSwapIntervalEXT = (fpl__win32_func_wglSwapIntervalEXT *)(void *)glapi->wglGetProcAddress("wglSwapIntervalEXT");
20457 glapi->wglChoosePixelFormatARB = (fpl__win32_func_wglChoosePixelFormatARB *)(void *)glapi->wglGetProcAddress("wglChoosePixelFormatARB");
20458 glapi->wglCreateContextAttribsARB = (fpl__win32_func_wglCreateContextAttribsARB *)(void *)glapi->wglGetProcAddress("wglCreateContextAttribsARB");
20459
20460 // Disable legacy context
20461 glapi->wglMakeCurrent(fpl_null, fpl_null);
20462
20463 HGLRC activeRenderingContext;
20464 if (videoSettings->graphics.opengl.compabilityFlags != fplOpenGLCompabilityFlags_Legacy) {
20465 // @NOTE(final): This is only available in OpenGL 3.0+
20466 if (!(videoSettings->graphics.opengl.majorVersion >= 3 && videoSettings->graphics.opengl.minorVersion >= 0)) {
20467 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "You have not specified the 'majorVersion' and 'minorVersion' in the VideoSettings");
20468 return false;
20469 }
20470 if (glapi->wglChoosePixelFormatARB == fpl_null) {
20471 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "wglChoosePixelFormatARB is not available, modern OpenGL is not available for your video card");
20472 return false;
20473 }
20474 if (glapi->wglCreateContextAttribsARB == fpl_null) {
20475 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "wglCreateContextAttribsARB is not available, modern OpenGL is not available for your video card");
20476 return false;
20477 }
20478
20479 int profile = 0;
20480 int flags = 0;
20481 if (videoSettings->graphics.opengl.compabilityFlags & fplOpenGLCompabilityFlags_Core) {
20482 profile = FPL__WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
20483 } else if (videoSettings->graphics.opengl.compabilityFlags & fplOpenGLCompabilityFlags_Compability) {
20484 profile = FPL__WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
20485 } else {
20486 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "No opengl compability profile selected, please specific Core fplOpenGLCompabilityFlags_Core or fplOpenGLCompabilityFlags_Compability");
20487 return false;
20488 }
20489 if (videoSettings->graphics.opengl.compabilityFlags & fplOpenGLCompabilityFlags_Forward) {
20490 flags = FPL__WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
20491 }
20492
20493 int contextAttribIndex = 0;
20494 int contextAttribList[20 + 1] = fplZeroInit;
20495 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_MAJOR_VERSION_ARB;
20496 contextAttribList[contextAttribIndex++] = (int)videoSettings->graphics.opengl.majorVersion;
20497 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_MINOR_VERSION_ARB;
20498 contextAttribList[contextAttribIndex++] = (int)videoSettings->graphics.opengl.minorVersion;
20499 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_PROFILE_MASK_ARB;
20500 contextAttribList[contextAttribIndex++] = profile;
20501 if (flags > 0) {
20502 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_FLAGS_ARB;
20503 contextAttribList[contextAttribIndex++] = flags;
20504 }
20505
20506 // Create modern opengl rendering context
20507 HGLRC modernRenderingContext = glapi->wglCreateContextAttribsARB(deviceContext, 0, contextAttribList);
20508 if (modernRenderingContext) {
20509 if (!glapi->wglMakeCurrent(deviceContext, modernRenderingContext)) {
20510 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);
20511
20512 glapi->wglDeleteContext(modernRenderingContext);
20513 modernRenderingContext = fpl_null;
20514
20515 // Fallback to legacy context
20516 glapi->wglMakeCurrent(deviceContext, legacyRenderingContext);
20517 activeRenderingContext = legacyRenderingContext;
20518 } else {
20519 // Destroy legacy rendering context
20520 glapi->wglDeleteContext(legacyRenderingContext);
20521 legacyRenderingContext = fpl_null;
20522 activeRenderingContext = modernRenderingContext;
20523 }
20524 } else {
20525 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);
20526
20527 // Fallback to legacy context
20528 glapi->wglMakeCurrent(deviceContext, legacyRenderingContext);
20529 activeRenderingContext = legacyRenderingContext;
20530 }
20531 } else {
20532 // Caller wants legacy context
20533 glapi->wglMakeCurrent(deviceContext, legacyRenderingContext);
20534 activeRenderingContext = legacyRenderingContext;
20535 }
20536
20537 fplAssert(activeRenderingContext != fpl_null);
20538 nativeBackend->renderingContext = activeRenderingContext;
20539
20540 // Set vertical syncronisation if available
20541 if (glapi->wglSwapIntervalEXT != fpl_null) {
20542 int swapInterval = videoSettings->isVSync ? 1 : 0;
20543 glapi->wglSwapIntervalEXT(swapInterval);
20544 }
20545
20546 backend->surface.window.win32.deviceContext = deviceContext;
20547 backend->surface.window.win32.windowHandle = nativeWindowState->windowHandle;
20548 backend->surface.opengl.renderingContext = (void *)activeRenderingContext;
20549
20550 return true;
20551}
20552
20553fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Win32OpenGL_Unload) {
20554 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
20555 fpl__UnloadWin32OpenGLApi(&nativeBackend->api);
20556 fplClearStruct(nativeBackend);
20557}
20558
20559fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Win32OpenGL_Load) {
20560 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
20561 const fplVideoSettings *videoSettings = &appState->currentSettings.video;
20562 fplClearStruct(nativeBackend);
20563 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
20564 if (!fpl__LoadWin32OpenGLApi(&nativeBackend->api, videoSettings->graphics.opengl.libraryFile)) {
20565 return(false);
20566 }
20567 return(true);
20568}
20569
20570fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Win32OpenGL_Present) {
20571 const fpl__Win32AppState *win32AppState = &appState->win32;
20572 const fpl__Win32WindowState *win32WindowState = &appState->window.win32;
20573 const fpl__Win32Api *wapi = &win32AppState->winApi;
20574 const fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
20575 wapi->gdi.SwapBuffers(win32WindowState->deviceContext);
20576}
20577
20578fpl_internal fpl__VideoContext fpl__VideoBackend_Win32OpenGL_Construct(void) {
20579 fpl__VideoContext result = fpl__StubVideoContext();
20580 result.loadFunc = fpl__VideoBackend_Win32OpenGL_Load;
20581 result.unloadFunc = fpl__VideoBackend_Win32OpenGL_Unload;
20582 result.getProcedureFunc = fpl__VideoBackend_Win32OpenGL_GetProcedure;
20583 result.initializeFunc = fpl__VideoBackend_Win32OpenGL_Initialize;
20584 result.shutdownFunc = fpl__VideoBackend_Win32OpenGL_Shutdown;
20585 result.prepareWindowFunc = fpl__VideoBackend_Win32OpenGL_PrepareWindow;
20586 result.finalizeWindowFunc = fpl__VideoBackend_Win32OpenGL_FinalizeWindow;
20587 result.presentFunc = fpl__VideoBackend_Win32OpenGL_Present;
20588 return(result);
20589}
20590#endif // FPL__ENABLE_VIDEO_OPENGL && FPL_PLATFORM_WINDOWS
20591
20592// ############################################################################
20593//
20594// > VIDEO_BACKEND_OPENGL_X11
20595//
20596// ############################################################################
20597#if defined(FPL__ENABLE_VIDEO_OPENGL) && defined(FPL_SUBPLATFORM_X11)
20598#ifndef __gl_h_
20599typedef uint8_t GLubyte;
20600#endif
20601#ifndef GLX_H
20602typedef XID GLXDrawable;
20603typedef XID GLXWindow;
20604typedef void GLXContext_Void;
20605typedef GLXContext_Void *GLXContext;
20606typedef void GLXFBConfig_Void;
20607typedef GLXFBConfig_Void *GLXFBConfig;
20608
20609#define GLX_RGBA 4
20610#define GLX_DOUBLEBUFFER 5
20611#define GLX_RED_SIZE 8
20612#define GLX_GREEN_SIZE 9
20613#define GLX_BLUE_SIZE 10
20614#define GLX_ALPHA_SIZE 11
20615#define GLX_DEPTH_SIZE 12
20616#define GLX_STENCIL_SIZE 13
20617#define GLX_SAMPLE_BUFFERS 0x186a0
20618#define GLX_SAMPLES 0x186a1
20619
20620#define GLX_X_VISUAL_TYPE 0x22
20621#define GLX_TRUE_COLOR 0x8002
20622#define GLX_RGBA_TYPE 0x8014
20623#endif
20624
20625#define FPL__FUNC_GLX_glXQueryVersion(name) Bool name(Display *dpy, int *major, int *minor)
20626typedef FPL__FUNC_GLX_glXQueryVersion(fpl__func_glx_glXQueryVersion);
20627#define FPL__FUNC_GLX_glXChooseVisual(name) XVisualInfo* name(Display *dpy, int screen, int *attribList)
20628typedef FPL__FUNC_GLX_glXChooseVisual(fpl__func_glx_glXChooseVisual);
20629#define FPL__FUNC_GLX_glXCreateContext(name) GLXContext name(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)
20630typedef FPL__FUNC_GLX_glXCreateContext(fpl__func_glx_glXCreateContext);
20631#define FPL__FUNC_GLX_glXCreateNewContext(name) GLXContext name(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)
20632typedef FPL__FUNC_GLX_glXCreateNewContext(fpl__func_glx_glXCreateNewContext);
20633#define FPL__FUNC_GLX_glXDestroyContext(name) void name(Display *dpy, GLXContext ctx)
20634typedef FPL__FUNC_GLX_glXDestroyContext(fpl__func_glx_glXDestroyContext);
20635#define FPL__FUNC_GLX_glXMakeCurrent(name) Bool name(Display *dpy, GLXDrawable drawable, GLXContext ctx)
20636typedef FPL__FUNC_GLX_glXMakeCurrent(fpl__func_glx_glXMakeCurrent);
20637#define FPL__FUNC_GLX_glXSwapBuffers(name) void name(Display *dpy, GLXDrawable drawable)
20638typedef FPL__FUNC_GLX_glXSwapBuffers(fpl__func_glx_glXSwapBuffers);
20639#define FPL__FUNC_GLX_glXGetProcAddress(name) void *name(const GLubyte *procName)
20640typedef FPL__FUNC_GLX_glXGetProcAddress(fpl__func_glx_glXGetProcAddress);
20641#define FPL__FUNC_GLX_glXChooseFBConfig(name) GLXFBConfig *name(Display *dpy, int screen, const int *attrib_list, int *nelements)
20642typedef FPL__FUNC_GLX_glXChooseFBConfig(fpl__func_glx_glXChooseFBConfig);
20643#define FPL__FUNC_GLX_glXGetFBConfigs(name) GLXFBConfig *name(Display *dpy, int screen, int *nelements)
20644typedef FPL__FUNC_GLX_glXGetFBConfigs(fpl__func_glx_glXGetFBConfigs);
20645#define FPL__FUNC_GLX_glXGetVisualFromFBConfig(name) XVisualInfo *name(Display *dpy, GLXFBConfig config)
20646typedef FPL__FUNC_GLX_glXGetVisualFromFBConfig(fpl__func_glx_glXGetVisualFromFBConfig);
20647#define FPL__FUNC_GLX_glXGetFBConfigAttrib(name) int name(Display *dpy, GLXFBConfig config, int attribute, int *value)
20648typedef FPL__FUNC_GLX_glXGetFBConfigAttrib(fpl__func_glx_glXGetFBConfigAttrib);
20649#define FPL__FUNC_GLX_glXCreateWindow(name) GLXWindow name(Display *dpy, GLXFBConfig config, Window win, const int *attrib_list)
20650typedef FPL__FUNC_GLX_glXCreateWindow(fpl__func_glx_glXCreateWindow);
20651#define FPL__FUNC_GLX_glXQueryExtension(name) Bool name(Display *dpy, int *errorBase, int *eventBase)
20652typedef FPL__FUNC_GLX_glXQueryExtension(fpl__func_glx_glXQueryExtension);
20653#define FPL__FUNC_GLX_glXQueryExtensionsString(name) const char *name(Display *dpy, int screen)
20654typedef FPL__FUNC_GLX_glXQueryExtensionsString(fpl__func_glx_glXQueryExtensionsString);
20655
20656// Modern GLX
20657#define FPL__FUNC_GLX_glXCreateContextAttribsARB(name) GLXContext name(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)
20658typedef FPL__FUNC_GLX_glXCreateContextAttribsARB(fpl__func_glx_glXCreateContextAttribsARB);
20659
20660#define FPL__GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
20661#define FPL__GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
20662#define FPL__GLX_CONTEXT_FLAGS_ARB 0x2094
20663#define FPL__GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
20664
20665#define FPL__GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
20666#define FPL__GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
20667#define FPL__GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
20668#define FPL__GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
20669
20670typedef struct fpl__X11VideoOpenGLApi {
20671 void *libHandle;
20672 fpl__func_glx_glXQueryVersion *glXQueryVersion;
20673 fpl__func_glx_glXChooseVisual *glXChooseVisual;
20674 fpl__func_glx_glXCreateContext *glXCreateContext;
20675 fpl__func_glx_glXDestroyContext *glXDestroyContext;
20676 fpl__func_glx_glXCreateNewContext *glXCreateNewContext;
20677 fpl__func_glx_glXMakeCurrent *glXMakeCurrent;
20678 fpl__func_glx_glXSwapBuffers *glXSwapBuffers;
20679 fpl__func_glx_glXGetProcAddress *glXGetProcAddress;
20680 fpl__func_glx_glXChooseFBConfig *glXChooseFBConfig;
20681 fpl__func_glx_glXGetFBConfigs *glXGetFBConfigs;
20682 fpl__func_glx_glXGetVisualFromFBConfig *glXGetVisualFromFBConfig;
20683 fpl__func_glx_glXGetFBConfigAttrib *glXGetFBConfigAttrib;
20684 fpl__func_glx_glXCreateWindow *glXCreateWindow;
20685 fpl__func_glx_glXQueryExtension *glXQueryExtension;
20686 fpl__func_glx_glXQueryExtensionsString *glXQueryExtensionsString;
20687 fpl__func_glx_glXCreateContextAttribsARB *glXCreateContextAttribsARB;
20688} fpl__X11VideoOpenGLApi;
20689
20690fpl_internal void fpl__UnloadX11OpenGLApi(fpl__X11VideoOpenGLApi *api) {
20691 if (api->libHandle != fpl_null) {
20692 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Unload Api (Library '%p')", api->libHandle);
20693 dlclose(api->libHandle);
20694 }
20695 fplClearStruct(api);
20696}
20697
20698fpl_internal bool fpl__LoadX11OpenGLApi(fpl__X11VideoOpenGLApi *api, const char *libraryName) {
20699 uint32_t libFileCount = 0;
20700
20701 const char *libFileNames[4];
20702 if (fplGetStringLength(libraryName) > 0) {
20703 libFileNames[libFileCount++] = libraryName;
20704 } else {
20705 libFileNames[libFileCount++] = "libGL.so.1";
20706 libFileNames[libFileCount++] = "libGL.so";
20707 }
20708
20709 bool result = false;
20710 for (uint32_t index = 0; index < libFileCount; ++index) {
20711 const char *libName = libFileNames[index];
20712 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Load GLX Api from Library: %s", libName);
20713 do {
20714 void *libHandle = fpl_null;
20715 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_GLX, libHandle, libName);
20716 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXQueryVersion, glXQueryVersion);
20717 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXChooseVisual, glXChooseVisual);
20718 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXCreateContext, glXCreateContext);
20719 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXDestroyContext, glXDestroyContext);
20720 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXCreateNewContext, glXCreateNewContext);
20721 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXMakeCurrent, glXMakeCurrent);
20722 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXSwapBuffers, glXSwapBuffers);
20723 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetProcAddress, glXGetProcAddress);
20724 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXChooseFBConfig, glXChooseFBConfig);
20725 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetFBConfigs, glXGetFBConfigs);
20726 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetVisualFromFBConfig, glXGetVisualFromFBConfig);
20727 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetFBConfigAttrib, glXGetFBConfigAttrib);
20728 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXCreateWindow, glXCreateWindow);
20729 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXQueryExtension, glXQueryExtension);
20730 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXQueryExtensionsString, glXQueryExtensionsString);
20731 api->libHandle = libHandle;
20732 result = true;
20733 } while (0);
20734 if (result) {
20735 FPL_LOG_DEBUG(FPL__MODULE_GLX, , "Successfully loaded GLX Api from Library '%s'", libName);
20736 break;
20737 }
20738 fpl__UnloadX11OpenGLApi(api);
20739 }
20740 return (result);
20741}
20742
20743typedef struct fpl__VideoBackendX11OpenGL {
20744 fpl__VideoBackend base;
20745 fpl__X11VideoOpenGLApi api;
20746 GLXFBConfig fbConfig;
20747 XVisualInfo *visualInfo;
20748 GLXContext context;
20749 bool isActiveContext;
20750} fpl__VideoBackendX11OpenGL;
20751
20752fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_X11OpenGL_GetProcedure) {
20753 const fpl__VideoBackendX11OpenGL *nativeBackend = (const fpl__VideoBackendX11OpenGL *)backend;
20754 void *result = dlsym(nativeBackend->api.libHandle, procName);
20755 return(result);
20756}
20757
20758fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_X11OpenGL_PrepareWindow) {
20759 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
20760 const fpl__X11Api *x11Api = &nativeAppState->api;
20761
20762 fpl__X11WindowState *nativeWindowState = &windowState->x11;
20763 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
20764 fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
20765
20766 Display *display = nativeWindowState->display;
20767 Window window = nativeWindowState->window;
20768 int screen = nativeWindowState->screen;
20769
20770 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Query GLX version for display '%p'", display);
20771 int major = 0, minor = 0;
20772 if (!glApi->glXQueryVersion(display, &major, &minor)) {
20773 FPL_LOG_ERROR(FPL__MODULE_GLX, "Failed querying GLX version for display '%p'", display);
20774 return false;
20775 }
20776 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully queried GLX version for display '%p': %d.%d", display, major, minor);
20777
20778 // @NOTE(final): Required for AMD Drivers?
20779
20780 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Query OpenGL extension on display '%p'", display);
20781 if (!glApi->glXQueryExtension(display, fpl_null, fpl_null)) {
20782 FPL__ERROR(FPL__MODULE_GLX, "OpenGL GLX Extension is not supported by the active display '%p'", display);
20783 return false;
20784 }
20785
20786 const char *extensionString = glApi->glXQueryExtensionsString(display, screen);
20787 if (extensionString != fpl_null) {
20788 FPL_LOG_DEBUG(FPL__MODULE_GLX, "OpenGL GLX extensions: %s", extensionString);
20789 }
20790
20791 bool isModern = major > 1 || (major == 1 && minor >= 3);
20792
20793 int attr[32] = fplZeroInit;
20794 int attrIndex = 0;
20795
20796 attr[attrIndex++] = GLX_X_VISUAL_TYPE;
20797 attr[attrIndex++] = GLX_TRUE_COLOR;
20798
20799 if (!isModern) {
20800 attr[attrIndex++] = GLX_RGBA;
20801 attr[attrIndex++] = True;
20802 }
20803
20804 attr[attrIndex++] = GLX_DOUBLEBUFFER;
20805 attr[attrIndex++] = True;
20806
20807 attr[attrIndex++] = GLX_RED_SIZE;
20808 attr[attrIndex++] = 8;
20809
20810 attr[attrIndex++] = GLX_GREEN_SIZE;
20811 attr[attrIndex++] = 8;
20812
20813 attr[attrIndex++] = GLX_BLUE_SIZE;
20814 attr[attrIndex++] = 8;
20815
20816 attr[attrIndex++] = GLX_ALPHA_SIZE;
20817 attr[attrIndex++] = 8;
20818
20819 attr[attrIndex++] = GLX_DEPTH_SIZE;
20820 attr[attrIndex++] = 24;
20821
20822 attr[attrIndex++] = GLX_STENCIL_SIZE;
20823 attr[attrIndex++] = 8;
20824
20825 if (videoSettings->graphics.opengl.multiSamplingCount > 0) {
20826 attr[attrIndex++] = GLX_SAMPLE_BUFFERS;
20827 attr[attrIndex++] = 1;
20828
20829 attr[attrIndex++] = GLX_SAMPLES;
20830 attr[attrIndex++] = (int)videoSettings->graphics.opengl.multiSamplingCount;
20831 }
20832
20833 attr[attrIndex] = 0;
20834
20835 if (isModern) {
20836 // Use frame buffer config approach (GLX >= 1.3)
20837 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Get framebuffer configuration from display '%p' and screen '%d'", display, screen);
20838 int configCount = 0;
20839 GLXFBConfig *configs = glApi->glXChooseFBConfig(display, screen, attr, &configCount);
20840 if (configs == fpl_null || !configCount) {
20841 FPL__ERROR(FPL__MODULE_GLX, "No framebuffer configuration from display '%p' and screen '%d' found!", display, screen);
20842 nativeBackend->fbConfig = fpl_null;
20843 return false;
20844 }
20845 nativeBackend->fbConfig = configs[0];
20846 nativeBackend->visualInfo = fpl_null;
20847 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully got framebuffer configuration from display '%p' and screen '%d': %p", display, screen, nativeBackend->fbConfig);
20848
20849 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Release %d framebuffer configurations", configCount);
20850 x11Api->XFree(configs);
20851 } else {
20852 // Use choose visual (Old way)
20853 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Choose visual from display '%p' and screen '%d'", display, screen);
20854 XVisualInfo *visualInfo = glApi->glXChooseVisual(display, screen, attr);
20855 if (visualInfo == fpl_null) {
20856 FPL__ERROR(FPL__MODULE_GLX, "No visual info for display '%p' and screen '%d' found!", display, screen);
20857 return false;
20858 }
20859 nativeBackend->visualInfo = visualInfo;
20860 nativeBackend->fbConfig = fpl_null;
20861 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully got visual info from display '%p' and screen '%d': %p", display, screen, nativeBackend->visualInfo);
20862 }
20863
20864 if (nativeBackend->fbConfig != fpl_null) {
20865 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Get visual info from display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
20866 XVisualInfo *visualInfo = glApi->glXGetVisualFromFBConfig(display, nativeBackend->fbConfig);
20867 if (visualInfo == fpl_null) {
20868 FPL__ERROR(FPL__MODULE_GLX, "Failed getting visual info from display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
20869 return false;
20870 }
20871 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully got visual info from display '%p' and frame buffer config '%p': %p", display, nativeBackend->fbConfig, visualInfo);
20872
20873 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using visual: %p", visualInfo->visual);
20874 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using color depth: %d", visualInfo->depth);
20875
20876 nativeWindowState->visual = visualInfo->visual;
20877 nativeWindowState->colorDepth = visualInfo->depth;
20878
20879 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Release visual info '%p'", visualInfo);
20880 x11Api->XFree(visualInfo);
20881 } else if (nativeBackend->visualInfo != fpl_null) {
20882 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using existing visual info: %p", nativeBackend->visualInfo);
20883 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using visual: %p", nativeBackend->visualInfo->visual);
20884 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using color depth: %d", nativeBackend->visualInfo->depth);
20885 nativeWindowState->visual = nativeBackend->visualInfo->visual;
20886 nativeWindowState->colorDepth = nativeBackend->visualInfo->depth;
20887 } else {
20888 FPL__ERROR(FPL__MODULE_GLX, "No visual info or frame buffer config defined!");
20889 return false;
20890 }
20891
20892 return true;
20893}
20894
20895fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_X11OpenGL_Shutdown) {
20896 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
20897 const fpl__X11Api *x11Api = &nativeAppState->api;
20898 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
20899
20900 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
20901 fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
20902
20903 if (nativeBackend->isActiveContext) {
20904 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Deactivate GLX rendering context for display '%p'", nativeWindowState->display);
20905 glApi->glXMakeCurrent(nativeWindowState->display, 0, fpl_null);
20906 nativeBackend->isActiveContext = false;
20907 }
20908
20909 if (nativeBackend->context != fpl_null) {
20910 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Destroy GLX rendering context '%p' for display '%p'", nativeBackend->context, nativeWindowState->display);
20911 glApi->glXDestroyContext(nativeWindowState->display, nativeBackend->context);
20912 nativeBackend->context = fpl_null;
20913 }
20914
20915 if (nativeBackend->visualInfo != fpl_null) {
20916 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Destroy visual info '%p' (Fallback)", nativeBackend->visualInfo);
20917 x11Api->XFree(nativeBackend->visualInfo);
20918 nativeBackend->visualInfo = fpl_null;
20919 }
20920}
20921
20922fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_X11OpenGL_Initialize) {
20923 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
20924 const fpl__X11Api *x11Api = &nativeAppState->api;
20925 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
20926
20927 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
20928 fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
20929
20930 Display *display = nativeWindowState->display;
20931 Window window = nativeWindowState->window;
20932
20933 //
20934 // Create legacy context
20935 //
20936 GLXContext legacyRenderingContext;
20937 if (nativeBackend->fbConfig != fpl_null) {
20938 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Create GLX legacy rendering context on display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
20939 legacyRenderingContext = glApi->glXCreateNewContext(display, nativeBackend->fbConfig, GLX_RGBA_TYPE, fpl_null, 1);
20940 if (!legacyRenderingContext) {
20941 FPL__ERROR(FPL__MODULE_GLX, "Failed creating GLX legacy rendering context on display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
20942 goto failed_x11_glx;
20943 }
20944 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);
20945 } else if (nativeBackend->visualInfo != fpl_null) {
20946 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Create GLX legacy rendering context on display '%p' and visual info '%p'", display, nativeBackend->visualInfo);
20947 legacyRenderingContext = glApi->glXCreateContext(display, nativeBackend->visualInfo, fpl_null, 1);
20948 if (!legacyRenderingContext) {
20949 FPL__ERROR(FPL__MODULE_GLX, "Failed creating GLX legacy rendering context on display '%p' and visual info '%p'", display, nativeBackend->visualInfo);
20950 goto failed_x11_glx;
20951 }
20952 } else {
20953 FPL__ERROR(FPL__MODULE_GLX, "No visual info or frame buffer config defined!");
20954 goto failed_x11_glx;
20955 }
20956
20957 //
20958 // Activate legacy context
20959 //
20960 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Activate GLX legacy rendering context '%p' on display '%p' and window '%d'", legacyRenderingContext, display, (int)window);
20961 if (!glApi->glXMakeCurrent(display, window, legacyRenderingContext)) {
20962 FPL__ERROR(FPL__MODULE_GLX, "Failed activating GLX legacy rendering context '%p' on display '%p' and window '%d'", legacyRenderingContext, display, (int)window);
20963 goto failed_x11_glx;
20964 } else {
20965 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully activated GLX legacy rendering context '%p' on display '%p' and window '%d'", legacyRenderingContext, display, (int)window);
20966 }
20967
20968 //
20969 // Load extensions
20970 //
20971 glApi->glXCreateContextAttribsARB = (fpl__func_glx_glXCreateContextAttribsARB *)glApi->glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
20972
20973 // Disable legacy rendering context
20974 glApi->glXMakeCurrent(display, 0, fpl_null);
20975
20976 GLXContext activeRenderingContext;
20977
20978 if ((videoSettings->graphics.opengl.compabilityFlags != fplOpenGLCompabilityFlags_Legacy) && (nativeBackend->fbConfig != fpl_null)) {
20979 // @NOTE(final): This is only available in OpenGL 3.0+
20980 if (!(videoSettings->graphics.opengl.majorVersion >= 3 && videoSettings->graphics.opengl.minorVersion >= 0)) {
20981 FPL__ERROR(FPL__MODULE_GLX, "You have not specified the 'majorVersion' and 'minorVersion' in the VideoSettings");
20982 goto failed_x11_glx;
20983 }
20984
20985 if (glApi->glXCreateContextAttribsARB == fpl_null) {
20986 FPL__ERROR(FPL__MODULE_GLX, "glXCreateContextAttribsARB is not available, modern OpenGL is not available for your video card");
20987 goto failed_x11_glx;
20988 }
20989
20990 int flags = 0;
20991 int profile = 0;
20993 profile = FPL__GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
20995 profile = FPL__GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
20996 } else {
20997 FPL__ERROR(FPL__MODULE_GLX, "No opengl compability profile selected, please specific Core OpenGLCompabilityFlags_Core or OpenGLCompabilityFlags_Compability");
20998 goto failed_x11_glx;
20999 }
21001 flags = FPL__GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
21002 }
21003
21004 int contextAttribIndex = 0;
21005 int contextAttribList[32] = fplZeroInit;
21006 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_MAJOR_VERSION_ARB;
21007 contextAttribList[contextAttribIndex++] = videoSettings->graphics.opengl.majorVersion;
21008 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_MINOR_VERSION_ARB;
21009 contextAttribList[contextAttribIndex++] = videoSettings->graphics.opengl.minorVersion;
21010 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_PROFILE_MASK_ARB;
21011 contextAttribList[contextAttribIndex++] = profile;
21012 if (flags > 0) {
21013 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_FLAGS_ARB;
21014 contextAttribList[contextAttribIndex++] = flags;
21015 }
21016 contextAttribList[contextAttribIndex] = 0;
21017
21018 GLXContext modernRenderingContext = glApi->glXCreateContextAttribsARB(display, nativeBackend->fbConfig, fpl_null, True, contextAttribList);
21019 if (!modernRenderingContext) {
21020 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);
21021
21022 // Fallback to legacy rendering context
21023 glApi->glXMakeCurrent(display, window, legacyRenderingContext);
21024 activeRenderingContext = legacyRenderingContext;
21025 } else {
21026 if (!glApi->glXMakeCurrent(display, window, modernRenderingContext)) {
21027 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);
21028
21029 // Destroy modern rendering context
21030 glApi->glXDestroyContext(display, modernRenderingContext);
21031
21032 // Fallback to legacy rendering context
21033 glApi->glXMakeCurrent(display, window, legacyRenderingContext);
21034 activeRenderingContext = legacyRenderingContext;
21035 } else {
21036 // Destroy legacy rendering context
21037 glApi->glXDestroyContext(display, legacyRenderingContext);
21038 legacyRenderingContext = fpl_null;
21039 activeRenderingContext = modernRenderingContext;
21040 }
21041 }
21042 } else {
21043 // Caller wants legacy context
21044 glApi->glXMakeCurrent(display, window, legacyRenderingContext);
21045 activeRenderingContext = legacyRenderingContext;
21046 }
21047
21048 bool result;
21049
21050 fplAssert(activeRenderingContext != fpl_null);
21051 nativeBackend->context = activeRenderingContext;
21052 nativeBackend->isActiveContext = true;
21053
21054 backend->surface.window.x11.display = display;
21055 backend->surface.window.x11.window = window;
21056 backend->surface.window.x11.visual = nativeWindowState->visual;
21057 backend->surface.window.x11.screen = nativeWindowState->screen;
21058 backend->surface.opengl.renderingContext = (void *)activeRenderingContext;
21059
21060 result = true;
21061
21062 goto done_x11_glx;
21063
21064failed_x11_glx:
21065 result = false;
21066
21067done_x11_glx:
21068 if (nativeBackend->visualInfo != fpl_null) {
21069 // If there is a cached visual info, get rid of it now - regardless of its result
21070 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Destroy visual info '%p'", nativeBackend->visualInfo);
21071 x11Api->XFree(nativeBackend->visualInfo);
21072 nativeBackend->visualInfo = fpl_null;
21073 }
21074
21075 if (!result) {
21076 if (legacyRenderingContext) {
21077 glApi->glXDestroyContext(display, legacyRenderingContext);
21078 }
21079 fpl__VideoBackend_X11OpenGL_Shutdown(appState, windowState, backend);
21080 }
21081
21082 return (result);
21083}
21084
21085fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_X11OpenGL_Unload) {
21086 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
21087 fpl__UnloadX11OpenGLApi(&nativeBackend->api);
21088 fplClearStruct(nativeBackend);
21089}
21090
21091fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_X11OpenGL_Load) {
21092 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
21093 const fplVideoSettings *videoSettings = &appState->currentSettings.video;
21094 fplClearStruct(nativeBackend);
21095 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
21096 if (!fpl__LoadX11OpenGLApi(&nativeBackend->api, videoSettings->graphics.opengl.libraryFile)) {
21097 return(false);
21098 }
21099 return(true);
21100}
21101
21102fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_X11OpenGL_Present) {
21103 const fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
21104 const fpl__X11WindowState *x11WinState = &appState->window.x11;
21105 const fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
21106 glApi->glXSwapBuffers(x11WinState->display, x11WinState->window);
21107}
21108
21109fpl_internal fpl__VideoContext fpl__VideoBackend_X11OpenGL_Construct(void) {
21110 fpl__VideoContext result = fpl__StubVideoContext();
21111 result.loadFunc = fpl__VideoBackend_X11OpenGL_Load;
21112 result.unloadFunc = fpl__VideoBackend_X11OpenGL_Unload;
21113 result.getProcedureFunc = fpl__VideoBackend_X11OpenGL_GetProcedure;
21114 result.initializeFunc = fpl__VideoBackend_X11OpenGL_Initialize;
21115 result.shutdownFunc = fpl__VideoBackend_X11OpenGL_Shutdown;
21116 result.prepareWindowFunc = fpl__VideoBackend_X11OpenGL_PrepareWindow;
21117 result.presentFunc = fpl__VideoBackend_X11OpenGL_Present;
21118 return(result);
21119}
21120#endif // FPL__ENABLE_VIDEO_OPENGL && FPL_SUBPLATFORM_X11
21121
21122// ############################################################################
21123//
21124// > VIDEO_BACKEND_SOFTWARE_X11
21125//
21126// ############################################################################
21127#if defined(FPL__ENABLE_VIDEO_SOFTWARE) && defined(FPL_SUBPLATFORM_X11)
21128typedef struct fpl__VideoBackendX11Software {
21129 fpl__VideoBackend base;
21130 GC graphicsContext;
21131 XImage *buffer;
21132} fpl__VideoBackendX11Software;
21133
21134fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_X11Software_Shutdown) {
21135 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
21136 const fpl__X11Api *x11Api = &nativeAppState->api;
21137 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
21138
21139 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
21140
21141 if (nativeBackend->buffer != fpl_null) {
21142 // @NOTE(final): Dont use XDestroyImage here, as it points to the backbuffer memory directly - which is released later
21143 nativeBackend->buffer = fpl_null;
21144 }
21145
21146 if (nativeBackend->graphicsContext != fpl_null) {
21147 nativeBackend->graphicsContext = fpl_null;
21148 }
21149}
21150
21151fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_X11Software_Initialize) {
21152 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
21153 const fpl__X11Api *x11Api = &nativeAppState->api;
21154 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
21155
21156 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
21157
21158 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
21159
21160 // Based on: https://bbs.archlinux.org/viewtopic.php?id=225741
21161 nativeBackend->graphicsContext = x11Api->XCreateGC(nativeWindowState->display, nativeWindowState->window, 0, 0);
21162 if (nativeBackend->graphicsContext == fpl_null) {
21163 return false;
21164 }
21165
21166 nativeBackend->buffer = x11Api->XCreateImage(nativeWindowState->display, nativeWindowState->visual, 24, ZPixmap, 0, (char *)backbuffer->pixels, backbuffer->width, backbuffer->height, 32, (int)backbuffer->lineWidth);
21167 if (nativeBackend->buffer == fpl_null) {
21168 fpl__VideoBackend_X11Software_Shutdown(appState, windowState, backend);
21169 return false;
21170 }
21171
21172 // Initial draw pixels to the window
21173 x11Api->XPutImage(nativeWindowState->display, nativeWindowState->window, nativeBackend->graphicsContext, nativeBackend->buffer, 0, 0, 0, 0, backbuffer->width, backbuffer->height);
21174 x11Api->XSync(nativeWindowState->display, False);
21175
21176 backend->surface.window.x11.display = nativeWindowState->display;
21177 backend->surface.window.x11.window = nativeWindowState->window;
21178 backend->surface.window.x11.visual = nativeWindowState->visual;
21179 backend->surface.window.x11.screen = nativeWindowState->screen;
21180
21181 return (true);
21182}
21183
21184fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_X11Software_Load) {
21185 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
21186 fplClearStruct(nativeBackend);
21187 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
21188 return(true);
21189}
21190
21191fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_X11Software_Unload) {
21192 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
21193 fplClearStruct(nativeBackend);
21194}
21195
21196fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_X11Software_Present) {
21197 const fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
21198 const fpl__X11WindowState *x11WinState = &appState->window.x11;
21199 const fpl__X11Api *x11Api = &appState->x11.api;
21200 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
21201 x11Api->XPutImage(x11WinState->display, x11WinState->window, nativeBackend->graphicsContext, nativeBackend->buffer, 0, 0, 0, 0, backbuffer->width, backbuffer->height);
21202 x11Api->XSync(x11WinState->display, False);
21203}
21204
21205fpl_internal fpl__VideoContext fpl__VideoBackend_X11Software_Construct(void) {
21206 fpl__VideoContext result = fpl__StubVideoContext();
21207 result.loadFunc = fpl__VideoBackend_X11Software_Load;
21208 result.unloadFunc = fpl__VideoBackend_X11Software_Unload;
21209 result.initializeFunc = fpl__VideoBackend_X11Software_Initialize;
21210 result.shutdownFunc = fpl__VideoBackend_X11Software_Shutdown;
21211 result.presentFunc = fpl__VideoBackend_X11Software_Present;
21212 result.recreateOnResize = true;
21213 return(result);
21214}
21215#endif // FPL__ENABLE_VIDEO_SOFTWARE && FPL_SUBPLATFORM_X11
21216
21217// ############################################################################
21218//
21219// > VIDEO_BACKEND_SOFTWARE_WIN32
21220//
21221// ############################################################################
21222#if defined(FPL__ENABLE_VIDEO_SOFTWARE) && defined(FPL_PLATFORM_WINDOWS)
21223typedef struct fpl__VideoBackendWin32Software {
21224 fpl__VideoBackend base;
21225 BITMAPINFO bitmapInfo;
21226} fpl__VideoBackendWin32Software;
21227
21228fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Win32Software_Shutdown) {
21229 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
21230 BITMAPINFO *bitmapInfo = &nativeBackend->bitmapInfo;
21231 fplClearStruct(bitmapInfo);
21232}
21233
21234fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Win32Software_Initialize) {
21235 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
21236 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
21237 BITMAPINFO *bitmapInfo = &nativeBackend->bitmapInfo;
21238 fplClearStruct(bitmapInfo);
21239 bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
21240 bitmapInfo->bmiHeader.biWidth = (LONG)backbuffer->width;
21241 // @NOTE(final): Top-down bitmap requires a negative height
21242 bitmapInfo->bmiHeader.biHeight = -(LONG)backbuffer->height;
21243 bitmapInfo->bmiHeader.biBitCount = 32;
21244 bitmapInfo->bmiHeader.biCompression = BI_RGB;
21245 bitmapInfo->bmiHeader.biPlanes = 1;
21246 bitmapInfo->bmiHeader.biSizeImage = (DWORD)(backbuffer->height * backbuffer->lineWidth);
21247 return true;
21248}
21249
21250fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Win32Software_Unload) {
21251 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
21252 fplClearStruct(nativeBackend);
21253}
21254
21255fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Win32Software_Load) {
21256 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
21257 fplClearStruct(nativeBackend);
21258 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
21259 return(true);
21260}
21261
21262fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Win32Software_Present) {
21263 const fpl__Win32AppState *win32AppState = &appState->win32;
21264 const fpl__Win32WindowState *win32WindowState = &appState->window.win32;
21265 const fpl__Win32Api *wapi = &win32AppState->winApi;
21266 const fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
21267 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
21268 fplWindowSize area;
21269 if (fplGetWindowSize(&area)) {
21270 int32_t targetX = 0;
21271 int32_t targetY = 0;
21272 int32_t targetWidth = area.width;
21273 int32_t targetHeight = area.height;
21274 int32_t sourceWidth = backbuffer->width;
21275 int32_t sourceHeight = backbuffer->height;
21276 if (backbuffer->useOutputRect) {
21277 targetX = backbuffer->outputRect.x;
21278 targetY = backbuffer->outputRect.y;
21279 targetWidth = backbuffer->outputRect.width;
21280 targetHeight = backbuffer->outputRect.height;
21281 wapi->gdi.StretchDIBits(win32WindowState->deviceContext, 0, 0, area.width, area.height, 0, 0, 0, 0, fpl_null, fpl_null, DIB_RGB_COLORS, BLACKNESS);
21282 }
21283 wapi->gdi.StretchDIBits(win32WindowState->deviceContext, targetX, targetY, targetWidth, targetHeight, 0, 0, sourceWidth, sourceHeight, backbuffer->pixels, &nativeBackend->bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
21284 }
21285}
21286
21287fpl_internal fpl__VideoContext fpl__VideoBackend_Win32Software_Construct(void) {
21288 fpl__VideoContext result = fpl__StubVideoContext();
21289 result.loadFunc = fpl__VideoBackend_Win32Software_Load;
21290 result.unloadFunc = fpl__VideoBackend_Win32Software_Unload;
21291 result.initializeFunc = fpl__VideoBackend_Win32Software_Initialize;
21292 result.shutdownFunc = fpl__VideoBackend_Win32Software_Shutdown;
21293 result.presentFunc = fpl__VideoBackend_Win32Software_Present;
21294 result.recreateOnResize = true;
21295 return(result);
21296}
21297#endif // FPL__ENABLE_VIDEO_SOFTWARE && FPL_PLATFORM_WINDOWS
21298
21299// ############################################################################
21300//
21301// > VIDEO_BACKEND_VULKAN (Win32, X11)
21302//
21303// ############################################################################
21304#if defined(FPL__ENABLE_VIDEO_VULKAN)
21305
21306#if !fplHasInclude(<vulkan/vulkan.h>) || defined(FPL_NO_PLATFORM_INCLUDES)
21307
21308#if defined(FPL_PLATFORM_WINDOWS)
21309# define fpl__VKAPI_CALL __stdcall
21310# define fpl__VKAPI_PTR fpl__VKAPI_CALL
21311# define fpl__VKAPI_ATTR
21312#else
21313# define fpl__VKAPI_CALL
21314# define fpl__VKAPI_PTR fpl__VKAPI_CALL
21315# define fpl__VKAPI_ATTR
21316#endif
21317
21318#define FPL__VK_NULL_HANDLE fpl_null
21319
21320#define FPL__VK_MAX_EXTENSION_NAME_SIZE 256
21321#define FPL__VK_MAX_DESCRIPTION_SIZE 256
21322
21323#define FPL__VK_MAKE_VERSION(major, minor, patch) \
21324 ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch)))
21325
21326typedef enum fpl__VkResult {
21327 FPL__VK_ERROR_OUT_OF_HOST_MEMORY = -1,
21328 FPL__VK_ERROR_OUT_OF_DEVICE_MEMORY = -2,
21329 FPL__VK_ERROR_INITIALIZATION_FAILED = -3,
21330 FPL__VK_ERROR_DEVICE_LOST = -4,
21331 FPL__VK_ERROR_MEMORY_MAP_FAILED = -5,
21332 FPL__VK_ERROR_LAYER_NOT_PRESENT = -6,
21333 FPL__VK_ERROR_EXTENSION_NOT_PRESENT = -7,
21334 FPL__VK_ERROR_FEATURE_NOT_PRESENT = -8,
21335 FPL__VK_ERROR_INCOMPATIBLE_DRIVER = -9,
21336 FPL__VK_ERROR_TOO_MANY_OBJECTS = -10,
21337 FPL__VK_ERROR_FORMAT_NOT_SUPPORTED = -11,
21338 FPL__VK_ERROR_FRAGMENTED_POOL = -12,
21339 FPL__VK_ERROR_UNKNOWN = -13,
21340
21341 FPL__VK_SUCCESS = 0,
21342
21343 FPL__VK_NOT_READY = 1,
21344 FPL__VK_TIMEOUT = 2,
21345 FPL__VK_EVENT_SET = 3,
21346 FPL__VK_EVENT_RESET = 4,
21347 FPL__VK_INCOMPLETE = 5,
21348
21349 FPL__VK_RESULT_MAX_ENUM = 0x7FFFFFFF
21350} fpl__VkResult;
21351
21352typedef uint32_t fpl__VkFlags;
21353typedef uint32_t fpl__VkBool32;
21354
21355typedef void fpl__VkAllocationCallbacks;
21356
21357typedef void *fpl__VkInstance;
21358typedef void *fpl__VkSurfaceKHR;
21359typedef void *fpl__VkPhysicalDevice;
21360
21361typedef enum fpl__VkStructureType {
21362 FPL__VK_STRUCTURE_TYPE_APPLICATION_INFO = 0,
21363 FPL__VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1,
21364 FPL__VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000,
21365 FPL__VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
21366 FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT = 1000128002,
21367 FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004,
21368 FPL__VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
21369} fpl__VkStructureType;
21370
21371typedef struct fpl__VkApplicationInfo {
21372 fpl__VkStructureType sType;
21373 const void *pNext;
21374 const char *pApplicationName;
21375 uint32_t applicationVersion;
21376 const char *pEngineName;
21377 uint32_t engineVersion;
21378 uint32_t apiVersion;
21379} fpl__VkApplicationInfo;
21380
21381typedef fpl__VkFlags fpl__VkInstanceCreateFlags;
21382
21383typedef struct fpl__VkInstanceCreateInfo {
21384 fpl__VkStructureType sType;
21385 const void *pNext;
21386 fpl__VkInstanceCreateFlags flags;
21387 const fpl__VkApplicationInfo *pApplicationInfo;
21388 uint32_t enabledLayerCount;
21389 const char *const *ppEnabledLayerNames;
21390 uint32_t enabledExtensionCount;
21391 const char *const *ppEnabledExtensionNames;
21392} fpl__VkInstanceCreateInfo;
21393
21394typedef struct fpl__VkExtensionProperties {
21395 char extensionName[FPL__VK_MAX_EXTENSION_NAME_SIZE];
21396 uint32_t specVersion;
21397} fpl__VkExtensionProperties;
21398
21399typedef struct fpl__VkLayerProperties {
21400 char layerName[FPL__VK_MAX_EXTENSION_NAME_SIZE];
21401 uint32_t specVersion;
21402 uint32_t implementationVersion;
21403 char description[FPL__VK_MAX_DESCRIPTION_SIZE];
21404} fpl__VkLayerProperties;
21405
21406// Core procs (opaque)
21407typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateInstance)(const fpl__VkInstanceCreateInfo *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkInstance *pInstance);
21408typedef void (fpl__VKAPI_PTR *fpl__func_vkDestroyInstance)(fpl__VkInstance instance, const fpl__VkAllocationCallbacks *pAllocator);
21409typedef void *(fpl__VKAPI_PTR *fpl__func_vkGetInstanceProcAddr)(fpl__VkInstance instance, const char *pName);
21410typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkEnumerateInstanceExtensionProperties)(const char *pLayerName, uint32_t *pPropertyCount, fpl__VkExtensionProperties *pProperties);
21411typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkEnumerateInstanceLayerProperties)(uint32_t *pPropertyCount, fpl__VkLayerProperties *pProperties);
21412
21413// Surface KHR procs (opaque)
21414typedef void (fpl__VKAPI_PTR *fpl__func_vkDestroySurfaceKHR)(fpl__VkInstance instance, fpl__VkSurfaceKHR surface, const fpl__VkAllocationCallbacks *pAllocator);
21415
21416#if defined(FPL_PLATFORM_WINDOWS)
21417
21418typedef fpl__VkFlags fpl__VkWin32SurfaceCreateFlagsKHR;
21419typedef struct fpl__VkWin32SurfaceCreateInfoKHR {
21420 fpl__VkStructureType sType;
21421 const void *pNext;
21422 fpl__VkWin32SurfaceCreateFlagsKHR flags;
21423 fpl__Win32InstanceHandle hinstance;
21424 fpl__Win32WindowHandle hwnd;
21425} fpl__VkWin32SurfaceCreateInfoKHR;
21426
21427typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateWin32SurfaceKHR)(fpl__VkInstance instance, const fpl__VkWin32SurfaceCreateInfoKHR *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkSurfaceKHR *pSurface);
21428typedef fpl__VkBool32(fpl__VKAPI_PTR *fpl__func_vkGetPhysicalDeviceWin32PresentationSupportKHR)(fpl__VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
21429
21430#elif defined(FPL_SUBPLATFORM_X11)
21431
21432typedef fpl__VkFlags fpl__VkXlibSurfaceCreateFlagsKHR;
21433typedef struct fpl__VkXlibSurfaceCreateInfoKHR {
21434 fpl__VkStructureType sType;
21435 const void *pNext;
21436 fpl__VkXlibSurfaceCreateFlagsKHR flags;
21437 fpl__X11Display *dpy;
21438 fpl__X11Window window;
21439} fpl__VkXlibSurfaceCreateInfoKHR;
21440
21441typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateXlibSurfaceKHR)(fpl__VkInstance instance, const fpl__VkXlibSurfaceCreateInfoKHR *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkSurfaceKHR *pSurface);
21442typedef fpl__VkBool32(fpl__VKAPI_PTR *fpl__func_vkGetPhysicalDeviceXlibPresentationSupportKHR)(fpl__VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
21443
21444#endif
21445
21446typedef void *fpl__VkDebugUtilsMessengerEXT;
21447
21448typedef enum fpl__VkDebugUtilsMessageSeverityFlagBitsEXT {
21449 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 0x00000001,
21450 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT = 0x00000010,
21451 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 0x00000100,
21452 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 0x00001000,
21453 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
21454} fpl__VkDebugUtilsMessageSeverityFlagBitsEXT;
21455typedef fpl__VkFlags fpl__VkDebugUtilsMessageSeverityFlagsEXT;
21456
21457typedef enum fpl__VkDebugUtilsMessageTypeFlagBitsEXT {
21458 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 0x00000001,
21459 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT = 0x00000002,
21460 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 0x00000004,
21461 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
21462} fpl__VkDebugUtilsMessageTypeFlagBitsEXT;
21463typedef fpl__VkFlags fpl__VkDebugUtilsMessageTypeFlagsEXT;
21464
21465typedef struct fpl__VkDebugUtilsLabelEXT {
21466 fpl__VkStructureType sType;
21467 const void *pNext;
21468 const char *pLabelName;
21469 float color[4];
21470} fpl__VkDebugUtilsLabelEXT;
21471
21472typedef void fpl__VkDebugUtilsObjectNameInfoEXT;
21473typedef fpl__VkFlags fpl__VkDebugUtilsMessengerCallbackDataFlagsEXT;
21474typedef struct fpl__VkDebugUtilsMessengerCallbackDataEXT {
21475 fpl__VkStructureType sType;
21476 const void *pNext;
21477 fpl__VkDebugUtilsMessengerCallbackDataFlagsEXT flags;
21478 const char *pMessageIdName;
21479 int32_t messageIdNumber;
21480 const char *pMessage;
21481 uint32_t queueLabelCount;
21482 const fpl__VkDebugUtilsLabelEXT *pQueueLabels;
21483 uint32_t cmdBufLabelCount;
21484 const fpl__VkDebugUtilsLabelEXT *pCmdBufLabels;
21485 uint32_t objectCount;
21486 const fpl__VkDebugUtilsObjectNameInfoEXT *pObjects;
21487} fpl__VkDebugUtilsMessengerCallbackDataEXT;
21488
21489typedef fpl__VkFlags fpl__VkDebugUtilsMessengerCreateFlagsEXT;
21490
21491typedef fpl__VkBool32(fpl__VKAPI_PTR *fpl__func_vkDebugUtilsMessengerCallbackEXT) (
21492 fpl__VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
21493 fpl__VkDebugUtilsMessageTypeFlagsEXT messageTypes,
21494 const fpl__VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
21495 void *pUserData);
21496
21497typedef struct fpl__VkDebugUtilsMessengerCreateInfoEXT {
21498 fpl__VkStructureType sType;
21499 const void *pNext;
21500 fpl__VkDebugUtilsMessengerCreateFlagsEXT flags;
21501 fpl__VkDebugUtilsMessageSeverityFlagsEXT messageSeverity;
21502 fpl__VkDebugUtilsMessageTypeFlagsEXT messageType;
21503 fpl__func_vkDebugUtilsMessengerCallbackEXT pfnUserCallback;
21504 void *pUserData;
21505} fpl__VkDebugUtilsMessengerCreateInfoEXT;
21506
21507typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateDebugUtilsMessengerEXT)(fpl__VkInstance instance, const fpl__VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkDebugUtilsMessengerEXT *pMessenger);
21508typedef void (fpl__VKAPI_PTR *fpl__func_vkDestroyDebugUtilsMessengerEXT)(fpl__VkInstance instance, fpl__VkDebugUtilsMessengerEXT messenger, const fpl__VkAllocationCallbacks *pAllocator);
21509
21510#else
21511
21512# if defined(FPL_PLATFORM_WINDOWS)
21513# define VK_USE_PLATFORM_WIN32_KHR
21514# elif defined(FPL_SUBPLATFORM_X11)
21515# define VK_USE_PLATFORM_XLIB_KHR
21516# endif
21517
21518# if !defined(FPL_NO_RUNTIME_LINKING)
21519# define VK_NO_PROTOTYPES
21520# endif
21521# include <vulkan/vulkan.h>
21522
21523# define fpl__VKAPI_CALL VKAPI_CALL
21524# define fpl__VKAPI_PTR VKAPI_PTR
21525# define fpl__VKAPI_ATTR VKAPI_ATTR
21526
21527# define FPL__VK_STRUCTURE_TYPE_APPLICATION_INFO VK_STRUCTURE_TYPE_APPLICATION_INFO
21528# define FPL__VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
21529# define FPL__VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR
21530# define FPL__VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR
21531# define FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT
21532
21533typedef enum fpl__VkResult {
21534 FPL__VK_ERROR_OUT_OF_HOST_MEMORY = VK_ERROR_OUT_OF_HOST_MEMORY,
21535 FPL__VK_ERROR_OUT_OF_DEVICE_MEMORY = VK_ERROR_OUT_OF_DEVICE_MEMORY,
21536 FPL__VK_ERROR_INITIALIZATION_FAILED = VK_ERROR_INITIALIZATION_FAILED,
21537 FPL__VK_ERROR_DEVICE_LOST = VK_ERROR_DEVICE_LOST,
21538 FPL__VK_ERROR_MEMORY_MAP_FAILED = VK_ERROR_MEMORY_MAP_FAILED,
21539 FPL__VK_ERROR_LAYER_NOT_PRESENT = VK_ERROR_LAYER_NOT_PRESENT,
21540 FPL__VK_ERROR_EXTENSION_NOT_PRESENT = VK_ERROR_EXTENSION_NOT_PRESENT,
21541 FPL__VK_ERROR_FEATURE_NOT_PRESENT = VK_ERROR_FEATURE_NOT_PRESENT,
21542 FPL__VK_ERROR_INCOMPATIBLE_DRIVER = VK_ERROR_INCOMPATIBLE_DRIVER,
21543 FPL__VK_ERROR_TOO_MANY_OBJECTS = VK_ERROR_TOO_MANY_OBJECTS,
21544 FPL__VK_ERROR_FORMAT_NOT_SUPPORTED = VK_ERROR_FORMAT_NOT_SUPPORTED,
21545 FPL__VK_ERROR_FRAGMENTED_POOL = VK_ERROR_FRAGMENTED_POOL,
21546 FPL__VK_ERROR_UNKNOWN = VK_ERROR_UNKNOWN,
21547
21548 FPL__VK_SUCCESS = VK_SUCCESS,
21549
21550 FPL__VK_NOT_READY = VK_NOT_READY,
21551 FPL__VK_TIMEOUT = VK_TIMEOUT,
21552 FPL__VK_EVENT_SET = VK_EVENT_SET,
21553 FPL__VK_EVENT_RESET = VK_EVENT_RESET,
21554 FPL__VK_INCOMPLETE = VK_INCOMPLETE,
21555
21556 FPL__VK_RESULT_MAX_ENUM = VK_RESULT_MAX_ENUM
21557} fpl__VkResult;
21558
21559# define FPL__VK_NULL_HANDLE VK_NULL_HANDLE
21560
21561# define FPL__VK_MAKE_VERSION(major, minor, patch) VK_MAKE_VERSION(major, minor, patch)
21562
21563typedef VkFlags fpl__VkFlags;
21564typedef VkBool32 fpl__VkBool32;
21565
21566typedef VkAllocationCallbacks fpl__VkAllocationCallbacks;
21567
21568typedef VkInstance fpl__VkInstance;
21569typedef VkSurfaceKHR fpl__VkSurfaceKHR;
21570typedef VkPhysicalDevice fpl__VkPhysicalDevice;
21571
21572typedef VkApplicationInfo fpl__VkApplicationInfo;
21573typedef VkInstanceCreateInfo fpl__VkInstanceCreateInfo;
21574
21575typedef VkExtensionProperties fpl__VkExtensionProperties;
21576typedef VkLayerProperties fpl__VkLayerProperties;
21577
21578// Core procs
21579typedef PFN_vkCreateInstance fpl__func_vkCreateInstance;
21580typedef PFN_vkDestroyInstance fpl__func_vkDestroyInstance;
21581typedef PFN_vkGetInstanceProcAddr fpl__func_vkGetInstanceProcAddr;
21582typedef PFN_vkEnumerateInstanceExtensionProperties fpl__func_vkEnumerateInstanceExtensionProperties;
21583typedef PFN_vkEnumerateInstanceLayerProperties fpl__func_vkEnumerateInstanceLayerProperties;
21584
21585// Instance procs
21586typedef PFN_vkDestroySurfaceKHR fpl__func_vkDestroySurfaceKHR;
21587#if defined(FPL_PLATFORM_WINDOWS)
21588typedef VkWin32SurfaceCreateInfoKHR fpl__VkWin32SurfaceCreateInfoKHR;
21589typedef PFN_vkCreateWin32SurfaceKHR fpl__func_vkCreateWin32SurfaceKHR;
21590typedef PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR fpl__func_vkGetPhysicalDeviceWin32PresentationSupportKHR;
21591#elif defined(FPL_SUBPLATFORM_X11)
21592typedef VkXlibSurfaceCreateInfoKHR fpl__VkXlibSurfaceCreateInfoKHR;
21593typedef PFN_vkCreateXlibSurfaceKHR fpl__func_vkCreateXlibSurfaceKHR;
21594typedef PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR fpl__func_vkGetPhysicalDeviceXlibPresentationSupportKHR;
21595#endif
21596
21597typedef VkDebugUtilsMessageSeverityFlagBitsEXT fpl__VkDebugUtilsMessageSeverityFlagBitsEXT;
21598typedef VkDebugUtilsMessageSeverityFlagsEXT fpl__VkDebugUtilsMessageSeverityFlagsEXT;
21599# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
21600# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
21601# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
21602# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
21603# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT
21604typedef VkDebugUtilsMessageTypeFlagBitsEXT fpl__VkDebugUtilsMessageTypeFlagBitsEXT;
21605typedef VkDebugUtilsMessageTypeFlagsEXT fpl__VkDebugUtilsMessageTypeFlagsEXT;
21606# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
21607# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
21608# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
21609# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT
21610typedef VkDebugUtilsMessengerCallbackDataEXT fpl__VkDebugUtilsMessengerCallbackDataEXT;
21611typedef VkDebugUtilsMessengerEXT fpl__VkDebugUtilsMessengerEXT;
21612typedef PFN_vkDebugUtilsMessengerCallbackEXT fpl__func_vkDebugUtilsMessengerCallbackEXT;
21613typedef VkDebugUtilsMessengerCreateInfoEXT fpl__VkDebugUtilsMessengerCreateInfoEXT;
21614
21615typedef PFN_vkCreateDebugUtilsMessengerEXT fpl__func_vkCreateDebugUtilsMessengerEXT;
21616typedef PFN_vkDestroyDebugUtilsMessengerEXT fpl__func_vkDestroyDebugUtilsMessengerEXT;
21617
21618#endif // !fplHasInclude(<vulkan/vulkan.h>) || defined(FPL_NO_PLATFORM_INCLUDES)
21619
21620typedef struct fpl__VulkanApi {
21621 fplDynamicLibraryHandle libraryHandle;
21622 fpl__func_vkCreateInstance vkCreateInstance;
21623 fpl__func_vkDestroyInstance vkDestroyInstance;
21624 fpl__func_vkGetInstanceProcAddr vkGetInstanceProcAddr;
21625 fpl__func_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
21626 fpl__func_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
21627} fpl__VulkanApi;
21628
21629fpl_internal void fpl__UnloadVulkanApi(fpl__VulkanApi *api) {
21630 if (api->libraryHandle.isValid) {
21631 fplDynamicLibraryUnload(&api->libraryHandle);
21632 }
21633 fplClearStruct(api);
21634}
21635
21636fpl_internal bool fpl__LoadVulkanApi(fpl__VulkanApi *api, const char *libraryName) {
21637
21638 fplClearStruct(api);
21639
21640#if defined(FPL_NO_RUNTIME_LINKING)
21641 api->vkCreateInstance = vkCreateInstance;
21642 api->vkDestroyInstance = vkDestroyInstance;
21643 api->vkGetInstanceProcAddr = vkGetInstanceProcAddr;
21644 api->vkEnumerateInstanceExtensionProperties = vkEnumerateInstanceExtensionProperties;
21645 api->vkEnumerateInstanceLayerProperties = vkEnumerateInstanceLayerProperties;
21646 return(true);
21647#endif
21648
21649 uint32_t libraryCount = 0;
21650
21651 const char *libraryNames[4];
21652 if (fplGetStringLength(libraryName) > 0) {
21653 libraryNames[libraryCount++] = libraryName;
21654 } else {
21655 // Automatic detection of vulkan library
21656#if defined(FPL_PLATFORM_WINDOWS)
21657 libraryNames[libraryCount++] = "vulkan-1.dll";
21658#elif defined(FPL_SUBPLATFORM_POSIX)
21659 libraryNames[libraryCount++] = "libvulkan.so";
21660 libraryNames[libraryCount++] = "libvulkan.so.1";
21661#else
21662 FPL__WARNING(FPL__MODULE_VIDEO_VULKAN, "Unsupported Platform!");
21663 return(false);
21664#endif
21665 }
21666
21667#define FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libName, target, type, name) \
21668 (target)->name = (type)fplGetDynamicLibraryProc(&libHandle, #name); \
21669 if ((target)->name == fpl_null) { \
21670 FPL__WARNING(FPL__MODULE_VIDEO_VULKAN, "Failed getting procedure address '%s' from library '%s'", #name, libName); \
21671 continue; \
21672 }
21673
21674 bool result = false;
21675 for (uint32_t i = 0; i < libraryCount; ++i) {
21676 const char *libraryName = libraryNames[i];
21677
21678 if (api->libraryHandle.isValid) {
21679 fplDynamicLibraryUnload(&api->libraryHandle);
21680 }
21681 fplClearStruct(api);
21682
21684 if (!fplDynamicLibraryLoad(libraryName, &libHandle)) {
21685 continue;
21686 }
21687 api->libraryHandle = libHandle;
21688
21689 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkCreateInstance, vkCreateInstance);
21690 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkDestroyInstance, vkDestroyInstance);
21691 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkGetInstanceProcAddr, vkGetInstanceProcAddr);
21692 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkEnumerateInstanceExtensionProperties, vkEnumerateInstanceExtensionProperties);
21693 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkEnumerateInstanceLayerProperties, vkEnumerateInstanceLayerProperties);
21694
21695 result = true;
21696 break;
21697 }
21698
21699 if (!result) {
21700 fpl__UnloadVulkanApi(api);
21701 }
21702
21703#undef FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE
21704
21705 return(result);
21706}
21707
21708typedef struct fpl__VulkanDebugMessengerUserData {
21710 fplVulkanValidationLayerMode validationMode;
21711 void *userData;
21712} fpl__VulkanDebugMessengerUserData;
21713
21714typedef struct fpl__VideoBackendVulkan {
21715 fpl__VideoBackend base;
21716 fpl__VulkanApi api;
21717 fpl__VkInstance instanceHandle;
21718 fpl__VkSurfaceKHR surfaceHandle;
21719 fpl__VkDebugUtilsMessengerEXT debugMessenger;
21720 fpl__VulkanDebugMessengerUserData debugMessengerUserData;
21721 const fpl__VkAllocationCallbacks *allocator;
21722 fpl_b32 isInstanceUserDefined;
21723} fpl__VideoBackendVulkan;
21724
21725fpl_internal const char *fpl__GetVulkanResultString(const fpl__VkResult result) {
21726 switch (result) {
21727 case FPL__VK_ERROR_OUT_OF_HOST_MEMORY:
21728 return "Out of Host-Memory";
21729 case FPL__VK_ERROR_OUT_OF_DEVICE_MEMORY:
21730 return "Out of Device-Memory";
21731 case FPL__VK_ERROR_INITIALIZATION_FAILED:
21732 return "Initialization failed";
21733 case FPL__VK_ERROR_DEVICE_LOST:
21734 return "Device lost";
21735 case FPL__VK_ERROR_MEMORY_MAP_FAILED:
21736 return "Memory map failed";
21737 case FPL__VK_ERROR_LAYER_NOT_PRESENT:
21738 return "Layer not present";
21739 case FPL__VK_ERROR_EXTENSION_NOT_PRESENT:
21740 return "Extension not present";
21741 case FPL__VK_ERROR_FEATURE_NOT_PRESENT:
21742 return "Feature not present";
21743 case FPL__VK_ERROR_INCOMPATIBLE_DRIVER:
21744 return "Incompatible driver";
21745 case FPL__VK_ERROR_TOO_MANY_OBJECTS:
21746 return "Too many objects";
21747 case FPL__VK_ERROR_FORMAT_NOT_SUPPORTED:
21748 return "Format not supported";
21749 case FPL__VK_ERROR_FRAGMENTED_POOL:
21750 return "Fragmented pool";
21751 case FPL__VK_SUCCESS:
21752 return "Success";
21753 case FPL__VK_NOT_READY:
21754 return "Not-Ready";
21755 case FPL__VK_TIMEOUT:
21756 return "Timeout";
21757 case FPL__VK_EVENT_SET:
21758 return "Event-Set";
21759 case FPL__VK_EVENT_RESET:
21760 return "Event-Reset";
21761 case FPL__VK_INCOMPLETE:
21762 return "Incomplete";
21763 default:
21764 return "Unknown";
21765 }
21766}
21767
21768fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_Vulkan_GetProcedure) {
21769 const fpl__VideoBackendVulkan *nativeBackend = (const fpl__VideoBackendVulkan *)backend;
21770 const fpl__VulkanApi *api = &nativeBackend->api;
21771 if (api->libraryHandle.isValid) {
21772 return fplGetDynamicLibraryProc(&api->libraryHandle, procName);
21773 }
21774 return(fpl_null);
21775}
21776
21777fpl_internal FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(fpl__VideoBackend_Vulkan_GetRequirements) {
21778 FPL__CheckArgumentNull(requirements, false);
21779 fplClearStruct(requirements);
21780
21781 fplAssert(requirements->vulkan.instanceExtensionCount < fplArrayCount(requirements->vulkan.instanceExtensions));
21782 requirements->vulkan.instanceExtensions[requirements->vulkan.instanceExtensionCount++] = "VK_KHR_surface";
21783#if defined(FPL_PLATFORM_WINDOWS)
21784 fplAssert(requirements->vulkan.instanceExtensionCount < fplArrayCount(requirements->vulkan.instanceExtensions));
21785 requirements->vulkan.instanceExtensions[requirements->vulkan.instanceExtensionCount++] = "VK_KHR_win32_surface";
21786#elif defined(FPL_SUBPLATFORM_X11)
21787 fplAssert(requirements->vulkan.instanceExtensionCount < fplArrayCount(requirements->vulkan.instanceExtensions));
21788 requirements->vulkan.instanceExtensions[requirements->vulkan.instanceExtensionCount++] = "VK_KHR_xlib_surface";
21789#else
21790 return(false);
21791#endif
21792 return(true);
21793}
21794
21795fpl_internal uint32_t fpl__VersionInfoToVulkanVersion(const fplVersionInfo *versionInfo) {
21796 uint32_t major = fplStringToS32(versionInfo->version.parts.major);
21797 uint32_t minor = fplStringToS32(versionInfo->version.parts.minor);
21798 uint32_t patch = fplStringToS32(versionInfo->version.parts.fix);
21799 uint32_t result = FPL__VK_MAKE_VERSION(major, minor, patch);
21800 return(result);
21801}
21802
21803fpl_internal const char *fpl__GetVulkanMessageSeverityName(const fpl__VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity) {
21804 switch (messageSeverity) {
21805 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return "ERROR";
21806 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return "WARNING";
21807 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return "INFO";
21808 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return "VERBOSE";
21809 default: return "Unknown";
21810 }
21811}
21812
21813fpl_internal fpl__VKAPI_ATTR fpl__VkBool32 fpl__VKAPI_CALL fpl__VulkanDebugCallback(fpl__VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, fpl__VkDebugUtilsMessageTypeFlagsEXT messageType, const fpl__VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) {
21814 fpl__VulkanDebugMessengerUserData *data = (fpl__VulkanDebugMessengerUserData *)pUserData;
21815 const char *message = pCallbackData->pMessage;
21816 if (data->userCallback != fpl_null) {
21817 data->userCallback(data->userData, message, messageSeverity, messageType, pCallbackData);
21818 } else {
21819 if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
21820 FPL_LOG_ERROR(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
21821 else if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
21822 FPL_LOG_WARN(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
21823 else if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
21824 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
21825 else if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
21826 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
21827 else {
21828 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
21829 }
21830 }
21831 return 0;
21832}
21833
21834fpl_internal void fpl__VulkanDestroyDebugMessenger(fpl__VideoBackendVulkan *nativeBackend) {
21835 if (nativeBackend->debugMessenger != fpl_null) {
21836 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Destroy Vulkan Debug Messenger '%p'", nativeBackend->debugMessenger);
21837 const fpl__VulkanApi *api = &nativeBackend->api;
21838 fpl__func_vkDestroyDebugUtilsMessengerEXT destroyFunc = (fpl__func_vkDestroyDebugUtilsMessengerEXT)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkDestroyDebugUtilsMessengerEXT");
21839 fplAssert(destroyFunc != fpl_null);
21840 destroyFunc(nativeBackend->instanceHandle, nativeBackend->debugMessenger, nativeBackend->allocator);
21841 nativeBackend->debugMessenger = fpl_null;
21842 }
21843 fplClearStruct(&nativeBackend->debugMessengerUserData);
21844}
21845
21846fpl_internal bool fpl__VulkanCreateDebugMessenger(const fplVulkanSettings *settings, fpl__VideoBackendVulkan *nativeBackend) {
21847 fpl__VkDebugUtilsMessageSeverityFlagsEXT severities = 0;
21848 switch (settings->validationSeverity) {
21850 severities = FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
21851 break;
21853 severities = FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
21854 break;
21856 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;
21857 break;
21860 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;
21861 break;
21863 default:
21864 return(false);
21865 }
21866
21867 fpl__VulkanDebugMessengerUserData *userData = &nativeBackend->debugMessengerUserData;
21868 userData->userCallback = settings->validationLayerCallback;
21869 userData->userData = settings->userData;
21870 userData->validationMode = settings->validationLayerMode;
21871
21872 // @TODO(final): Message type filtering in fplVulkanSettings
21873 fpl__VkDebugUtilsMessageTypeFlagsEXT messageTypes =
21874 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
21875 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
21876 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
21877
21878 fpl__VkDebugUtilsMessengerCreateInfoEXT createInfo = fplZeroInit;
21879 createInfo.sType = FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
21880 createInfo.messageSeverity = severities;
21881 createInfo.messageType = messageTypes;
21882 createInfo.pfnUserCallback = fpl__VulkanDebugCallback;
21883 createInfo.pUserData = userData;
21884
21885 const fpl__VulkanApi *api = &nativeBackend->api;
21886
21887 fpl__func_vkCreateDebugUtilsMessengerEXT createFunc = (fpl__func_vkCreateDebugUtilsMessengerEXT)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkCreateDebugUtilsMessengerEXT");
21888 if (createFunc == fpl_null) {
21889 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Vulkan instance proc 'vkCreateDebugUtilsMessengerEXT' not found! Maybe the instance extension 'VK_EXT_debug_utils' was not set?");
21890 return(false);
21891 }
21892
21893 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Create Vulkan Debug Messenger for Instance '%p' with severity flags of '%lu'", nativeBackend->instanceHandle, severities);
21894 fpl__VkResult creationResult = (fpl__VkResult)createFunc(nativeBackend->instanceHandle, &createInfo, nativeBackend->allocator, &nativeBackend->debugMessenger);
21895 if (creationResult != FPL__VK_SUCCESS) {
21896 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);
21897 return(false);
21898 }
21899
21900 return(true);
21901}
21902
21903fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_Vulkan_PrepareWindow) {
21904 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
21905 if (videoSettings->graphics.vulkan.instanceHandle == fpl_null) {
21906
21907 const fpl__VulkanApi *api = &nativeBackend->api;
21908
21909 nativeBackend->allocator = fpl_null;
21910 nativeBackend->instanceHandle = fpl_null;
21911 nativeBackend->isInstanceUserDefined = false;
21912
21913 fplVideoRequirements requirements = fplZeroInit;
21914 if (!fpl__VideoBackend_Vulkan_GetRequirements(&requirements) || requirements.vulkan.instanceExtensionCount == 0) {
21915 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed getting required instance extensions for creating an Vulkan instance!");
21916 return(false);
21917 }
21918
21919 // @TODO(final): Validate vulkan video settings
21920
21921 //
21922 // Validate required instance extensions and layers
21923 //
21924 bool supportsDebugUtils = false;
21925
21926 fpl__VkExtensionProperties *supportedInstanceExtensions = fpl_null;
21927 uint32_t supportedInstanceExtensionCount = 0;
21928 api->vkEnumerateInstanceExtensionProperties(fpl_null, &supportedInstanceExtensionCount, fpl_null);
21929 if (supportedInstanceExtensionCount > 0) {
21930 supportedInstanceExtensions = (fpl__VkExtensionProperties *)fpl__AllocateTemporaryMemory(sizeof(fpl__VkExtensionProperties) * supportedInstanceExtensionCount, 16);
21931 api->vkEnumerateInstanceExtensionProperties(fpl_null, &supportedInstanceExtensionCount, supportedInstanceExtensions);
21932 for (uint32_t i = 0; i < supportedInstanceExtensionCount; ++i) {
21933 if (fplIsStringEqual(supportedInstanceExtensions[i].extensionName, "VK_EXT_debug_utils")) {
21934 supportsDebugUtils = true;
21935 }
21936 }
21937 fpl__ReleaseTemporaryMemory(supportedInstanceExtensions);
21938 supportedInstanceExtensions = fpl_null;
21939 }
21940
21941 bool supportsValidationLayer = false;
21942
21943 fpl__VkLayerProperties *supportedLayers = fpl_null;
21944 uint32_t supportedLayerCount = 0;
21945 api->vkEnumerateInstanceLayerProperties(&supportedLayerCount, fpl_null);
21946 if (supportedLayerCount > 0) {
21947 supportedLayers = (fpl__VkLayerProperties *)fpl__AllocateTemporaryMemory(sizeof(fpl__VkLayerProperties) * supportedLayerCount, 16);
21948 api->vkEnumerateInstanceLayerProperties(&supportedLayerCount, supportedLayers);
21949 for (uint32_t i = 0; i < supportedLayerCount; ++i) {
21950 if (fplIsStringEqual(supportedLayers[i].layerName, "VK_LAYER_KHRONOS_validation")) {
21951 supportsValidationLayer = true;
21952 }
21953 }
21954 fpl__ReleaseTemporaryMemory(supportedLayers);
21955 supportedLayers = fpl_null;
21956 }
21957
21958 const char *enabledValidationLayers[4] = fplZeroInit;
21959 const char *enabledInstanceExtensions[8] = fplZeroInit;
21960 uint32_t enabledValidationLayerCount = 0;
21961 uint32_t enabledInstanceExtensionCount = 0;
21964 if (!supportsDebugUtils) {
21965 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "VK_EXT_debug_utils instance extension is not supported!");
21966 return(false);
21967 }
21968 if (!supportsValidationLayer) {
21969 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "VK_LAYER_KHRONOS_validation instance layer is not supported!");
21970 return(false);
21971 }
21972 }
21973 if (supportsDebugUtils && supportsValidationLayer) {
21974 enabledValidationLayers[enabledValidationLayerCount++] = "VK_LAYER_KHRONOS_validation";
21975 enabledInstanceExtensions[enabledInstanceExtensionCount++] = "VK_EXT_debug_utils";
21976 }
21977 }
21978 for (uint32_t i = 0; i < requirements.vulkan.instanceExtensionCount; ++i) {
21979 fplAssert(enabledInstanceExtensionCount < fplArrayCount(enabledInstanceExtensions));
21980 enabledInstanceExtensions[enabledInstanceExtensionCount++] = requirements.vulkan.instanceExtensions[i];
21981 }
21982
21983 fpl__VkApplicationInfo applicationInfo = fplZeroInit;
21984 applicationInfo.sType = FPL__VK_STRUCTURE_TYPE_APPLICATION_INFO;
21985 applicationInfo.pApplicationName = videoSettings->graphics.vulkan.appName;
21986 applicationInfo.pEngineName = videoSettings->graphics.vulkan.engineName;
21987 applicationInfo.apiVersion = fpl__VersionInfoToVulkanVersion(&videoSettings->graphics.vulkan.apiVersion);
21988 applicationInfo.engineVersion = fpl__VersionInfoToVulkanVersion(&videoSettings->graphics.vulkan.engineVersion);
21989 applicationInfo.applicationVersion = fpl__VersionInfoToVulkanVersion(&videoSettings->graphics.vulkan.appVersion);
21990
21991 fpl__VkInstanceCreateInfo instanceCreateInfo = fplZeroInit;
21992 instanceCreateInfo.sType = FPL__VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
21993 instanceCreateInfo.pApplicationInfo = &applicationInfo;
21994 instanceCreateInfo.enabledExtensionCount = enabledInstanceExtensionCount;
21995 instanceCreateInfo.ppEnabledExtensionNames = enabledInstanceExtensions;
21996 instanceCreateInfo.enabledLayerCount = enabledValidationLayerCount;
21997 instanceCreateInfo.ppEnabledLayerNames = enabledValidationLayers;
21998
21999 const fpl__VkAllocationCallbacks *allocator = (const fpl__VkAllocationCallbacks *)videoSettings->graphics.vulkan.allocator;
22000
22001 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Create Vulkan Instance with %lu extensions and %lu layers", instanceCreateInfo.enabledExtensionCount, instanceCreateInfo.enabledLayerCount);
22002 fpl__VkInstance instance = fpl_null;
22003 fpl__VkResult creationResult = (fpl__VkResult)api->vkCreateInstance(&instanceCreateInfo, allocator, &instance);
22004 if (creationResult != FPL__VK_SUCCESS) {
22005 const char *creationError = fpl__GetVulkanResultString(creationResult);
22006 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);
22007 return(false);
22008 }
22009
22010 nativeBackend->allocator = allocator;
22011 nativeBackend->instanceHandle = instance;
22012 nativeBackend->isInstanceUserDefined = false;
22013
22014 // Debug utils
22016 if (!fpl__VulkanCreateDebugMessenger(&videoSettings->graphics.vulkan, nativeBackend)) {
22018 FPL__WARNING(FPL__MODULE_VIDEO_VULKAN, "The debug messenger could not be created, no validation message are printed");
22019 } else {
22020 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "The debug messenger could not be created, stop vulkan initialization!");
22021 return(false);
22022 }
22023 }
22024 }
22025
22026 return(true);
22027 } else {
22028 // Instance passed by user
22029 nativeBackend->allocator = (const fpl__VkAllocationCallbacks *)videoSettings->graphics.vulkan.allocator;
22030 nativeBackend->instanceHandle = (fpl__VkInstance)videoSettings->graphics.vulkan.instanceHandle;
22031 nativeBackend->isInstanceUserDefined = true;
22032 return(true);
22033 }
22034}
22035
22036fpl_internal FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__VideoBackend_Vulkan_FinalizeWindow) {
22037 return(true);
22038}
22039
22040fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Vulkan_Shutdown) {
22041 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
22042
22043 const fpl__VulkanApi *api = &nativeBackend->api;
22044
22045 if (nativeBackend->surfaceHandle != FPL__VK_NULL_HANDLE) {
22046 fpl__func_vkDestroySurfaceKHR destroyProc = (fpl__func_vkDestroySurfaceKHR)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkDestroySurfaceKHR");
22047 fplAssert(destroyProc != fpl_null);
22048
22049 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Destroy Vulkan Surface '%p'", nativeBackend->surfaceHandle);
22050 destroyProc(nativeBackend->instanceHandle, nativeBackend->surfaceHandle, nativeBackend->allocator);
22051
22052 nativeBackend->surfaceHandle = FPL__VK_NULL_HANDLE;
22053 }
22054}
22055
22056fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Vulkan_Initialize) {
22057 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
22058
22059 const fpl__VulkanApi *api = &nativeBackend->api;
22060
22061 if (nativeBackend->instanceHandle == FPL__VK_NULL_HANDLE) {
22062 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Cannot create a Vulkan surface without a Vulkan instance!");
22063 return(false);
22064 }
22065
22066 fpl__VkSurfaceKHR surfaceHandle = FPL__VK_NULL_HANDLE;
22067
22068#if defined(FPL_PLATFORM_WINDOWS)
22069 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Query Vulkan Instance Proc 'vkCreateWin32SurfaceKHR' for instance '%p'", nativeBackend->instanceHandle);
22070 fpl__func_vkCreateWin32SurfaceKHR createProc = (fpl__func_vkCreateWin32SurfaceKHR)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkCreateWin32SurfaceKHR");
22071 if (createProc == fpl_null) {
22072 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Vulkan instance proc 'vkCreateWin32SurfaceKHR' not found! Maybe the instance extension 'VK_KHR_win32_surface' was not set?");
22073 return(false);
22074 }
22075
22076 fpl__VkWin32SurfaceCreateInfoKHR creationInfo = fplZeroInit;
22077 creationInfo.sType = FPL__VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
22078 creationInfo.hinstance = fpl__global__InitState.win32.appInstance;
22079 creationInfo.hwnd = windowState->win32.windowHandle;
22080
22081 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);
22082 fpl__VkResult creationResult = (fpl__VkResult)createProc(nativeBackend->instanceHandle, &creationInfo, nativeBackend->allocator, &surfaceHandle);
22083 if (creationResult != FPL__VK_SUCCESS) {
22084 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed creating vulkan surface KHR for Win32 -> (VkResult: %d)!", creationResult);
22085 return(false);
22086 }
22087#elif defined(FPL_SUBPLATFORM_X11)
22088 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Query Vulkan Instance Proc 'vkCreateXlibSurfaceKHR' for instance '%p'", nativeBackend->instanceHandle);
22089 fpl__func_vkCreateXlibSurfaceKHR createProc = (fpl__func_vkCreateXlibSurfaceKHR)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkCreateXlibSurfaceKHR");
22090 if (createProc == fpl_null) {
22091 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Vulkan instance proc 'vkCreateXlibSurfaceKHR' not found! Maybe the instance extension 'VK_KHR_xlib_surface' was not set?");
22092 return(false);
22093 }
22094
22095 fpl__VkXlibSurfaceCreateInfoKHR creationInfo = fplZeroInit;
22096 creationInfo.sType = FPL__VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
22097 creationInfo.dpy = (fpl__X11Display *)windowState->x11.display;
22098 creationInfo.window = windowState->x11.window;
22099
22100 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);
22101 fpl__VkResult creationResult = createProc(nativeBackend->instanceHandle, &creationInfo, nativeBackend->allocator, &surfaceHandle);
22102 if (creationResult != FPL__VK_SUCCESS) {
22103 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed creating vulkan surface KHR for X11 -> (VkResult: %d)!", creationResult);
22104 return(false);
22105 }
22106#else
22107 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Unsupported Platform!");
22108 return(false);
22109#endif
22110
22111 fplAssert(surfaceHandle != FPL__VK_NULL_HANDLE);
22112 nativeBackend->surfaceHandle = surfaceHandle;
22113
22114 backend->surface.vulkan.instance = nativeBackend->instanceHandle;
22115 backend->surface.vulkan.surfaceKHR = (void *)nativeBackend->surfaceHandle;
22116
22117#if defined(FPL_PLATFORM_WINDOWS)
22118 backend->surface.window.win32.windowHandle = windowState->win32.windowHandle;
22119 backend->surface.window.win32.deviceContext = windowState->win32.deviceContext;
22120#elif defined(FPL_SUBPLATFORM_X11)
22121 backend->surface.window.x11.display = windowState->x11.display;
22122 backend->surface.window.x11.window = windowState->x11.window;
22123 backend->surface.window.x11.screen = windowState->x11.screen;
22124 backend->surface.window.x11.visual = windowState->x11.visual;
22125#endif
22126
22127 return(true);
22128}
22129
22130fpl_internal FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(fpl__VideoBackend_Vulkan_DestroyedWindow) {
22131 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
22132
22133 // Destroy Vulkan Debug Messenger
22134 if (!nativeBackend->isInstanceUserDefined && nativeBackend->instanceHandle != fpl_null && nativeBackend->debugMessenger != fpl_null) {
22135 fpl__VulkanDestroyDebugMessenger(nativeBackend);
22136 }
22137
22138 // Destroy Vulkan instance
22139 const fpl__VulkanApi *api = &nativeBackend->api;
22140 if (!nativeBackend->isInstanceUserDefined && nativeBackend->instanceHandle != fpl_null) {
22141 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Destroy Vulkan Instance '%p'", nativeBackend->instanceHandle);
22142 api->vkDestroyInstance(nativeBackend->instanceHandle, nativeBackend->allocator);
22143 nativeBackend->instanceHandle = fpl_null;
22144 }
22145}
22146
22147fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Vulkan_Unload) {
22148 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
22149
22150 // Unload core api
22151 fpl__UnloadVulkanApi(&nativeBackend->api);
22152
22153 // Clear everything
22154 fplClearStruct(nativeBackend);
22155}
22156
22157fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Vulkan_Load) {
22158 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
22159 const fplVideoSettings *videoSettings = &appState->currentSettings.video;
22160
22161 // Clear and set magic id
22162 fplClearStruct(nativeBackend);
22163 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
22164
22165 // Load core api
22166 if (!fpl__LoadVulkanApi(&nativeBackend->api, videoSettings->graphics.vulkan.libraryFile)) {
22167 return(false);
22168 }
22169
22170 return(true);
22171}
22172
22173fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Vulkan_Present) {
22174 const fpl__VideoBackendVulkan *nativeBackend = (const fpl__VideoBackendVulkan *)backend;
22175}
22176
22177fpl_internal fpl__VideoContext fpl__VideoBackend_Vulkan_Construct(void) {
22178 fpl__VideoContext result = fpl__StubVideoContext();
22179 result.loadFunc = fpl__VideoBackend_Vulkan_Load;
22180 result.unloadFunc = fpl__VideoBackend_Vulkan_Unload;
22181 result.getProcedureFunc = fpl__VideoBackend_Vulkan_GetProcedure;
22182 result.initializeFunc = fpl__VideoBackend_Vulkan_Initialize;
22183 result.shutdownFunc = fpl__VideoBackend_Vulkan_Shutdown;
22184 result.prepareWindowFunc = fpl__VideoBackend_Vulkan_PrepareWindow;
22185 result.finalizeWindowFunc = fpl__VideoBackend_Vulkan_FinalizeWindow;
22186 result.destroyedWindowFunc = fpl__VideoBackend_Vulkan_DestroyedWindow;
22187 result.presentFunc = fpl__VideoBackend_Vulkan_Present;
22188 result.getRequirementsFunc = fpl__VideoBackend_Vulkan_GetRequirements;
22189 return(result);
22190}
22191
22192#endif // FPL__ENABLE_VIDEO_VULKAN
22193
22194
22195#endif // FPL__VIDEO_BACKENDS_IMPLEMENTED
22196
22197// ****************************************************************************
22198//
22199// > AUDIO_BACKEND_API
22200//
22201// ****************************************************************************
22202#if !defined(FPL__AUDIO_BACKEND_API_IMPLEMENTED) && defined(FPL__ENABLE_AUDIO)
22203# define FPL__AUDIO_BACKEND_API_IMPLEMENTED
22204
22205struct fplAudioContext;
22206
22207struct fplAudioBackend;
22208
22209#define FPL_AUDIO_BACKEND_INITIALIZE_FUNC(name) fplAudioResultType name(struct fplAudioContext *context, struct fplAudioBackend *backend)
22216typedef FPL_AUDIO_BACKEND_INITIALIZE_FUNC(fpl_audio_backend_initialize_func);
22217
22218#define FPL_AUDIO_BACKEND_GET_AUDIO_DEVICES_FUNC(name) uint32_t name(struct fplAudioContext *context, struct fplAudioBackend *backend, const uint32_t maxDeviceCount, const uint32_t deviceInfoSize, fplAudioDeviceInfo *deviceInfos)
22219typedef FPL_AUDIO_BACKEND_GET_AUDIO_DEVICES_FUNC(fpl_audio_backend_get_audio_devices_func);
22220
22221#define FPL_AUDIO_BACKEND_GET_AUDIO_DEVICE_INFO_FUNC(name) fplAudioResultType name(struct fplAudioContext *context, struct fplAudioBackend *backend, const fplAudioDeviceID *targetDevice, fplAudioDeviceInfoExtended *outDeviceInfo)
22222typedef FPL_AUDIO_BACKEND_GET_AUDIO_DEVICE_INFO_FUNC(fpl_audio_backend_get_audio_device_info_func);
22223
22224#define FPL_AUDIO_BACKEND_RELEASE_FUNC(name) bool name(struct fplAudioContext *context, struct fplAudioBackend *backend)
22231typedef FPL_AUDIO_BACKEND_RELEASE_FUNC(fpl_audio_backend_release_func);
22232
22244#define FPL_AUDIO_BACKEND_INITIALIZE_DEVICE_FUNC(name) fplAudioResultType name(struct fplAudioContext *context, struct fplAudioBackend *backend, const fplSpecificAudioSettings *audioSettings, const fplAudioFormat *targetFormat, const fplAudioDeviceInfo *targetDevice, fplAudioFormat *outputFormat, fplAudioDeviceInfo *outputDevice, fplAudioChannelMap *outputChannelMap)
22245typedef FPL_AUDIO_BACKEND_INITIALIZE_DEVICE_FUNC(fpl_audio_backend_initialize_device_func);
22246
22247#define FPL_AUDIO_BACKEND_RELEASE_DEVICE_FUNC(name) bool name(struct fplAudioContext *context, struct fplAudioBackend *backend)
22254typedef FPL_AUDIO_BACKEND_RELEASE_DEVICE_FUNC(fpl_audio_backend_release_device_func);
22255
22256#define FPL_AUDIO_BACKEND_START_DEVICE_FUNC(name) fplAudioResultType name(struct fplAudioContext *context, struct fplAudioBackend *backend)
22257typedef FPL_AUDIO_BACKEND_START_DEVICE_FUNC(fpl_audio_backend_start_device_func);
22258
22259#define FPL_AUDIO_BACKEND_STOP_DEVICE_FUNC(name) bool name(struct fplAudioContext *context, struct fplAudioBackend *backend)
22260typedef FPL_AUDIO_BACKEND_STOP_DEVICE_FUNC(fpl_audio_backend_stop_device_func);
22261
22262#define FPL_AUDIO_BACKEND_MAIN_LOOP_FUNC(name) void name(struct fplAudioContext *context, struct fplAudioBackend *backend)
22263typedef FPL_AUDIO_BACKEND_MAIN_LOOP_FUNC(fpl_audio_backend_main_loop_func);
22264
22265#define FPL_AUDIO_BACKEND_STOP_MAIN_LOOP_FUNC(name) void name(struct fplAudioContext *context, struct fplAudioBackend *backend)
22266typedef FPL_AUDIO_BACKEND_STOP_MAIN_LOOP_FUNC(fpl_audio_backend_stop_main_loop_func);
22267
22269typedef struct fplAudioBackendFunctionTable {
22270 fpl_audio_backend_initialize_func *initialize;
22271 fpl_audio_backend_release_func *release;
22272 fpl_audio_backend_get_audio_devices_func *getAudioDevices;
22273 fpl_audio_backend_get_audio_device_info_func *getAudioDeviceInfo;
22274 fpl_audio_backend_initialize_device_func *initializeDevice;
22275 fpl_audio_backend_release_device_func *releaseDevice;
22276 fpl_audio_backend_start_device_func *startDevice;
22277 fpl_audio_backend_stop_device_func *stopDevice;
22278 fpl_audio_backend_main_loop_func *mainLoop;
22279 fpl_audio_backend_stop_main_loop_func *stopMainLoop;
22280 uintptr_t padding;
22281} fplAudioBackendFunctionTable;
22282
22284typedef struct fplAudioBackendID {
22285 uint32_t a;
22286 uint16_t b;
22287 uint16_t c;
22288 uint8_t d[8];
22289} fplAudioBackendID;
22290
22292typedef struct fplAudioBackendDescriptorIDName {
22294 fplAudioBackendID id;
22296 const char *name;
22297} fplAudioBackendDescriptorIDName;
22298
22299typedef struct fplAudioBackendDescriptorHeader {
22301 fplAudioBackendDescriptorIDName idName;
22305 uint32_t backendSize;
22307 bool isAsync;
22309 bool isValid;
22310} fplAudioBackendDescriptorHeader;
22311
22313typedef struct fplAudioBackendDescriptor {
22315 fplAudioBackendDescriptorHeader header;
22317 fplAudioBackendFunctionTable table;
22318} fplAudioBackendDescriptor;
22319
22320typedef struct fplAudioBackend {
22321 // Internal audio device
22322 fplAudioDeviceInfo internalDevice;
22323 // Internal audio format
22324 fplAudioFormat internalFormat;
22325 // User audio format
22326 fplAudioFormat desiredFormat;
22327 // Callback that is called from the user to retrieve audio frames/samples
22328 fpl_audio_client_read_callback *clientReadCallback;
22329 // User data that is passed to the user callback
22330 void *clientUserData;
22331} fplAudioBackend;
22332
22334#define FPL_AUDIO_BACKEND_DATA_PADDING 16
22335
22337#define FPL_AUDIO_BACKEND_DATA_OFFSET (sizeof(fplAudioBackend) + FPL_AUDIO_BACKEND_DATA_PADDING)
22338
22340#define FPL_GET_AUDIO_BACKEND_IMPL(backend, type) (type *)(((uint8_t *)(backend) + FPL_AUDIO_BACKEND_DATA_OFFSET))
22341
22342#endif // FPL__AUDIO_BACKEND_API_IMPLEMENTED
22343
22344// ****************************************************************************
22345//
22346// > AUDIO_BACKENDS
22347//
22348// ****************************************************************************
22349#if !defined(FPL__AUDIO_BACKENDS_IMPLEMENTED) && defined(FPL__ENABLE_AUDIO)
22350# define FPL__AUDIO_BACKENDS_IMPLEMENTED
22351
22352typedef enum fpl__AudioDeviceState {
22353 // Audio device is not initialized
22354 fpl__AudioDeviceState_Uninitialized = 0,
22355 // Audio device is stopped, but still initialized
22356 fpl__AudioDeviceState_Stopped,
22357 // Audio device is started, meaning that all the main-loop that requests samples are already running
22358 fpl__AudioDeviceState_Started,
22359 // Audio device is going to be started and the main-loop is going to be started as well
22360 fpl__AudioDeviceState_Starting,
22361 // Audio device is going to be stopped
22362 fpl__AudioDeviceState_Stopping,
22363} fpl__AudioDeviceState;
22364
22365typedef struct fplAudioContext {
22366 // Current audio device state
22367 volatile fpl__AudioDeviceState state;
22368} fplAudioContext;
22369
22370typedef struct fpl__CommonAudioState {
22371 // Audio function table
22372 fplAudioBackendFunctionTable funcTable;
22373 // Channels mapping table
22374 fplAudioChannelMap channelsMapping;
22375 // Reference to the active @ref fplAudioBackend, actual backend data starts directly after with an additional padding of @ref FPL_AUDIO_BACKEND_DATA_PADDING
22376 fplAudioBackend *backend;
22377 // Audio context
22378 fplAudioContext context;
22379} fpl__CommonAudioState;
22380
22381fpl_internal uint32_t fpl__ReadAudioFramesFromClient(const fplAudioBackend *backend, uint32_t frameCount, void *pSamples) {
22382 uint32_t framesRead = 0;
22383 if (backend->clientReadCallback != fpl_null) {
22384 framesRead = backend->clientReadCallback(&backend->internalFormat, frameCount, pSamples, backend->clientUserData);
22385 }
22386 uint32_t channels = backend->internalFormat.channels;
22387 uint32_t samplesRead = framesRead * channels;
22388 uint32_t sampleSize = fplGetAudioSampleSizeInBytes(backend->internalFormat.type);
22389 uint32_t consumedBytes = samplesRead * sampleSize;
22390 uint32_t remainingBytes = ((frameCount * channels) - samplesRead) * sampleSize;
22391 if (remainingBytes > 0) {
22392 fplMemoryClear((uint8_t *)pSamples + consumedBytes, remainingBytes);
22393 }
22394 return(samplesRead);
22395}
22396
22397// Global Audio GUIDs
22398#if defined(FPL_PLATFORM_WINDOWS)
22399fpl_globalvar const fpl__Win32Guid FPL__GUID_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
22400fpl_globalvar const fpl__Win32Guid FPL__GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
22401
22402// Sets the default channel for a win32 audio backend, such directsound, wasapi, etc.
22403fpl_internal void fpl__SetAudioDefaultChannelMapWin32(const uint16_t channels, const fplAudioChannelLayout layout, fplAudioChannelMap *outChannelMap) {
22404 fplClearStruct(outChannelMap);
22405
22406 if (channels == 0 || layout == fplAudioChannelLayout_Unsupported) {
22407 return;
22408 }
22409
22410 if (channels == 1 || layout == fplAudioChannelLayout_Mono) {
22411 outChannelMap->speakers[0] = fplAudioChannelType_FrontCenter;
22412 } else if (channels == 2 || layout == fplAudioChannelLayout_Stereo) {
22413 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22414 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22415 } else if (channels == 3) {
22416 if (layout == fplAudioChannelLayout_2_1) {
22417 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22418 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22419 outChannelMap->speakers[2] = fplAudioChannelType_LowFrequency;
22420 } else {
22421 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22422 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22423 outChannelMap->speakers[2] = fplAudioChannelType_FrontCenter;
22424 }
22425 } else if (channels == 4) {
22426 if (layout == fplAudioChannelLayout_4_0_Surround) {
22427 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22428 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22429 outChannelMap->speakers[2] = fplAudioChannelType_FrontCenter;
22430 outChannelMap->speakers[3] = fplAudioChannelType_BackCenter;
22431 } else {
22432 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22433 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22434 outChannelMap->speakers[2] = fplAudioChannelType_BackLeft;
22435 outChannelMap->speakers[3] = fplAudioChannelType_BackRight;
22436 }
22437 } else if (channels == 5) {
22438 if (layout == fplAudioChannelLayout_4_1) {
22439 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22440 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22441 outChannelMap->speakers[2] = fplAudioChannelType_LowFrequency;
22442 outChannelMap->speakers[3] = fplAudioChannelType_SideLeft;
22443 outChannelMap->speakers[4] = fplAudioChannelType_SideRight;
22444 } else {
22445 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22446 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22447 outChannelMap->speakers[2] = fplAudioChannelType_FrontCenter;
22448 outChannelMap->speakers[3] = fplAudioChannelType_BackLeft;
22449 outChannelMap->speakers[4] = fplAudioChannelType_BackRight;
22450 }
22451 } else if (channels == 6) {
22452 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22453 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22454 outChannelMap->speakers[2] = fplAudioChannelType_FrontCenter;
22455 outChannelMap->speakers[3] = fplAudioChannelType_LowFrequency;
22456 outChannelMap->speakers[4] = fplAudioChannelType_SideLeft;
22457 outChannelMap->speakers[5] = fplAudioChannelType_SideRight;
22458 } else if (channels == 7) {
22459 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22460 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22461 outChannelMap->speakers[2] = fplAudioChannelType_FrontCenter;
22462 outChannelMap->speakers[3] = fplAudioChannelType_LowFrequency;
22463 outChannelMap->speakers[4] = fplAudioChannelType_BackCenter;
22464 outChannelMap->speakers[5] = fplAudioChannelType_SideLeft;
22465 outChannelMap->speakers[6] = fplAudioChannelType_SideRight;
22466 } else {
22467 fplAssert(channels >= 8);
22468 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22469 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
22470 outChannelMap->speakers[2] = fplAudioChannelType_FrontCenter;
22471 outChannelMap->speakers[3] = fplAudioChannelType_LowFrequency;
22472 outChannelMap->speakers[4] = fplAudioChannelType_BackLeft;
22473 outChannelMap->speakers[5] = fplAudioChannelType_BackRight;
22474 outChannelMap->speakers[6] = fplAudioChannelType_SideLeft;
22475 outChannelMap->speakers[7] = fplAudioChannelType_SideRight;
22476 }
22477}
22478#endif
22479
22480// Forward declarations
22481fpl_internal fpl__AudioDeviceState fpl__AudioGetDeviceState(fplAudioContext *context);
22482fpl_internal bool fpl__IsAudioDeviceInitialized(fplAudioContext *context);
22483fpl_internal bool fpl__IsAudioDeviceStarted(fplAudioContext *context);
22484
22485// ############################################################################
22486//
22487// > AUDIO_BACKEND_DIRECTSOUND
22488//
22489// ############################################################################
22490#if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22491# include <mmreg.h>
22492# include <mmsystem.h>
22493# include <dsound.h>
22494
22495#define FPL__FUNC_DSOUND_DirectSoundCreate(name) HRESULT WINAPI name(const GUID* pcGuidDevice, LPDIRECTSOUND *ppDS8, LPUNKNOWN pUnkOuter)
22496typedef FPL__FUNC_DSOUND_DirectSoundCreate(func_DirectSoundCreate);
22497#define FPL__FUNC_DSOUND_DirectSoundEnumerateW(name) HRESULT WINAPI name(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext)
22498typedef FPL__FUNC_DSOUND_DirectSoundEnumerateW(func_DirectSoundEnumerateW);
22499
22500#define FPL__DIRECTSOUND_MAX_PERIODS 4
22501
22502static GUID FPL__IID_IDirectSoundNotify = { 0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16} };
22503#ifdef __cplusplus
22504 GUID FPL__IID_IDirectSoundNotify_Guid = FPL__IID_IDirectSoundNotify;
22505#else
22506 GUID *FPL__IID_IDirectSoundNotify_Guid = &FPL__IID_IDirectSoundNotify;
22507#endif
22508
22509fpl_globalvar const DWORD FPL__DirectSound_ChannelMask_Mono = SPEAKER_FRONT_CENTER;
22510fpl_globalvar const DWORD FPL__DirectSound_ChannelMask_Stereo = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
22511
22512typedef struct {
22513 HMODULE dsoundLibrary;
22514 func_DirectSoundCreate *DirectSoundCreate;
22515 func_DirectSoundEnumerateW *DirectSoundEnumerateW;
22516} fpl__DirectSoundApi;
22517
22518fpl_internal void fpl__UnloadDirectSoundApi(fpl__DirectSoundApi *dsoundApi) {
22519 fplAssert(dsoundApi != fpl_null);
22520 if (dsoundApi->dsoundLibrary != fpl_null) {
22521 FreeLibrary(dsoundApi->dsoundLibrary);
22522 }
22523 fplClearStruct(dsoundApi);
22524}
22525
22526fpl_internal bool fpl__LoadDirectSoundApi(fpl__DirectSoundApi *dsoundApi) {
22527 fplAssert(dsoundApi != fpl_null);
22528 bool result = false;
22529 const char *dsoundLibraryName = "dsound.dll";
22530 fplClearStruct(dsoundApi);
22531 do {
22532 HMODULE dsoundLibrary = fpl_null;
22533 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_AUDIO_DIRECTSOUND, dsoundLibrary, dsoundLibraryName);
22534 dsoundApi->dsoundLibrary = dsoundLibrary;
22535 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_DIRECTSOUND, dsoundLibrary, dsoundLibraryName, dsoundApi, func_DirectSoundCreate, DirectSoundCreate);
22536 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_DIRECTSOUND, dsoundLibrary, dsoundLibraryName, dsoundApi, func_DirectSoundEnumerateW, DirectSoundEnumerateW);
22537 result = true;
22538 } while (0);
22539 if (!result) {
22540 fpl__UnloadDirectSoundApi(dsoundApi);
22541 }
22542 return(result);
22543}
22544
22545typedef struct {
22546 fpl__DirectSoundApi api;
22547 LPDIRECTSOUND directSound;
22548 LPDIRECTSOUNDBUFFER primaryBuffer;
22549 LPDIRECTSOUNDBUFFER secondaryBuffer;
22550 LPDIRECTSOUNDNOTIFY notify;
22551 HANDLE notifyEvents[FPL__DIRECTSOUND_MAX_PERIODS];
22552 HANDLE stopEvent;
22553 uint32_t lastProcessedFrame;
22554 bool breakMainLoop;
22555} fpl__AudioBackendDirectSound;
22556
22557typedef struct {
22558 fpl__Win32Guid lookupID;
22559 fplAudioDeviceInfo *deviceInfos;
22560 uint32_t deviceInfoSize;
22561 uint32_t foundDeviceCount;
22562 uint32_t maxDeviceCount;
22563 uint32_t capacityOverflow;
22564 bool isLookup;
22565} fpl__DirectSoundDeviceInfos;
22566
22567typedef enum {
22568 fpl__DirectSoundChannelsInfoResult_Success = 0,
22569 fpl__DirectSoundChannelsInfoResult_Failed,
22570 fpl__DirectSoundChannelsInfoResult_TargetFormat,
22571} fpl__DirectSoundChannelInfoResult;
22572
22573fpl_internal uint16_t fpl__GetDirectSoundChannelsAndMapFromSpeakerConfig(const DWORD speakerConfig, DWORD *outputChannelMask, fplAudioChannelLayout *outputLayout) {
22574 uint16_t channels = 0;
22575 DWORD channelMask = 0;
22577
22578 if (outputChannelMask != fpl_null) {
22579 channelMask = *outputChannelMask;
22580 }
22581
22582 if (outputLayout != fpl_null) {
22583 layout = *outputLayout;
22584 }
22585
22586 switch (speakerConfig & 0xFF) {
22587 case DSSPEAKER_MONO:
22588 channels = 1;
22589 channelMask = SPEAKER_FRONT_CENTER;
22591 break;
22592
22593 case DSSPEAKER_HEADPHONE:
22594 case DSSPEAKER_STEREO:
22595 channels = 2;
22596 channelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
22598 break;
22599
22600 case DSSPEAKER_QUAD:
22601 channels = 4;
22602 channelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
22604 break;
22605
22606 case DSSPEAKER_SURROUND:
22607 channels = 4;
22608 channelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER;
22610 break;
22611
22612 case DSSPEAKER_5POINT1_SURROUND:
22613 channels = 6;
22614 channelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
22616 break;
22617
22618 case DSSPEAKER_5POINT1_BACK:
22619 channels = 6;
22620 channelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
22622 break;
22623
22624 case DSSPEAKER_7POINT1_SURROUND:
22625 channels = 8;
22626 channelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
22628 break;
22629
22630 case DSSPEAKER_7POINT1_WIDE:
22631 channels = 8;
22632 channelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;
22634 break;
22635 default:
22636 break;
22637 }
22638
22639 if (outputChannelMask != fpl_null) {
22640 *outputChannelMask = channelMask;
22641 }
22642
22643 if (outputLayout != fpl_null) {
22644 *outputLayout = layout;
22645 }
22646
22647 return channels;
22648}
22649
22650fpl_internal BOOL CALLBACK fpl__GetDeviceCallbackDirectSound(LPGUID lpGuid, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule, LPVOID lpContext) {
22651 fpl__DirectSoundDeviceInfos *infos = (fpl__DirectSoundDeviceInfos *)lpContext;
22652 fplAssert(infos != fpl_null);
22653 if (infos->deviceInfos != fpl_null) {
22654 bool isMatch;
22655 if (infos->isLookup) {
22656 if (lpGuid != fpl_null) {
22657 isMatch = fpl__Win32IsEqualGuid(*lpGuid, infos->lookupID);
22658 } else {
22659 isMatch = fpl__Win32IsEqualGuid(FPL__WIN32_GUID_ZERO, infos->lookupID);
22660 }
22661 } else {
22662 isMatch = true;
22663 }
22664
22665 if (!isMatch) {
22666 return TRUE;
22667 }
22668
22669 uint32_t index = infos->foundDeviceCount++;
22670 if (index < infos->maxDeviceCount) {
22671 fplAudioDeviceInfo *outDeviceInfo = (fplAudioDeviceInfo *)((uint8_t *)infos->deviceInfos + (infos->deviceInfoSize * index));
22672 fplClearStruct(outDeviceInfo);
22673 fplWideStringToUTF8String(lpwstrDescription, lstrlenW(lpwstrDescription), outDeviceInfo->name, fplArrayCount(outDeviceInfo->name));
22674 if (lpGuid != fpl_null) {
22675 fplMemoryCopy(lpGuid, sizeof(outDeviceInfo->id.dshow), &outDeviceInfo->id.dshow);
22676 }
22677 outDeviceInfo->isDefault = fpl__Win32IsEqualGuid(outDeviceInfo->id.dshow, FPL__WIN32_GUID_ZERO);
22678 } else {
22679 infos->capacityOverflow++;
22680 }
22681 } else {
22682 infos->foundDeviceCount++;
22683 }
22684
22685 if (infos->isLookup && infos->foundDeviceCount > 0) {
22686 return FALSE;
22687 }
22688
22689 return TRUE;
22690}
22691
22692fpl_internal FPL_AUDIO_BACKEND_GET_AUDIO_DEVICES_FUNC(fpl__AudiobackendDirectSoundGetAudioDevices) {
22693 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
22694 fplAssert(impl != fpl_null);
22695
22696 const fpl__DirectSoundApi *dsoundApi = &impl->api;
22697 if (dsoundApi->dsoundLibrary == fpl_null) {
22698 FPL__WARNING(FPL__MODULE_AUDIO_DIRECTSOUND, "API is not loaded!");
22699 return 0;
22700 }
22701
22702 uint32_t result = 0;
22703 fpl__DirectSoundDeviceInfos infos = fplZeroInit;
22704 infos.maxDeviceCount = maxDeviceCount;
22705 infos.deviceInfos = deviceInfos;
22706 infos.capacityOverflow = 0;
22707 infos.deviceInfoSize = deviceInfoSize;
22708 dsoundApi->DirectSoundEnumerateW(fpl__GetDeviceCallbackDirectSound, &infos);
22709 result = infos.foundDeviceCount;
22710 if (infos.capacityOverflow > 0) {
22711 FPL__WARNING(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);
22712 }
22713 return(result);
22714}
22715
22716fpl_internal FPL_AUDIO_BACKEND_GET_AUDIO_DEVICE_INFO_FUNC(fpl__AudiobackendDirectSoundGetAudioDeviceInfo) {
22717 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
22718 fplAssert(impl != fpl_null);
22719
22720 fplAssert(fpl__global__AppState != fpl_null);
22721 fpl__PlatformAppState *appState = fpl__global__AppState;
22722 fpl__Win32AppState *win32AppState = &appState->win32;
22723 const fpl__Win32Api *apiFuncs = &win32AppState->winApi;
22724
22725 const fpl__DirectSoundApi *dsoundApi = &impl->api;
22726 if (dsoundApi->dsoundLibrary == fpl_null) {
22727 FPL__WARNING(FPL__MODULE_AUDIO_DIRECTSOUND, "API is not loaded!");
22729 }
22730
22731 LPDIRECTSOUND directSound = fpl_null;
22732
22733 // Load direct sound object
22734 const fpl__Win32Guid *deviceId = fpl_null;
22735 if (targetDevice != fpl_null) {
22736 fplAssert(sizeof(GUID) == sizeof(targetDevice->dshow));
22737 deviceId = &targetDevice->dshow;
22738 }
22739
22740 // Query device
22741 fplAudioDeviceInfo deviceInfo = fplZeroInit;
22742 fpl__DirectSoundDeviceInfos infos = fplZeroInit;
22743 infos.maxDeviceCount = 1;
22744 infos.deviceInfos = &deviceInfo;
22745 infos.capacityOverflow = 0;
22746 infos.deviceInfoSize = sizeof(deviceInfo);
22747 infos.isLookup = true;
22748 if (deviceId != fpl_null) {
22749 fpl__Win32CopyGuid(deviceId, &infos.lookupID);
22750 } else {
22751 fpl__Win32CopyGuid(&FPL__WIN32_GUID_ZERO, &infos.lookupID);
22752 }
22753 if (FAILED(dsoundApi->DirectSoundEnumerateW(fpl__GetDeviceCallbackDirectSound, &infos))) {
22755 }
22756
22757 // Open direct sound with device
22758 if (!SUCCEEDED(dsoundApi->DirectSoundCreate((const GUID *)deviceId, &directSound, fpl_null))) {
22759 char idString[64];
22760 fpl__Win32FormatGuidString(idString, fplArrayCount(idString), deviceId);
22762 }
22763
22764 // Get either local window handle or desktop handle
22765 HWND windowHandle = fpl_null;
22766# if defined(FPL__ENABLE_WINDOW)
22767 if (appState->initFlags & fplInitFlags_Window) {
22768 windowHandle = appState->window.win32.windowHandle;
22769 }
22770# endif
22771 if (windowHandle == fpl_null) {
22772 windowHandle = apiFuncs->user.GetDesktopWindow();
22773 }
22774
22775 // The cooperative level must be set before doing anything else
22776 if (FAILED(IDirectSound_SetCooperativeLevel(directSound, windowHandle, DSSCL_PRIORITY))) {
22777 IDirectSound_Release(directSound);
22779 }
22780
22781 // Get capabilities
22782 DSCAPS caps = fplZeroInit;
22783 caps.dwSize = sizeof(caps);
22784 if (FAILED(IDirectSound_GetCaps(directSound, &caps))) {
22785 IDirectSound_Release(directSound);
22787 }
22788
22789 // Get number of channels
22790 uint16_t channels;
22791 if ((caps.dwFlags & DSCAPS_PRIMARYSTEREO) != 0) {
22792 DWORD speakerConfig;
22793 channels = 2;
22794 if (SUCCEEDED(IDirectSound_GetSpeakerConfig(directSound, &speakerConfig))) {
22795 channels = fpl__GetDirectSoundChannelsAndMapFromSpeakerConfig(speakerConfig, fpl_null, fpl_null);
22796 }
22797 } else {
22798 channels = 1;
22799 }
22800
22801 // @IMPLEMENT(final): [DirectSound] Implement audio device info extended
22802
22804}
22805
22806// Convert a flag from a dwChannelMask to a fplAudioChannelType
22807fpl_internal fplAudioChannelType fpl__MapWin32AudioChannelIdToAudioSpeakerFlags(const DWORD id) {
22808 switch (id) {
22809 case SPEAKER_FRONT_LEFT: return fplAudioChannelType_FrontLeft;
22810 case SPEAKER_FRONT_RIGHT: return fplAudioChannelType_FrontRight;
22811 case SPEAKER_FRONT_CENTER: return fplAudioChannelType_FrontCenter;
22812 case SPEAKER_LOW_FREQUENCY: return fplAudioChannelType_LowFrequency;
22813 case SPEAKER_BACK_LEFT: return fplAudioChannelType_BackLeft;
22814 case SPEAKER_BACK_RIGHT: return fplAudioChannelType_BackRight;
22815 case SPEAKER_FRONT_LEFT_OF_CENTER: return fplAudioChannelType_FrontLeftOfCenter;
22816 case SPEAKER_FRONT_RIGHT_OF_CENTER: return fplAudioChannelType_FrontRightOfCenter;
22817 case SPEAKER_BACK_CENTER: return fplAudioChannelType_BackCenter;
22818 case SPEAKER_SIDE_LEFT: return fplAudioChannelType_SideLeft;
22819 case SPEAKER_SIDE_RIGHT: return fplAudioChannelType_SideRight;
22820 case SPEAKER_TOP_CENTER: return fplAudioChannelType_TopCenter;
22821 case SPEAKER_TOP_FRONT_LEFT: return fplAudioChannelType_TopFrontLeft;
22822 case SPEAKER_TOP_FRONT_CENTER: return fplAudioChannelType_TopFrontCenter;
22823 case SPEAKER_TOP_FRONT_RIGHT: return fplAudioChannelType_TopFrontRight;
22824 case SPEAKER_TOP_BACK_LEFT: return fplAudioChannelType_TopBackLeft;
22825 case SPEAKER_TOP_BACK_CENTER: return fplAudioChannelType_TopBackCenter;
22826 case SPEAKER_TOP_BACK_RIGHT: return fplAudioChannelType_TopBackRight;
22827 default: return fplAudioChannelType_None;
22828 }
22829}
22830
22831// Convert a fplAudioChannelType to flag for dwChannelMask
22832fpl_internal DWORD fpl__MapAudioSpeakerFlagsToWin32AudioChannelId(const fplAudioChannelType audioChannelFlags) {
22833 switch (audioChannelFlags) {
22834 case fplAudioChannelType_FrontLeft: return SPEAKER_FRONT_LEFT;
22835 case fplAudioChannelType_FrontRight: return SPEAKER_FRONT_RIGHT;
22836 case fplAudioChannelType_FrontCenter: return SPEAKER_FRONT_CENTER;
22837 case fplAudioChannelType_LowFrequency: return SPEAKER_LOW_FREQUENCY;
22838 case fplAudioChannelType_BackLeft: return SPEAKER_BACK_LEFT;
22839 case fplAudioChannelType_BackRight: return SPEAKER_BACK_RIGHT;
22840 case fplAudioChannelType_FrontLeftOfCenter: return SPEAKER_FRONT_LEFT_OF_CENTER;
22841 case fplAudioChannelType_FrontRightOfCenter: return SPEAKER_FRONT_RIGHT_OF_CENTER;
22842 case fplAudioChannelType_BackCenter: return SPEAKER_BACK_CENTER;
22843 case fplAudioChannelType_SideLeft: return SPEAKER_SIDE_LEFT;
22844 case fplAudioChannelType_SideRight: return SPEAKER_SIDE_RIGHT;
22845 case fplAudioChannelType_TopCenter: return SPEAKER_TOP_CENTER;
22846 case fplAudioChannelType_TopFrontLeft: return SPEAKER_TOP_FRONT_LEFT;
22847 case fplAudioChannelType_TopFrontCenter: return SPEAKER_TOP_FRONT_CENTER;
22848 case fplAudioChannelType_TopFrontRight: return SPEAKER_TOP_FRONT_RIGHT;
22849 case fplAudioChannelType_TopBackLeft: return SPEAKER_TOP_BACK_LEFT;
22850 case fplAudioChannelType_TopBackCenter: return SPEAKER_TOP_BACK_CENTER;
22851 case fplAudioChannelType_TopBackRight: return SPEAKER_TOP_BACK_RIGHT;
22852 default: return 0;
22853 }
22854}
22855
22856// Fill out the mapping table from a win32 channel mask and number of channels
22857fpl_internal void fpl__CreateChannelsMappingFromChannelMask(const DWORD channelMask, const uint16_t channels, fplAudioChannelMap *channelMap) {
22858 fplClearStruct(channelMap);
22859 if ((channels == 1) && ((channelMask == 0) || ((channelMask & SPEAKER_FRONT_CENTER) != 0))) {
22861 } else if (channels == 2 && channelMask == 0) {
22862 channelMap->speakers[0] = fplAudioChannelType_FrontLeft;
22864 } else {
22865 uint32_t channelIndex = 0;
22866 for (uint32_t bit = 0; bit < 32; ++bit) {
22867 DWORD bitMask = (channelMask & (1UL << bit));
22868 if (bitMask != 0) {
22869 fplAudioChannelType flags = fpl__MapWin32AudioChannelIdToAudioSpeakerFlags(bitMask);
22870 channelMap->speakers[channelIndex++] = flags;
22871 }
22872 }
22873 }
22874}
22875
22876// Get a dwChannelMaskl from the specified channel map and number of channels
22877fpl_internal DWORD fpl__GetWin32AudioChannelMaskFromMapping(const fplAudioChannelMap *channelMap, const uint16_t channels) {
22878 fplAssert(channelMap != fpl_null);
22879 DWORD result = 0;
22880 for (uint16_t channelIndex = 0; channelIndex < channels; ++channelIndex) {
22881 DWORD channelValue = fpl__MapAudioSpeakerFlagsToWin32AudioChannelId(channelMap->speakers[channelIndex]);
22882 result |= channelValue;
22883 }
22884 return result;
22885}
22886
22887// Converts a audio format with its channel map into a WAVEFORMATEXTENSIBLE
22888fpl_internal void fpl__SetupWaveFormatDirectSound(const fplAudioFormat *sourceFormat, const fplAudioChannelMap *channelMap, WAVEFORMATEXTENSIBLE *outputWaveFormat) {
22889 WAVEFORMATEXTENSIBLE waveFormat = fplZeroInit;
22890 waveFormat.Format.cbSize = sizeof(waveFormat);
22891 waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
22892 waveFormat.Format.nChannels = (WORD)sourceFormat->channels;
22893 waveFormat.Format.nSamplesPerSec = (DWORD)sourceFormat->sampleRate;
22894 waveFormat.Format.wBitsPerSample = (WORD)fplGetAudioSampleSizeInBytes(sourceFormat->type) * 8;
22895 waveFormat.Format.nBlockAlign = (waveFormat.Format.nChannels * waveFormat.Format.wBitsPerSample) / 8;
22896 waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nBlockAlign * waveFormat.Format.nSamplesPerSec;
22897 waveFormat.Samples.wValidBitsPerSample = waveFormat.Format.wBitsPerSample;
22898 if ((sourceFormat->type == fplAudioFormatType_F32) || (sourceFormat->type == fplAudioFormatType_F64)) {
22899 fpl__Win32CopyGuid(&FPL__GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, &waveFormat.SubFormat);
22900 } else {
22901 fpl__Win32CopyGuid(&FPL__GUID_KSDATAFORMAT_SUBTYPE_PCM, &waveFormat.SubFormat);
22902 }
22903 waveFormat.dwChannelMask = fpl__GetWin32AudioChannelMaskFromMapping(channelMap, sourceFormat->channels);
22904 *outputWaveFormat = waveFormat;
22905}
22906
22907fpl_internal FPL_AUDIO_BACKEND_RELEASE_DEVICE_FUNC(fpl__AudiobackendDirectSoundReleaseDevice) {
22908 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
22909 fplAssert(impl != fpl_null);
22910
22911 if (impl->stopEvent != fpl_null) {
22912 CloseHandle(impl->stopEvent);
22913 impl->stopEvent = fpl_null;
22914 }
22915
22916 for (uint32_t i = 0; i < backend->internalFormat.periods; ++i) {
22917 if (impl->notifyEvents[i] != fpl_null) {
22918 CloseHandle(impl->notifyEvents[i]);
22919 impl->notifyEvents[i] = fpl_null;
22920 }
22921 }
22922
22923 if (impl->notify != fpl_null) {
22924 IDirectSoundNotify_Release(impl->notify);
22925 impl->notify = fpl_null;
22926 }
22927
22928 if (impl->secondaryBuffer != fpl_null) {
22929 IDirectSoundBuffer_Release(impl->secondaryBuffer);
22930 impl->secondaryBuffer = fpl_null;
22931 }
22932
22933 if (impl->primaryBuffer != fpl_null) {
22934 IDirectSoundBuffer_Release(impl->primaryBuffer);
22935 impl->primaryBuffer = fpl_null;
22936 }
22937
22938 if (impl->directSound != fpl_null) {
22939 IDirectSound_Release(impl->directSound);
22940 impl->directSound = fpl_null;
22941 }
22942
22943 return true;
22944}
22945
22946fpl_internal FPL_AUDIO_BACKEND_RELEASE_FUNC(fpl__AudiobackendDirectSoundRelease) {
22947 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
22948 fplAssert(impl != fpl_null);
22949
22950 fpl__AudiobackendDirectSoundReleaseDevice(context, backend);
22951
22952 fpl__UnloadDirectSoundApi(&impl->api);
22953
22954 fplClearStruct(impl);
22955
22956 return true;
22957}
22958
22959fpl_internal FPL_AUDIO_BACKEND_INITIALIZE_FUNC(fpl__AudiobackendDirectSoundInitialize) {
22960 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
22961 fplAssert(impl != fpl_null);
22962
22963#define FPL__DSOUND_INIT_ERROR(ret, format, ...) do { \
22964 FPL__ERROR(FPL__MODULE_AUDIO_DIRECTSOUND, format, ## __VA_ARGS__); \
22965 fpl__AudiobackendDirectSoundRelease(context, backend); \
22966 return ret; \
22967} while (0)
22968
22969 // Load direct sound library
22970 fpl__DirectSoundApi *dsoundApi = &impl->api;
22971 if (!fpl__LoadDirectSoundApi(dsoundApi)) {
22972 FPL__DSOUND_INIT_ERROR(fplAudioResultType_ApiFailed, "API could not be loaded!");
22973 }
22974
22976
22977#undef FPL__DSOUND_INIT_ERROR
22978}
22979
22980fpl_internal FPL_AUDIO_BACKEND_INITIALIZE_DEVICE_FUNC(fpl__AudiobackendDirectSoundInitializeDevice) {
22981 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
22982 fplAssert(impl != fpl_null);
22983
22984#define FPL__DSOUND_INIT_ERROR(ret, format, ...) do { \
22985 FPL__ERROR(FPL__MODULE_AUDIO_DIRECTSOUND, format, ## __VA_ARGS__); \
22986 fpl__AudiobackendDirectSoundReleaseDevice(context, backend); \
22987 return ret; \
22988} while (0)
22989
22990 fplAssert(fpl__global__AppState != fpl_null);
22991 fpl__PlatformAppState *appState = fpl__global__AppState;
22992 fpl__Win32AppState *win32AppState = &appState->win32;
22993 const fpl__Win32Api *apiFuncs = &win32AppState->winApi;
22994
22995 fpl__DirectSoundApi *dsoundApi = &impl->api;
22996 if (dsoundApi->dsoundLibrary == fpl_null) {
22997 FPL__DSOUND_INIT_ERROR(fplAudioResultType_ApiFailed, "API is not loaded!");
22998 }
22999
23000 // Initialize channel map
23001 fpl__SetAudioDefaultChannelMapWin32(targetFormat->channels, targetFormat->channelLayout, outputChannelMap);
23002
23003 // Convert source format to wave format
23004 fplAudioChannelLayout channelLayout = targetFormat->channelLayout;
23005 WAVEFORMATEXTENSIBLE waveFormat = fplZeroInit;
23006 fpl__SetupWaveFormatDirectSound(targetFormat, outputChannelMap, &waveFormat);
23007
23008 // Query device
23009 fplAudioDeviceInfo internalDevice = fplZeroInit;
23010 fpl__DirectSoundDeviceInfos infos = fplZeroInit;
23011 infos.maxDeviceCount = 1;
23012 infos.deviceInfos = &internalDevice;
23013 infos.deviceInfoSize = sizeof(internalDevice);
23014 infos.isLookup = true;
23015 if (fplGetStringLength(targetDevice->name) > 0) {
23016 fpl__Win32CopyGuid(&targetDevice->id.dshow, &infos.lookupID);
23017 } else {
23018 fpl__Win32CopyGuid(&FPL__WIN32_GUID_ZERO, &infos.lookupID);
23019 }
23020 if (FAILED(dsoundApi->DirectSoundEnumerateW(fpl__GetDeviceCallbackDirectSound, &infos))) {
23022 }
23023
23024 const fpl__Win32Guid *deviceId = &internalDevice.id.dshow;
23025
23026 if (FAILED(dsoundApi->DirectSoundCreate((const GUID *)deviceId, &impl->directSound, fpl_null))) {
23027 char idString[64];
23028 fpl__Win32FormatGuidString(idString, fplArrayCount(idString), deviceId);
23029 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceByIdNotFound, "Audio device by id '%s' could not be created!", idString);
23030 }
23031
23032 // Get either local window handle or desktop handle
23033 HWND windowHandle = fpl_null;
23034# if defined(FPL__ENABLE_WINDOW)
23035 if (appState->initFlags & fplInitFlags_Window) {
23036 windowHandle = appState->window.win32.windowHandle;
23037 }
23038# endif
23039 if (windowHandle == fpl_null) {
23040 windowHandle = apiFuncs->user.GetDesktopWindow();
23041 }
23042
23043 // The cooperative level must be set before doing anything else
23044 fplAudioShareMode shareMode = fplGetAudioShareMode(targetFormat->mode);
23045 bool isExclusive = shareMode == fplAudioShareMode_Exclusive;
23046 if (FAILED(IDirectSound_SetCooperativeLevel(impl->directSound, windowHandle, isExclusive ? DSSCL_EXCLUSIVE : DSSCL_PRIORITY))) {
23047 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed setting DirectSound Cooperative Level to '%s' mode!", (isExclusive ? "Exclusive" : "Priority"));
23048 }
23049
23050 // Create primary buffer
23051 DSBUFFERDESC descDSPrimary = fplZeroInit;
23052 descDSPrimary.dwSize = sizeof(DSBUFFERDESC);
23053 descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
23054 if (FAILED(IDirectSound_CreateSoundBuffer(impl->directSound, &descDSPrimary, &impl->primaryBuffer, fpl_null))) {
23055 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed creating primary DirectSound sound buffer!");
23056 }
23057
23058 LPDIRECTSOUNDBUFFER secondaryBuffer = fpl_null;
23059
23060 fplAudioFormat internalFormat = fplZeroInit;
23061
23062 // Get capabilities
23063 DSCAPS caps = fplZeroInit;
23064 caps.dwSize = sizeof(caps);
23065 if (FAILED(IDirectSound_GetCaps(impl->directSound, &caps))) {
23066 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed getting device caps for DirectSound!");
23067 }
23068
23069 // Get supported number of channels and channel mask, when channels or layout was default
23070 if ((targetFormat->defaultFields & fplAudioDefaultFields_Channels) && (targetFormat->defaultFields & fplAudioDefaultFields_ChannelLayout)) {
23071 if ((caps.dwFlags & DSCAPS_PRIMARYSTEREO) != 0) {
23072 DWORD speakerConfig;
23073 if (SUCCEEDED(IDirectSound_GetSpeakerConfig(impl->directSound, &speakerConfig))) {
23074 DWORD channelMask = 0;
23075 waveFormat.Format.nChannels = fpl__GetDirectSoundChannelsAndMapFromSpeakerConfig(speakerConfig, &channelMask, &channelLayout);
23076 waveFormat.dwChannelMask = channelMask;
23077 } else {
23078 waveFormat.Format.nChannels = 2;
23079 waveFormat.dwChannelMask = FPL__DirectSound_ChannelMask_Stereo;
23080 channelLayout = fplAudioChannelLayout_Stereo;
23081 }
23082 } else {
23083 waveFormat.Format.nChannels = 1;
23084 waveFormat.dwChannelMask = FPL__DirectSound_ChannelMask_Mono;
23085 channelLayout = fplAudioChannelLayout_Mono;
23086 }
23087 fpl__CreateChannelsMappingFromChannelMask(waveFormat.dwChannelMask, waveFormat.Format.nChannels, outputChannelMap);
23088 } else {
23089 fplAssert(targetFormat->channels > 0);
23090 fplAssert(targetFormat->channelLayout >= fplAudioChannelLayout_First && targetFormat->channelLayout <= fplAudioChannelLayout_Last);
23091 }
23092
23093 // Set format
23094 if (FAILED(IDirectSoundBuffer_SetFormat(impl->primaryBuffer, (WAVEFORMATEX *)&waveFormat))) {
23095 char subformatString[64];
23096 fpl__Win32FormatGuidString(subformatString, fplArrayCount(subformatString), (const fpl__Win32Guid *)&waveFormat.SubFormat);
23097 FPL__DSOUND_INIT_ERROR(fplAudioResultType_UnsuportedDeviceFormat, "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);
23098 }
23099
23100 // Get the required format size in bytes
23101 DWORD requiredFormatSize;
23102 if (FAILED(IDirectSoundBuffer_GetFormat(impl->primaryBuffer, fpl_null, 0, &requiredFormatSize))) {
23103 FPL__DSOUND_INIT_ERROR(fplAudioResultType_UnsuportedDeviceFormat, "Failed getting format size for primary sound buffer!");
23104 }
23105
23106 // Get actual format
23107 char *actualFormatData = (char *)fplStackAllocate(requiredFormatSize);
23108 WAVEFORMATEXTENSIBLE *actualFormat = (WAVEFORMATEXTENSIBLE *)actualFormatData;
23109 if (FAILED(IDirectSoundBuffer_GetFormat(impl->primaryBuffer, (WAVEFORMATEX *)actualFormat, requiredFormatSize, fpl_null))) {
23110 FPL__DSOUND_INIT_ERROR(fplAudioResultType_UnsuportedDeviceFormat, "Failed getting actual wave format from size '{%u}' for primary sound buffer!", requiredFormatSize);
23111 }
23112
23113 // Set internal format
23114 if (fpl__Win32IsEqualGuid(actualFormat->SubFormat, FPL__GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
23115 if (actualFormat->Format.wBitsPerSample == 64) {
23116 internalFormat.type = fplAudioFormatType_F64;
23117 } else {
23118 internalFormat.type = fplAudioFormatType_F32;
23119 }
23120 } else {
23121 switch (actualFormat->Format.wBitsPerSample) {
23122 case 8:
23123 internalFormat.type = fplAudioFormatType_U8;
23124 break;
23125 case 16:
23126 internalFormat.type = fplAudioFormatType_S16;
23127 break;
23128 case 24:
23129 internalFormat.type = fplAudioFormatType_S24;
23130 break;
23131 case 32:
23132 internalFormat.type = fplAudioFormatType_S32;
23133 break;
23134 case 64:
23135 internalFormat.type = fplAudioFormatType_S64;
23136 break;
23137 }
23138 }
23139 internalFormat.channels = actualFormat->Format.nChannels;
23140 internalFormat.channelLayout = channelLayout;
23141 internalFormat.sampleRate = actualFormat->Format.nSamplesPerSec;
23142
23143 // @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.
23144 internalFormat.periods = fplMax(2, fplMin(targetFormat->periods, 4));
23145 internalFormat.bufferSizeInFrames = targetFormat->bufferSizeInFrames;
23146
23147 // Validate buffer and format
23148 uint32_t bufferSizeInBytes = fplGetAudioBufferSizeInBytes(internalFormat.type, internalFormat.channels, internalFormat.bufferSizeInFrames);
23149 fplAssert(bufferSizeInBytes > 0);
23150 fplAssert(waveFormat.Format.nChannels > 0 && waveFormat.Format.nSamplesPerSec > 0 && waveFormat.Format.wBitsPerSample > 0);
23151
23152 //
23153 // Create secondary buffer
23154 //
23155 DSBUFFERDESC descDS = fplZeroInit;
23156 descDS.dwSize = sizeof(DSBUFFERDESC);
23157 descDS.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
23158 descDS.dwBufferBytes = (DWORD)bufferSizeInBytes;
23159 descDS.lpwfxFormat = (WAVEFORMATEX *)&waveFormat;
23160 if (FAILED(IDirectSound_CreateSoundBuffer(impl->directSound, &descDS, &impl->secondaryBuffer, fpl_null))) {
23161 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed creating secondary sound buffer with buffer size of '%u' bytes", bufferSizeInBytes);
23162 }
23163
23164 const char *internalFormatTypeName = fplGetAudioFormatName(internalFormat.type);
23165 FPL_LOG(fplLogLevel_Info, FPL__MODULE_AUDIO_DIRECTSOUND,
23166 "Using internal format (Channels: %u, Samplerate: %u, Type: %s, Periods: %u, Buffer size frames/bytes: %u/%u)",
23167 internalFormat.channels,
23168 internalFormat.sampleRate,
23169 internalFormatTypeName,
23170 internalFormat.periods,
23171 internalFormat.bufferSizeInFrames,
23172 bufferSizeInBytes);
23173
23174 // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer.
23175 if (FAILED(IDirectSoundBuffer_QueryInterface(impl->secondaryBuffer, FPL__IID_IDirectSoundNotify_Guid, (void **)&impl->notify))) {
23176 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed query direct sound notify interface");
23177 }
23178
23179 // Setup notifications
23180 DWORD periodSizeInBytes = bufferSizeInBytes / internalFormat.periods;
23181 DSBPOSITIONNOTIFY notifyPoints[FPL__DIRECTSOUND_MAX_PERIODS];
23182 for (uint32_t i = 0; i < internalFormat.periods; ++i) {
23183 impl->notifyEvents[i] = CreateEventA(fpl_null, false, false, fpl_null);
23184 if (impl->notifyEvents[i] == fpl_null) {
23185 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed creating notify event for period %u", i);
23186 }
23187
23188 // The notification offset is in bytes.
23189 notifyPoints[i].dwOffset = i * periodSizeInBytes;
23190 notifyPoints[i].hEventNotify = impl->notifyEvents[i];
23191 }
23192 if (FAILED(IDirectSoundNotify_SetNotificationPositions(impl->notify, internalFormat.periods, notifyPoints))) {
23193 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed setting notification position for %u periods", internalFormat.periods);
23194 }
23195
23196 // Create stop event
23197 impl->stopEvent = CreateEventA(fpl_null, false, false, fpl_null);
23198 if (impl->stopEvent == fpl_null) {
23199 FPL__DSOUND_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed creating stop event");
23200 }
23201
23202 *outputFormat = internalFormat;
23203 *outputDevice = internalDevice;
23204
23206
23207#undef FPL__DSOUND_INIT_ERROR
23208}
23209
23210fpl_internal bool fpl__GetCurrentFrameDirectSound(const fplAudioFormat *internalFormat, fpl__AudioBackendDirectSound *impl, uint32_t *pCurrentPos) {
23211 fplAssert(pCurrentPos != fpl_null);
23212 *pCurrentPos = 0;
23213
23214 fplAssert(impl->secondaryBuffer != fpl_null);
23215 DWORD dwCurrentPosition;
23216 if (FAILED(IDirectSoundBuffer_GetCurrentPosition(impl->secondaryBuffer, fpl_null, &dwCurrentPosition))) {
23217 return false;
23218 }
23219
23220 fplAssert(internalFormat->channels > 0);
23221 *pCurrentPos = (uint32_t)dwCurrentPosition / fplGetAudioSampleSizeInBytes(internalFormat->type) / internalFormat->channels;
23222 return true;
23223}
23224
23225fpl_internal uint32_t fpl__GetAvailableFramesDirectSound(const fplAudioFormat *internalFormat, fpl__AudioBackendDirectSound *impl) {
23226 // Get current frame from current play position
23227 uint32_t currentFrame;
23228 if (!fpl__GetCurrentFrameDirectSound(internalFormat, impl, &currentFrame)) {
23229 return 0;
23230 }
23231
23232 // In a playback device the last processed frame should always be ahead of the current frame. The space between
23233 // the last processed and current frame (moving forward, starting from the last processed frame) is the amount
23234 // of space available to write.
23235 uint32_t totalFrameCount = internalFormat->bufferSizeInFrames;
23236 uint32_t committedBeg = currentFrame;
23237 uint32_t committedEnd;
23238 committedEnd = impl->lastProcessedFrame;
23239 if (committedEnd <= committedBeg) {
23240 committedEnd += totalFrameCount;
23241 }
23242
23243 uint32_t committedSize = (committedEnd - committedBeg);
23244 fplAssert(committedSize <= totalFrameCount);
23245
23246 return totalFrameCount - committedSize;
23247}
23248
23249fpl_internal uint32_t fpl__WaitForFramesDirectSound(const fplAudioFormat *internalFormat, fpl__AudioBackendDirectSound *impl) {
23250 fplAssert(internalFormat->sampleRate > 0);
23251 fplAssert(internalFormat->periods > 0);
23252
23253 // The timeout to use for putting the thread to sleep is based on the size of the buffer and the period count.
23254 DWORD timeoutInMilliseconds = (internalFormat->bufferSizeInFrames / (internalFormat->sampleRate / 1000)) / internalFormat->periods;
23255 if (timeoutInMilliseconds < 1) {
23256 timeoutInMilliseconds = 1;
23257 }
23258
23259 // Copy event handles so we can wait for each one
23260 unsigned int eventCount = internalFormat->periods + 1;
23261 HANDLE pEvents[FPL__DIRECTSOUND_MAX_PERIODS + 1]; // +1 for the stop event.
23262 fplMemoryCopy(impl->notifyEvents, sizeof(HANDLE) * internalFormat->periods, pEvents);
23263 pEvents[eventCount - 1] = impl->stopEvent;
23264
23265 while (!impl->breakMainLoop) {
23266 // Get available frames from directsound
23267 uint32_t framesAvailable = fpl__GetAvailableFramesDirectSound(internalFormat, impl);
23268 if (framesAvailable > 0) {
23269 return framesAvailable;
23270 }
23271
23272 // If we get here it means we weren't able to find any frames. We'll just wait here for a bit.
23273 WaitForMultipleObjects(eventCount, pEvents, FALSE, timeoutInMilliseconds);
23274 }
23275
23276 // We'll get here if the loop was terminated. Just return whatever's available.
23277 return fpl__GetAvailableFramesDirectSound(internalFormat, impl);
23278}
23279
23280fpl_internal FPL_AUDIO_BACKEND_STOP_DEVICE_FUNC(fpl__AudioBackendDirectSoundStopDevice) {
23281 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
23282
23283 fplAssert(impl->secondaryBuffer != fpl_null);
23284
23285 if (FAILED(IDirectSoundBuffer_Stop(impl->secondaryBuffer))) {
23286 return false;
23287 }
23288
23289 IDirectSoundBuffer_SetCurrentPosition(impl->secondaryBuffer, 0);
23290
23291 return true;
23292}
23293
23294fpl_internal FPL_AUDIO_BACKEND_START_DEVICE_FUNC(fpl__AudioBackendDirectSoundStartDevice) {
23295 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
23296 fplAssert(impl != fpl_null);
23297
23298 fplAssert(backend->internalFormat.channels > 0);
23299 fplAssert(backend->internalFormat.periods > 0);
23300 uint32_t audioSampleSizeBytes = fplGetAudioSampleSizeInBytes(backend->internalFormat.type);
23301 fplAssert(audioSampleSizeBytes > 0);
23302
23303 // Before playing anything we need to grab an initial group of samples from the client.
23304 uint32_t framesToRead = backend->internalFormat.bufferSizeInFrames / backend->internalFormat.periods;
23305 uint32_t desiredLockSize = framesToRead * backend->internalFormat.channels * audioSampleSizeBytes;
23306
23307 void *pLockPtr;
23308 DWORD actualLockSize;
23309 void *pLockPtr2;
23310 DWORD actualLockSize2;
23311
23312 if (SUCCEEDED(IDirectSoundBuffer_Lock(impl->secondaryBuffer, 0, desiredLockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) {
23313 framesToRead = actualLockSize / audioSampleSizeBytes / backend->internalFormat.channels;
23314 fpl__ReadAudioFramesFromClient(backend, framesToRead, pLockPtr);
23315 IDirectSoundBuffer_Unlock(impl->secondaryBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2);
23316 impl->lastProcessedFrame = framesToRead;
23317 if (FAILED(IDirectSoundBuffer_Play(impl->secondaryBuffer, 0, 0, DSBPLAY_LOOPING))) {
23319 }
23320 } else {
23322 }
23324}
23325
23326fpl_internal FPL_AUDIO_BACKEND_STOP_MAIN_LOOP_FUNC(fpl__AudioBackendDirectSoundStopMainLoop) {
23327 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
23328 fplAssert(impl != fpl_null);
23329 impl->breakMainLoop = true;
23330 SetEvent(impl->stopEvent);
23331}
23332
23333fpl_internal FPL_AUDIO_BACKEND_MAIN_LOOP_FUNC(fpl__AudioBackendDirectSoundMainLoop) {
23334 fpl__AudioBackendDirectSound *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AudioBackendDirectSound);
23335 fplAssert(impl != fpl_null);
23336
23337 fplAssert(backend->internalFormat.channels > 0);
23338 uint32_t audioSampleSizeBytes = fplGetAudioSampleSizeInBytes(backend->internalFormat.type);
23339 fplAssert(audioSampleSizeBytes > 0);
23340
23341 // Make sure the stop event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects().
23342 ResetEvent(impl->stopEvent);
23343
23344 // Main loop
23345 impl->breakMainLoop = false;
23346 while (!impl->breakMainLoop) {
23347 // Wait until we get available frames from directsound
23348 uint32_t framesAvailable = fpl__WaitForFramesDirectSound(&backend->internalFormat, impl);
23349 if (framesAvailable == 0) {
23350 continue;
23351 }
23352
23353 // Don't bother grabbing more data if the device is being stopped.
23354 if (impl->breakMainLoop) {
23355 break;
23356 }
23357
23358 // Lock playback buffer
23359 DWORD lockOffset = impl->lastProcessedFrame * backend->internalFormat.channels * audioSampleSizeBytes;
23360 DWORD lockSize = framesAvailable * backend->internalFormat.channels * audioSampleSizeBytes;
23361 {
23362 void *pLockPtr;
23363 DWORD actualLockSize;
23364 void *pLockPtr2;
23365 DWORD actualLockSize2;
23366 if (FAILED(IDirectSoundBuffer_Lock(impl->secondaryBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) {
23367 FPL__ERROR(FPL__MODULE_AUDIO_DIRECTSOUND, "Failed to lock directsound secondary buffer '%p' for offset/size (%lu / %lu)", impl->secondaryBuffer, lockOffset, lockSize);
23368 break;
23369 }
23370
23371 // Read actual frames from user
23372 uint32_t frameCount = actualLockSize / audioSampleSizeBytes / backend->internalFormat.channels;
23373 fpl__ReadAudioFramesFromClient(backend, frameCount, pLockPtr);
23374 impl->lastProcessedFrame = (impl->lastProcessedFrame + frameCount) % backend->internalFormat.bufferSizeInFrames;
23375
23376 // Unlock playback buffer
23377 IDirectSoundBuffer_Unlock(impl->secondaryBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2);
23378 }
23379 }
23380}
23381
23382fpl_globalvar fplAudioBackendDescriptor fpl__global_audioBackendDirectShowDescriptor = {
23383 fplStructField(fplAudioBackendDescriptor, header, {
23384 fplStructField(fplAudioBackendDescriptorHeader, idName, {
23385 fplStructField(fplAudioBackendDescriptorIDName, id, { 0x618d4a47, 0x163c, 0x441d, { 0x85, 0xc6, 0x4d, 0x94, 0x33, 0x61, 0xc8, 0xb3 } }),
23386 fplStructField(fplAudioBackendDescriptorIDName, name, "DirectSound"),
23387 }),
23388 fplStructField(fplAudioBackendDescriptorHeader, type, fplAudioBackendType_DirectSound),
23389 fplStructField(fplAudioBackendDescriptorHeader, backendSize, sizeof(fpl__AudioBackendDirectSound)),
23390 fplStructField(fplAudioBackendDescriptorHeader, isAsync, false),
23391 fplStructField(fplAudioBackendDescriptorHeader, isValid, true),
23392 }),
23393 fplStructField(fplAudioBackendDescriptor, table, {
23394 fplStructField(fplAudioBackendFunctionTable, initialize, fpl__AudiobackendDirectSoundInitialize),
23395 fplStructField(fplAudioBackendFunctionTable, release, fpl__AudiobackendDirectSoundRelease),
23396 fplStructField(fplAudioBackendFunctionTable, getAudioDevices, fpl__AudiobackendDirectSoundGetAudioDevices),
23397 fplStructField(fplAudioBackendFunctionTable, getAudioDeviceInfo, fpl__AudiobackendDirectSoundGetAudioDeviceInfo),
23398 fplStructField(fplAudioBackendFunctionTable, initializeDevice, fpl__AudiobackendDirectSoundInitializeDevice),
23399 fplStructField(fplAudioBackendFunctionTable, releaseDevice, fpl__AudiobackendDirectSoundReleaseDevice),
23400 fplStructField(fplAudioBackendFunctionTable, startDevice, fpl__AudioBackendDirectSoundStartDevice),
23401 fplStructField(fplAudioBackendFunctionTable, stopDevice, fpl__AudioBackendDirectSoundStopDevice),
23402 fplStructField(fplAudioBackendFunctionTable, mainLoop, fpl__AudioBackendDirectSoundMainLoop),
23403 fplStructField(fplAudioBackendFunctionTable, stopMainLoop, fpl__AudioBackendDirectSoundStopMainLoop),
23404 }),
23405};
23406
23407#endif // FPL__ENABLE_AUDIO_DIRECTSOUND
23408
23409// ############################################################################
23410//
23411// > AUDIO_BACKEND_ALSA
23412//
23413// ############################################################################
23414#if defined(FPL__ENABLE_AUDIO_ALSA)
23415
23416// @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.
23417typedef struct {
23418 const char *deviceName;
23419 float scale;
23420} fpl__AlsaBufferScale;
23421
23422fpl_globalvar fpl__AlsaBufferScale fpl__globalAlsaBufferScales[] = {
23423 fplStructInit(fpl__AlsaBufferScale, "*bcm2835*", 2.0f),
23424};
23425
23426fpl_internal float fpl__AlsaGetBufferScale(const char *deviceName) {
23427 if (fplGetStringLength(deviceName) > 0) {
23428 for (int i = 0; i < fplArrayCount(fpl__globalAlsaBufferScales); ++i) {
23429 const char *testDeviceName = fpl__globalAlsaBufferScales[i].deviceName;
23430 if (fplIsStringMatchWildcard(deviceName, testDeviceName)) {
23431 float scale = fpl__globalAlsaBufferScales[i].scale;
23432 return(scale);
23433 }
23434 }
23435 }
23436 return(1.0f);
23437}
23438
23439fpl_internal uint32_t fpl__AlsaScaleBufferSize(const uint32_t bufferSize, const float scale) {
23440 uint32_t result = fplMax(1, (uint32_t)(bufferSize * scale));
23441 return(result);
23442}
23443
23444// Sets the default audio channel map a alsa backend
23445fpl_internal void fpl__SetAudioDefaultChannelMapALSA(const uint16_t channels, const fplAudioChannelLayout layout, fplAudioChannelMap *outChannelMap) {
23446 fplClearStruct(outChannelMap);
23447
23448 if (channels == 0 || layout == fplAudioChannelLayout_Unsupported) {
23449 return;
23450 }
23451
23452 if (channels == 1 || layout == fplAudioChannelLayout_Mono) {
23453 outChannelMap->speakers[0] = fplAudioChannelType_FrontCenter;
23454 } else if (channels == 2 || layout == fplAudioChannelLayout_Stereo) {
23455 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23456 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23457 } else if (channels == 3) {
23458 if (layout == fplAudioChannelLayout_2_1) {
23459 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23460 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23461 outChannelMap->speakers[2] = fplAudioChannelType_LowFrequency;
23462 } else {
23463 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23464 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23465 outChannelMap->speakers[2] = fplAudioChannelType_FrontCenter;
23466 }
23467 } else if (channels == 4) {
23468 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23469 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23470 outChannelMap->speakers[2] = fplAudioChannelType_BackLeft;
23471 outChannelMap->speakers[3] = fplAudioChannelType_BackRight;
23472 } else if (channels == 5) {
23473 if (layout == fplAudioChannelLayout_4_1) {
23474 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23475 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23476 outChannelMap->speakers[2] = fplAudioChannelType_BackLeft;
23477 outChannelMap->speakers[3] = fplAudioChannelType_BackRight;
23478 outChannelMap->speakers[4] = fplAudioChannelType_LowFrequency;
23479 } else {
23480 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23481 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23482 outChannelMap->speakers[2] = fplAudioChannelType_BackLeft;
23483 outChannelMap->speakers[3] = fplAudioChannelType_BackRight;
23484 outChannelMap->speakers[4] = fplAudioChannelType_FrontCenter;
23485 }
23486 } else if (channels == 6) {
23487 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23488 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23489 outChannelMap->speakers[2] = fplAudioChannelType_BackLeft;
23490 outChannelMap->speakers[3] = fplAudioChannelType_BackRight;
23491 outChannelMap->speakers[4] = fplAudioChannelType_FrontCenter;
23492 outChannelMap->speakers[5] = fplAudioChannelType_LowFrequency;
23493 } else if (channels == 7) {
23494 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23495 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23496 outChannelMap->speakers[2] = fplAudioChannelType_BackLeft;
23497 outChannelMap->speakers[3] = fplAudioChannelType_BackRight;
23498 outChannelMap->speakers[4] = fplAudioChannelType_FrontCenter;
23499 outChannelMap->speakers[5] = fplAudioChannelType_LowFrequency;
23500 outChannelMap->speakers[6] = fplAudioChannelType_BackCenter;
23501 } else {
23502 fplAssert(channels >= 8);
23503 outChannelMap->speakers[0] = fplAudioChannelType_FrontLeft;
23504 outChannelMap->speakers[1] = fplAudioChannelType_FrontRight;
23505 outChannelMap->speakers[2] = fplAudioChannelType_BackLeft;
23506 outChannelMap->speakers[3] = fplAudioChannelType_BackRight;
23507 outChannelMap->speakers[4] = fplAudioChannelType_FrontCenter;
23508 outChannelMap->speakers[5] = fplAudioChannelType_LowFrequency;
23509 outChannelMap->speakers[6] = fplAudioChannelType_SideLeft;
23510 outChannelMap->speakers[7] = fplAudioChannelType_SideRight;
23511 }
23512}
23513
23514#if defined(FPL__ANONYMOUS_ALSA_HEADERS)
23515typedef void snd_pcm_t;
23516typedef void snd_pcm_format_mask_t;
23517typedef void snd_pcm_hw_params_t;
23518typedef void snd_pcm_sw_params_t;
23519
23520typedef uint64_t snd_pcm_uframes_t;
23521typedef int64_t snd_pcm_sframes_t;
23522
23523typedef struct snd_pcm_chmap_t {
23524 unsigned int channels;
23525 unsigned int pos[0];
23526} snd_pcm_chmap_t;
23527
23528typedef enum snd_pcm_stream_t {
23529 SND_PCM_STREAM_PLAYBACK = 0,
23530 SND_PCM_STREAM_CAPTURE,
23531} snd_pcm_stream_t;
23532
23533typedef struct snd_pcm_channel_area_t {
23534 void *addr;
23535 unsigned int first;
23536 unsigned int step;
23537} snd_pcm_channel_area_t;
23538
23539typedef enum snd_pcm_format_t {
23540 SND_PCM_FORMAT_UNKNOWN = -1,
23541 SND_PCM_FORMAT_S8 = 0,
23542 SND_PCM_FORMAT_U8,
23543 SND_PCM_FORMAT_S16_LE,
23544 SND_PCM_FORMAT_S16_BE,
23545 SND_PCM_FORMAT_U16_LE,
23546 SND_PCM_FORMAT_U16_BE,
23547 SND_PCM_FORMAT_S24_LE,
23548 SND_PCM_FORMAT_S24_BE,
23549 SND_PCM_FORMAT_U24_LE,
23550 SND_PCM_FORMAT_U24_BE,
23551 SND_PCM_FORMAT_S32_LE,
23552 SND_PCM_FORMAT_S32_BE,
23553 SND_PCM_FORMAT_U32_LE,
23554 SND_PCM_FORMAT_U32_BE,
23555 SND_PCM_FORMAT_FLOAT_LE,
23556 SND_PCM_FORMAT_FLOAT_BE,
23557 SND_PCM_FORMAT_FLOAT64_LE,
23558 SND_PCM_FORMAT_FLOAT64_BE,
23559 SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
23560 SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
23561 SND_PCM_FORMAT_MU_LAW,
23562 SND_PCM_FORMAT_A_LAW,
23563 SND_PCM_FORMAT_IMA_ADPCM,
23564 SND_PCM_FORMAT_MPEG,
23565 SND_PCM_FORMAT_GSM,
23566 SND_PCM_FORMAT_S20_LE,
23567 SND_PCM_FORMAT_S20_BE,
23568 SND_PCM_FORMAT_U20_LE,
23569 SND_PCM_FORMAT_U20_BE,
23570 SND_PCM_FORMAT_SPECIAL,
23571 SND_PCM_FORMAT_S24_3LE,
23572 SND_PCM_FORMAT_S24_3BE,
23573 SND_PCM_FORMAT_U24_3LE,
23574 SND_PCM_FORMAT_U24_3BE,
23575 SND_PCM_FORMAT_S20_3LE,
23576 SND_PCM_FORMAT_S20_3BE,
23577 SND_PCM_FORMAT_U20_3LE,
23578 SND_PCM_FORMAT_U20_3BE,
23579 SND_PCM_FORMAT_S18_3LE,
23580 SND_PCM_FORMAT_S18_3BE,
23581 SND_PCM_FORMAT_U18_3LE,
23582 SND_PCM_FORMAT_U18_3BE,
23583 SND_PCM_FORMAT_S16,
23584 SND_PCM_FORMAT_U16,
23585 SND_PCM_FORMAT_S24,
23586 SND_PCM_FORMAT_U24,
23587 SND_PCM_FORMAT_S32,
23588 SND_PCM_FORMAT_U32,
23589 SND_PCM_FORMAT_FLOAT,
23590 SND_PCM_FORMAT_FLOAT64,
23591 SND_PCM_FORMAT_IEC958_SUBFRAME,
23592 SND_PCM_FORMAT_S20,
23593 SND_PCM_FORMAT_U20
23594} snd_pcm_format_t;
23595
23596typedef enum snd_pcm_access_t {
23597 SND_PCM_ACCESS_MMAP_INTERLEAVED = 0,
23598 SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
23599 SND_PCM_ACCESS_MMAP_COMPLEX,
23600 SND_PCM_ACCESS_RW_INTERLEAVED,
23601 SND_PCM_ACCESS_RW_NONINTERLEAVED
23602} snd_pcm_access_t;
23603
23604#define SND_PCM_NO_AUTO_RESAMPLE 0x00010000
23605#define SND_PCM_NO_AUTO_CHANNELS 0x00020000
23606#define SND_PCM_NO_AUTO_FORMAT 0x00040000
23607#else
23608// @TODO(final/ALSA): Remove ALSA include when runtime linking is enabled
23609# include <alsa/asoundlib.h>
23610#endif // FPL__ANONYMOUS_ALSA_HEADERS
23611
23612#define FPL__ALSA_FUNC_snd_pcm_open(name) int name(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
23613typedef FPL__ALSA_FUNC_snd_pcm_open(fpl__alsa_func_snd_pcm_open);
23614#define FPL__ALSA_FUNC_snd_pcm_close(name) int name(snd_pcm_t *pcm)
23615typedef FPL__ALSA_FUNC_snd_pcm_close(fpl__alsa_func_snd_pcm_close);
23616#define FPL__ALSA_FUNC_snd_pcm_hw_params_sizeof(name) size_t name(void)
23617typedef FPL__ALSA_FUNC_snd_pcm_hw_params_sizeof(fpl__alsa_func_snd_pcm_hw_params_sizeof);
23618#define FPL__ALSA_FUNC_snd_pcm_hw_params(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
23619typedef FPL__ALSA_FUNC_snd_pcm_hw_params(fpl__alsa_func_snd_pcm_hw_params);
23620#define FPL__ALSA_FUNC_snd_pcm_hw_params_any(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
23621typedef FPL__ALSA_FUNC_snd_pcm_hw_params_any(fpl__alsa_func_snd_pcm_hw_params_any);
23622#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)
23623typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_format(fpl__alsa_func_snd_pcm_hw_params_set_format);
23624#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)
23625typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_format_first(fpl__alsa_func_snd_pcm_hw_params_set_format_first);
23626#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)
23627typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_format_mask(fpl__alsa_func_snd_pcm_hw_params_get_format_mask);
23628#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)
23629typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_channels_near(fpl__alsa_func_snd_pcm_hw_params_set_channels_near);
23630#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)
23631typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_rate_resample(fpl__alsa_func_snd_pcm_hw_params_set_rate_resample);
23632#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)
23633typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_rate_near(fpl__alsa_func_snd_pcm_hw_params_set_rate_near);
23634#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)
23635typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_buffer_size_near(fpl__alsa_func_snd_pcm_hw_params_set_buffer_size_near);
23636#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)
23637typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_periods_near(fpl__alsa_func_snd_pcm_hw_params_set_periods_near);
23638#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)
23639typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_access(fpl__alsa_func_snd_pcm_hw_params_set_access);
23640#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)
23641typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_format(fpl__alsa_func_snd_pcm_hw_params_get_format);
23642#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_channels(name) int name(const snd_pcm_hw_params_t *params, unsigned int *val)
23643typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_channels(fpl__alsa_func_snd_pcm_hw_params_get_channels);
23644#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)
23645typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_rate(fpl__alsa_func_snd_pcm_hw_params_get_rate);
23646#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)
23647typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_buffer_size(fpl__alsa_func_snd_pcm_hw_params_get_buffer_size);
23648#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)
23649typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_periods(fpl__alsa_func_snd_pcm_hw_params_get_periods);
23650#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)
23651typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_access(fpl__alsa_func_snd_pcm_hw_params_get_access);
23652#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_sbits(name) int name(const snd_pcm_hw_params_t *params)
23653typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_sbits(fpl__alsa_func_snd_pcm_hw_params_get_sbits);
23654#define FPL__ALSA_FUNC_snd_pcm_sw_params_sizeof(name) size_t name(void)
23655typedef FPL__ALSA_FUNC_snd_pcm_sw_params_sizeof(fpl__alsa_func_snd_pcm_sw_params_sizeof);
23656#define FPL__ALSA_FUNC_snd_pcm_sw_params_current(name) int name(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
23657typedef FPL__ALSA_FUNC_snd_pcm_sw_params_current(fpl__alsa_func_snd_pcm_sw_params_current);
23658#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)
23659typedef FPL__ALSA_FUNC_snd_pcm_sw_params_set_avail_min(fpl__alsa_func_snd_pcm_sw_params_set_avail_min);
23660#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)
23661typedef FPL__ALSA_FUNC_snd_pcm_sw_params_set_start_threshold(fpl__alsa_func_snd_pcm_sw_params_set_start_threshold);
23662#define FPL__ALSA_FUNC_snd_pcm_sw_params(name) int name(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
23663typedef FPL__ALSA_FUNC_snd_pcm_sw_params(fpl__alsa_func_snd_pcm_sw_params);
23664#define FPL__ALSA_FUNC_snd_pcm_format_mask_sizeof(name) size_t name(void)
23665typedef FPL__ALSA_FUNC_snd_pcm_format_mask_sizeof(fpl__alsa_func_snd_pcm_format_mask_sizeof);
23666#define FPL__ALSA_FUNC_snd_pcm_format_mask_test(name) int name(const snd_pcm_format_mask_t *mask, snd_pcm_format_t val)
23667typedef FPL__ALSA_FUNC_snd_pcm_format_mask_test(fpl__alsa_func_snd_pcm_format_mask_test);
23668#define FPL__ALSA_FUNC_snd_pcm_get_chmap(name) snd_pcm_chmap_t *name(snd_pcm_t *pcm)
23669typedef FPL__ALSA_FUNC_snd_pcm_get_chmap(fpl__alsa_func_snd_pcm_get_chmap);
23670#define FPL__ALSA_FUNC_snd_pcm_prepare(name) int name(snd_pcm_t *pcm)
23671typedef FPL__ALSA_FUNC_snd_pcm_prepare(fpl__alsa_func_snd_pcm_prepare);
23672#define FPL__ALSA_FUNC_snd_pcm_start(name) int name(snd_pcm_t *pcm)
23673typedef FPL__ALSA_FUNC_snd_pcm_start(fpl__alsa_func_snd_pcm_start);
23674#define FPL__ALSA_FUNC_snd_pcm_drop(name) int name(snd_pcm_t *pcm)
23675typedef FPL__ALSA_FUNC_snd_pcm_drop(fpl__alsa_func_snd_pcm_drop);
23676#define FPL__ALSA_FUNC_snd_device_name_hint(name) int name(int card, const char *iface, void ***hints)
23677typedef FPL__ALSA_FUNC_snd_device_name_hint(fpl__alsa_func_snd_device_name_hint);
23678#define FPL__ALSA_FUNC_snd_device_name_get_hint(name) char *name(const void *hint, const char *id)
23679typedef FPL__ALSA_FUNC_snd_device_name_get_hint(fpl__alsa_func_snd_device_name_get_hint);
23680#define FPL__ALSA_FUNC_snd_device_name_free_hint(name) int name(void **hints)
23681typedef FPL__ALSA_FUNC_snd_device_name_free_hint(fpl__alsa_func_snd_device_name_free_hint);
23682#define FPL__ALSA_FUNC_snd_card_get_index(name) int name(const char *name)
23683typedef FPL__ALSA_FUNC_snd_card_get_index(fpl__alsa_func_snd_card_get_index);
23684#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)
23685typedef FPL__ALSA_FUNC_snd_pcm_mmap_begin(fpl__alsa_func_snd_pcm_mmap_begin);
23686#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)
23687typedef FPL__ALSA_FUNC_snd_pcm_mmap_commit(fpl__alsa_func_snd_pcm_mmap_commit);
23688#define FPL__ALSA_FUNC_snd_pcm_recover(name) int name(snd_pcm_t *pcm, int err, int silent)
23689typedef FPL__ALSA_FUNC_snd_pcm_recover(fpl__alsa_func_snd_pcm_recover);
23690#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)
23691typedef FPL__ALSA_FUNC_snd_pcm_writei(fpl__alsa_func_snd_pcm_writei);
23692#define FPL__ALSA_FUNC_snd_pcm_avail(name) snd_pcm_sframes_t name(snd_pcm_t *pcm)
23693typedef FPL__ALSA_FUNC_snd_pcm_avail(fpl__alsa_func_snd_pcm_avail);
23694#define FPL__ALSA_FUNC_snd_pcm_avail_update(name) snd_pcm_sframes_t name(snd_pcm_t *pcm)
23695typedef FPL__ALSA_FUNC_snd_pcm_avail_update(fpl__alsa_func_snd_pcm_avail_update);
23696#define FPL__ALSA_FUNC_snd_pcm_wait(name) int name(snd_pcm_t *pcm, int timeout)
23697typedef FPL__ALSA_FUNC_snd_pcm_wait(fpl__alsa_func_snd_pcm_wait);
23698#define FPL__ALSA_FUNC_snd_pcm_info_sizeof(name) size_t name(void)
23699typedef FPL__ALSA_FUNC_snd_pcm_info_sizeof(fpl__alsa_func_snd_pcm_info_sizeof);
23700#define FPL__ALSA_FUNC_snd_pcm_info(name) int name(snd_pcm_t *handle, snd_pcm_info_t *info)
23701typedef FPL__ALSA_FUNC_snd_pcm_info(fpl__alsa_func_snd_pcm_info);
23702#define FPL__ALSA_FUNC_snd_pcm_info_get_name(name) const char* name(const snd_pcm_info_t *obj)
23703typedef FPL__ALSA_FUNC_snd_pcm_info_get_name(fpl__alsa_func_snd_pcm_info_get_name);
23704
23705typedef struct {
23706 void *libHandle;
23707 fpl__alsa_func_snd_pcm_open *snd_pcm_open;
23708 fpl__alsa_func_snd_pcm_close *snd_pcm_close;
23709 fpl__alsa_func_snd_pcm_hw_params_sizeof *snd_pcm_hw_params_sizeof;
23710 fpl__alsa_func_snd_pcm_hw_params *snd_pcm_hw_params;
23711 fpl__alsa_func_snd_pcm_hw_params_any *snd_pcm_hw_params_any;
23712 fpl__alsa_func_snd_pcm_hw_params_set_format *snd_pcm_hw_params_set_format;
23713 fpl__alsa_func_snd_pcm_hw_params_set_format_first *snd_pcm_hw_params_set_format_first;
23714 fpl__alsa_func_snd_pcm_hw_params_get_format_mask *snd_pcm_hw_params_get_format_mask;
23715 fpl__alsa_func_snd_pcm_hw_params_set_channels_near *snd_pcm_hw_params_set_channels_near;
23716 fpl__alsa_func_snd_pcm_hw_params_set_rate_resample *snd_pcm_hw_params_set_rate_resample;
23717 fpl__alsa_func_snd_pcm_hw_params_set_rate_near *snd_pcm_hw_params_set_rate_near;
23718 fpl__alsa_func_snd_pcm_hw_params_set_buffer_size_near *snd_pcm_hw_params_set_buffer_size_near;
23719 fpl__alsa_func_snd_pcm_hw_params_set_periods_near *snd_pcm_hw_params_set_periods_near;
23720 fpl__alsa_func_snd_pcm_hw_params_set_access *snd_pcm_hw_params_set_access;
23721 fpl__alsa_func_snd_pcm_hw_params_get_format *snd_pcm_hw_params_get_format;
23722 fpl__alsa_func_snd_pcm_hw_params_get_channels *snd_pcm_hw_params_get_channels;
23723 fpl__alsa_func_snd_pcm_hw_params_get_rate *snd_pcm_hw_params_get_rate;
23724 fpl__alsa_func_snd_pcm_hw_params_get_buffer_size *snd_pcm_hw_params_get_buffer_size;
23725 fpl__alsa_func_snd_pcm_hw_params_get_periods *snd_pcm_hw_params_get_periods;
23726 fpl__alsa_func_snd_pcm_hw_params_get_access *snd_pcm_hw_params_get_access;
23727 fpl__alsa_func_snd_pcm_hw_params_get_sbits *snd_pcm_hw_params_get_sbits;
23728 fpl__alsa_func_snd_pcm_sw_params_sizeof *snd_pcm_sw_params_sizeof;
23729 fpl__alsa_func_snd_pcm_sw_params_current *snd_pcm_sw_params_current;
23730 fpl__alsa_func_snd_pcm_sw_params_set_avail_min *snd_pcm_sw_params_set_avail_min;
23731 fpl__alsa_func_snd_pcm_sw_params_set_start_threshold *snd_pcm_sw_params_set_start_threshold;
23732 fpl__alsa_func_snd_pcm_sw_params *snd_pcm_sw_params;
23733 fpl__alsa_func_snd_pcm_format_mask_sizeof *snd_pcm_format_mask_sizeof;
23734 fpl__alsa_func_snd_pcm_format_mask_test *snd_pcm_format_mask_test;
23735 fpl__alsa_func_snd_pcm_get_chmap *snd_pcm_get_chmap;
23736 fpl__alsa_func_snd_pcm_prepare *snd_pcm_prepare;
23737 fpl__alsa_func_snd_pcm_start *snd_pcm_start;
23738 fpl__alsa_func_snd_pcm_drop *snd_pcm_drop;
23739 fpl__alsa_func_snd_device_name_hint *snd_device_name_hint;
23740 fpl__alsa_func_snd_device_name_get_hint *snd_device_name_get_hint;
23741 fpl__alsa_func_snd_device_name_free_hint *snd_device_name_free_hint;
23742 fpl__alsa_func_snd_card_get_index *snd_card_get_index;
23743 fpl__alsa_func_snd_pcm_mmap_begin *snd_pcm_mmap_begin;
23744 fpl__alsa_func_snd_pcm_mmap_commit *snd_pcm_mmap_commit;
23745 fpl__alsa_func_snd_pcm_recover *snd_pcm_recover;
23746 fpl__alsa_func_snd_pcm_writei *snd_pcm_writei;
23747 fpl__alsa_func_snd_pcm_avail *snd_pcm_avail;
23748 fpl__alsa_func_snd_pcm_avail_update *snd_pcm_avail_update;
23749 fpl__alsa_func_snd_pcm_wait *snd_pcm_wait;
23750 fpl__alsa_func_snd_pcm_info_sizeof *snd_pcm_info_sizeof;
23751 fpl__alsa_func_snd_pcm_info *snd_pcm_info;
23752 fpl__alsa_func_snd_pcm_info_get_name *snd_pcm_info_get_name;
23753} fpl__AlsaAudioApi;
23754
23755typedef struct {
23756 fpl__AlsaAudioApi api;
23757 snd_pcm_t *pcmDevice;
23758 void *intermediaryBuffer;
23759 bool isUsingMMap;
23760 bool breakMainLoop;
23761} fpl__AlsaAudioBackend;
23762
23763fpl_internal void fpl__UnloadAlsaApi(fpl__AlsaAudioApi *alsaApi) {
23764 fplAssert(alsaApi != fpl_null);
23765 if (alsaApi->libHandle != fpl_null) {
23766 dlclose(alsaApi->libHandle);
23767 }
23768 fplClearStruct(alsaApi);
23769}
23770
23771fpl_internal bool fpl__LoadAlsaApi(fpl__AlsaAudioApi *alsaApi) {
23772 fplAssert(alsaApi != fpl_null);
23773 const char *libraryNames[] = {
23774 "libasound.so",
23775 };
23776 bool result = false;
23777 for (uint32_t index = 0; index < fplArrayCount(libraryNames); ++index) {
23778 const char *libName = libraryNames[index];
23779 fplClearStruct(alsaApi);
23780 do {
23781 void *libHandle = fpl_null;
23782 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_AUDIO_ALSA, libHandle, libName);
23783 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_open, snd_pcm_open);
23784 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_close, snd_pcm_close);
23785 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);
23786 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params, snd_pcm_hw_params);
23787 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);
23788 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);
23789 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);
23790 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);
23791 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);
23792 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);
23793 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);
23794 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);
23795 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);
23796 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);
23797 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);
23798 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);
23799 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);
23800 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);
23801 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);
23802 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);
23803 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);
23804 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);
23805 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);
23806 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);
23807 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);
23808 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_sw_params, snd_pcm_sw_params);
23809 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);
23810 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);
23811 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_get_chmap, snd_pcm_get_chmap);
23812 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_prepare, snd_pcm_prepare);
23813 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_start, snd_pcm_start);
23814 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_drop, snd_pcm_drop);
23815 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_device_name_hint, snd_device_name_hint);
23816 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);
23817 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);
23818 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_card_get_index, snd_card_get_index);
23819 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_mmap_begin, snd_pcm_mmap_begin);
23820 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_mmap_commit, snd_pcm_mmap_commit);
23821 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_recover, snd_pcm_recover);
23822 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_writei, snd_pcm_writei);
23823 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_avail, snd_pcm_avail);
23824 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_avail_update, snd_pcm_avail_update);
23825 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_wait, snd_pcm_wait);
23826 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_info_sizeof, snd_pcm_info_sizeof);
23827 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_info, snd_pcm_info);
23828 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);
23829 alsaApi->libHandle = libHandle;
23830 result = true;
23831 } while (0);
23832 if (result) {
23833 break;
23834 }
23835 fpl__UnloadAlsaApi(alsaApi);
23836 }
23837 return(result);
23838}
23839
23840fpl_internal uint32_t fpl__AudioWaitForFramesAlsa(const fplAudioFormat *deviceFormat, fpl__AlsaAudioBackend *backend, bool *requiresRestart) {
23841 fplAssert(deviceFormat != fpl_null && backend != fpl_null);
23842 if (requiresRestart != fpl_null) {
23843 *requiresRestart = false;
23844 }
23845 const fpl__AlsaAudioApi *alsaApi = &backend->api;
23846 uint32_t periodSizeInFrames = deviceFormat->bufferSizeInFrames / deviceFormat->periods;
23847 while (!backend->breakMainLoop) {
23848 snd_pcm_sframes_t framesAvailable = alsaApi->snd_pcm_avail_update(backend->pcmDevice);
23849 if (framesAvailable < 0) {
23850 if (framesAvailable == -EPIPE) {
23851 if (alsaApi->snd_pcm_recover(backend->pcmDevice, framesAvailable, 1) < 0) {
23852 return 0;
23853 }
23854 if (requiresRestart != fpl_null) {
23855 *requiresRestart = true;
23856 }
23857 framesAvailable = alsaApi->snd_pcm_avail_update(backend->pcmDevice);
23858 if (framesAvailable < 0) {
23859 return 0;
23860 }
23861 }
23862 }
23863
23864 // Keep the returned number of samples consistent and based on the period size.
23865 if (framesAvailable >= periodSizeInFrames) {
23866 return periodSizeInFrames;
23867 }
23868
23869 if (framesAvailable < periodSizeInFrames) {
23870 // Less than a whole period is available so keep waiting.
23871 int waitResult = alsaApi->snd_pcm_wait(backend->pcmDevice, -1);
23872 if (waitResult < 0) {
23873 if (waitResult == -EPIPE) {
23874 if (alsaApi->snd_pcm_recover(backend->pcmDevice, waitResult, 1) < 0) {
23875 return 0;
23876 }
23877 if (requiresRestart != fpl_null) {
23878 *requiresRestart = true;
23879 }
23880 }
23881 }
23882 }
23883 }
23884
23885 // We'll get here if the loop was terminated. Just return whatever's available.
23886 snd_pcm_sframes_t framesAvailable = alsaApi->snd_pcm_avail_update(backend->pcmDevice);
23887 if (framesAvailable < 0) {
23888 return 0;
23889 }
23890 return framesAvailable;
23891}
23892
23893fpl_internal bool fpl__GetAudioFramesFromClientAlsa(fplAudioContext *context, fplAudioBackend *backend) {
23894 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
23895 fplAssert(impl != fpl_null);
23896
23897 const fpl__AlsaAudioApi *alsaApi = &impl->api;
23898
23899 if (!fpl__IsAudioDeviceStarted(context) && fpl__AudioGetDeviceState(context) != fpl__AudioDeviceState_Starting) {
23900 return false;
23901 }
23902 if (impl->breakMainLoop) {
23903 return false;
23904 }
23905
23906 if (impl->isUsingMMap) {
23907 // mmap path
23908 bool requiresRestart;
23909 uint32_t framesAvailable = fpl__AudioWaitForFramesAlsa(&backend->internalFormat, impl, &requiresRestart);
23910 if (framesAvailable == 0) {
23911 return false;
23912 }
23913 if (impl->breakMainLoop) {
23914 return false;
23915 }
23916
23917 const snd_pcm_channel_area_t *channelAreas;
23918 snd_pcm_uframes_t mappedOffset;
23919 snd_pcm_uframes_t mappedFrames = framesAvailable;
23920 while (framesAvailable > 0) {
23921 int result = alsaApi->snd_pcm_mmap_begin(impl->pcmDevice, &channelAreas, &mappedOffset, &mappedFrames);
23922 if (result < 0) {
23923 return false;
23924 }
23925 if (mappedFrames > 0) {
23926 void *bufferPtr = (uint8_t *)channelAreas[0].addr + ((channelAreas[0].first + (mappedOffset * channelAreas[0].step)) / 8);
23927 fpl__ReadAudioFramesFromClient(backend, mappedFrames, bufferPtr);
23928 }
23929 result = alsaApi->snd_pcm_mmap_commit(impl->pcmDevice, mappedOffset, mappedFrames);
23930 if (result < 0 || (snd_pcm_uframes_t)result != mappedFrames) {
23931 alsaApi->snd_pcm_recover(impl->pcmDevice, result, 1);
23932 return false;
23933 }
23934 if (requiresRestart) {
23935 if (alsaApi->snd_pcm_start(impl->pcmDevice) < 0) {
23936 return false;
23937 }
23938 }
23939 if (framesAvailable >= mappedFrames) {
23940 framesAvailable -= mappedFrames;
23941 } else {
23942 framesAvailable = 0;
23943 }
23944
23945 }
23946 } else {
23947 // readi/writei path
23948 while (!impl->breakMainLoop) {
23949 uint32_t framesAvailable = fpl__AudioWaitForFramesAlsa(&backend->internalFormat, impl, fpl_null);
23950 if (framesAvailable == 0) {
23951 continue;
23952 }
23953 if (impl->breakMainLoop) {
23954 return false;
23955 }
23956 fpl__ReadAudioFramesFromClient(backend, framesAvailable, impl->intermediaryBuffer);
23957 snd_pcm_sframes_t framesWritten = alsaApi->snd_pcm_writei(impl->pcmDevice, impl->intermediaryBuffer, framesAvailable);
23958 if (framesWritten < 0) {
23959 if (framesWritten == -EAGAIN) {
23960 // Keep trying
23961 continue;
23962 } else if (framesWritten == -EPIPE) {
23963 // Underrun -> Recover and try again
23964 if (alsaApi->snd_pcm_recover(impl->pcmDevice, framesWritten, 1) < 0) {
23965 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to recover device after underrun!");
23966 return false;
23967 }
23968 framesWritten = alsaApi->snd_pcm_writei(impl->pcmDevice, impl->intermediaryBuffer, framesAvailable);
23969 if (framesWritten < 0) {
23970 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to write data to the PCM device!");
23971 return false;
23972 }
23973 // Success
23974 break;
23975 } else {
23976 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to write audio frames from client, error code: %d!", framesWritten);
23977 return false;
23978 }
23979 } else {
23980 // Success
23981 break;
23982 }
23983 }
23984 }
23985 return true;
23986}
23987
23988fpl_internal FPL_AUDIO_BACKEND_STOP_MAIN_LOOP_FUNC(fpl__AudioBackendAlsaStopMainLoop) {
23989 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
23990 fplAssert(impl != fpl_null);
23991
23992 impl->breakMainLoop = true;
23993}
23994
23995fpl_internal FPL_AUDIO_BACKEND_RELEASE_DEVICE_FUNC(fpl__AudioBackendAlsaReleaseDevice) {
23996 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
23997 fplAssert(impl != fpl_null);
23998
23999 fpl__AlsaAudioApi *alsaApi = &impl->api;
24000
24001 if (impl->pcmDevice != fpl_null) {
24002 alsaApi->snd_pcm_close(impl->pcmDevice);
24003 impl->pcmDevice = fpl_null;
24004 }
24005
24006 if (impl->intermediaryBuffer != fpl_null) {
24007 fpl__ReleaseDynamicMemory(impl->intermediaryBuffer);
24008 impl->intermediaryBuffer = fpl_null;
24009 }
24010
24011 return true;
24012}
24013
24014fpl_internal FPL_AUDIO_BACKEND_RELEASE_FUNC(fpl__AudioBackendAlsaRelease) {
24015 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24016 fplAssert(impl != fpl_null);
24017
24018 fpl__AudioBackendAlsaReleaseDevice(context, backend);
24019
24020 fplClearStruct(impl);
24021
24022 return true;
24023}
24024
24025fpl_internal FPL_AUDIO_BACKEND_START_DEVICE_FUNC(fpl__AudioBackendAlsaStartDevice) {
24026 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24027 fplAssert(impl != fpl_null);
24028
24029 const fpl__AlsaAudioApi *alsaApi = &impl->api;
24030
24031 // Prepare the device
24032 if (alsaApi->snd_pcm_prepare(impl->pcmDevice) < 0) {
24033 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to prepare PCM device '%p'!", impl->pcmDevice);
24035 }
24036
24037 // Get initial frames to fill from the client
24038 if (!fpl__GetAudioFramesFromClientAlsa(context, backend)) {
24039 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to get initial audio frames from client!");
24041 }
24042
24043 if (impl->isUsingMMap) {
24044 if (alsaApi->snd_pcm_start(impl->pcmDevice) < 0) {
24045 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to start PCM device '%p'!", impl->pcmDevice);
24047 }
24048 }
24049
24051}
24052
24053fpl_internal FPL_AUDIO_BACKEND_STOP_DEVICE_FUNC(fpl__AudioBackendAlsaStopDevice) {
24054 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24055 fplAssert(impl != fpl_null);
24056
24057 const fpl__AlsaAudioApi *alsaApi = &impl->api;
24058
24059 if (alsaApi->snd_pcm_drop(impl->pcmDevice)) {
24060 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to drop the PCM device '%p'!", impl->pcmDevice);
24061 return false;
24062 }
24063 return true;
24064}
24065
24066fpl_internal snd_pcm_format_t fpl__MapAudioFormatToAlsaFormat(fplAudioFormatType format) {
24067 // @TODO(final): [ALSA] Mapping table from fplAudioFormatType to snd_pcm_format_t here!
24068 bool isBigEndian = fplIsBigEndian();
24069 if (isBigEndian) {
24070 switch (format) {
24072 return SND_PCM_FORMAT_U8;
24074 return SND_PCM_FORMAT_S16_BE;
24076 return SND_PCM_FORMAT_S24_3BE;
24078 return SND_PCM_FORMAT_S32_BE;
24080 return SND_PCM_FORMAT_FLOAT_BE;
24081 default:
24082 return SND_PCM_FORMAT_UNKNOWN;
24083 }
24084 } else {
24085 switch (format) {
24087 return SND_PCM_FORMAT_U8;
24089 return SND_PCM_FORMAT_S16_LE;
24091 return SND_PCM_FORMAT_S24_3LE;
24093 return SND_PCM_FORMAT_S32_LE;
24095 return SND_PCM_FORMAT_FLOAT_LE;
24096 default:
24097 return SND_PCM_FORMAT_UNKNOWN;
24098 }
24099 }
24100}
24101
24102fpl_internal FPL_AUDIO_BACKEND_MAIN_LOOP_FUNC(fpl__AudioBackendAlsaMainLoop) {
24103 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24104 fplAssert(impl != fpl_null);
24105
24106 impl->breakMainLoop = false;
24107 while (!impl->breakMainLoop && fpl__GetAudioFramesFromClientAlsa(context, backend)) {
24108 }
24109}
24110
24111fpl_internal fplAudioFormatType fpl__MapAlsaFormatToAudioFormat(snd_pcm_format_t format) {
24112 // @TODO(final): [ALSA] Mapping table from snd_pcm_format_t to fplAudioFormatType here!
24113 switch (format) {
24114 case SND_PCM_FORMAT_U8:
24115 return fplAudioFormatType_U8;
24116 case SND_PCM_FORMAT_S16_BE:
24117 case SND_PCM_FORMAT_S16_LE:
24119 case SND_PCM_FORMAT_S24_3BE:
24120 case SND_PCM_FORMAT_S24_3LE:
24122 case SND_PCM_FORMAT_S32_BE:
24123 case SND_PCM_FORMAT_S32_LE:
24125 case SND_PCM_FORMAT_FLOAT_BE:
24126 case SND_PCM_FORMAT_FLOAT_LE:
24128 default:
24130 }
24131}
24132
24133fpl_internal FPL_AUDIO_BACKEND_INITIALIZE_FUNC(fpl__AudioBackendAlsaInitialize) {
24134 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24135 fplAssert(impl != fpl_null);
24136
24137# define FPL__ALSA_INIT_ERROR(ret, format, ...) do { \
24138 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, format, ## __VA_ARGS__); \
24139 fpl__AudioBackendAlsaRelease(context, backend); \
24140 return ret; \
24141 } while (0)
24142
24143 // Load ALSA library
24144 fpl__AlsaAudioApi *alsaApi = &impl->api;
24145 if (!fpl__LoadAlsaApi(alsaApi)) {
24146 FPL__ALSA_INIT_ERROR(fplAudioResultType_ApiFailed, "Failed loading ALSA api!");
24147 }
24148
24150#undef FPL__ALSA_INIT_ERROR
24151}
24152
24153fpl_internal FPL_AUDIO_BACKEND_INITIALIZE_DEVICE_FUNC(fpl__AudioBackendAlsaInitializeDevice) {
24154 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24155 fplAssert(impl != fpl_null);
24156
24157 snd_pcm_hw_params_t *hardwareParams = fpl_null;
24158 snd_pcm_sw_params_t *softwareParams = fpl_null;
24159 snd_pcm_info_t *pcmInfo = fpl_null;
24160
24161# define FPL__ALSA_INIT_ERROR(ret, format, ...) do { \
24162 if (pcmInfo != fpl_null) fpl__ReleaseTemporaryMemory(pcmInfo); \
24163 if (softwareParams != fpl_null) fpl__ReleaseTemporaryMemory(softwareParams); \
24164 if (hardwareParams != fpl_null) fpl__ReleaseTemporaryMemory(hardwareParams); \
24165 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, format, ## __VA_ARGS__); \
24166 fpl__AudioBackendAlsaReleaseDevice(context, backend); \
24167 return ret; \
24168 } while (0)
24169
24170 // Test ALSA library
24171 fpl__AlsaAudioApi *alsaApi = &impl->api;
24172 if (alsaApi->libHandle == fpl_null) {
24173 FPL__ALSA_INIT_ERROR(fplAudioResultType_ApiFailed, "ALSA api not loaded!");
24174 }
24175
24176 //
24177 // Open PCM Device
24178 //
24179 fplAudioDeviceInfo internalDevice = fplZeroInit;
24180
24181 const char *alsaDeviceID = fpl_null;
24182 if (targetDevice != fpl_null) {
24183 alsaDeviceID = targetDevice->id.alsa;
24184 }
24185
24186 fplAudioShareMode shareMode = fplGetAudioShareMode(targetFormat->mode);
24187
24188 snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
24189 int openMode = SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
24190 if (alsaDeviceID == fpl_null || fplGetStringLength(alsaDeviceID) == 0) {
24191 const char *defaultDeviceNames[16] = fplZeroInit;
24192 int defaultDeviceCount = 0;
24193 defaultDeviceNames[defaultDeviceCount++] = "default";
24194 if (shareMode != fplAudioShareMode_Exclusive) {
24195 defaultDeviceNames[defaultDeviceCount++] = "dmix";
24196 defaultDeviceNames[defaultDeviceCount++] = "dmix:0";
24197 defaultDeviceNames[defaultDeviceCount++] = "dmix:0,0";
24198 }
24199 defaultDeviceNames[defaultDeviceCount++] = "hw";
24200 defaultDeviceNames[defaultDeviceCount++] = "hw:0";
24201 defaultDeviceNames[defaultDeviceCount++] = "hw:0,0";
24202
24203 bool isDeviceOpen = false;
24204 for (size_t defaultDeviceIndex = 0; defaultDeviceIndex < defaultDeviceCount; ++defaultDeviceIndex) {
24205 const char *defaultDeviceName = defaultDeviceNames[defaultDeviceIndex];
24206 FPL_LOG_DEBUG("ALSA", "Opening PCM audio device '%s'", defaultDeviceName);
24207 if (alsaApi->snd_pcm_open(&impl->pcmDevice, defaultDeviceName, stream, openMode) == 0) {
24208 FPL_LOG_DEBUG("ALSA", "Successfully opened PCM audio device '%s'", defaultDeviceName);
24209 isDeviceOpen = true;
24210 internalDevice.isDefault = true;
24211 fplCopyString(defaultDeviceName, internalDevice.id.alsa, fplArrayCount(internalDevice.id.alsa));
24212 fplCopyString("default", internalDevice.name, fplArrayCount(internalDevice.name));
24213 break;
24214 } else {
24215 FPL_LOG_WARN("ALSA", "Failed opening default PCM audio device '%s'!", defaultDeviceName);
24216 }
24217 }
24218 if (!isDeviceOpen) {
24219 FPL__ALSA_INIT_ERROR(fplAudioResultType_NoDeviceFound, "No PCM audio device found!");
24220 }
24221 } else {
24222 // @TODO(final/ALSA): Do we want to allow device ids to be :%d,%d so we can probe "dmix" and "hw" ?
24223 if (alsaApi->snd_pcm_open(&impl->pcmDevice, alsaDeviceID, stream, openMode) < 0) {
24224 FPL__ALSA_INIT_ERROR(fplAudioResultType_NoDeviceFound, "PCM audio device by id '%s' not found!", alsaDeviceID);
24225 }
24226 internalDevice.isDefault = fplIsStringEqual("default", alsaDeviceID);
24227 fplCopyString(alsaDeviceID, internalDevice.id.alsa, fplArrayCount(internalDevice.id.alsa));
24228 fplCopyString(alsaDeviceID, internalDevice.name, fplArrayCount(internalDevice.name));
24229 }
24230
24231 const char *internalDeviceId = &internalDevice.id.alsa[0];
24232
24233 fplAssert(impl->pcmDevice != fpl_null);
24234 fplAssert(fplGetStringLength(internalDeviceId) > 0);
24235
24236 //
24237 // Query Device Name
24238 //
24239
24240 size_t pcmInfoSize = alsaApi->snd_pcm_info_sizeof();
24241 pcmInfo = (snd_pcm_info_t *)fpl__AllocateTemporaryMemory(pcmInfoSize, 8);
24242 if (pcmInfo == fpl_null) {
24243 FPL__ALSA_INIT_ERROR(fplAudioResultType_OutOfMemory, "Out of stack memory for snd_pcm_info_t!");
24244 }
24245
24246 if (alsaApi->snd_pcm_info(impl->pcmDevice, pcmInfo) == 0) {
24247 const char *pcmName = alsaApi->snd_pcm_info_get_name(pcmInfo);
24248 if (fplGetStringLength(pcmName) > 0) {
24249 fplCopyString(pcmName, internalDevice.name, fplArrayCount(internalDevice.name));
24250 if (fplIsStringEqual("default", pcmName)) {
24251 char **ppDeviceHints;
24252 if (alsaApi->snd_device_name_hint(-1, "pcm", (void ***)&ppDeviceHints) == 0) {
24253 char **ppNextDeviceHint = ppDeviceHints;
24254 while (*ppNextDeviceHint != fpl_null) {
24255 char *hintName = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "NAME");
24256 char *hintDesc = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "DESC");
24257 char *hintIOID = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "IOID");
24258
24259 bool foundDevice = false;
24260 if (hintIOID == fpl_null || fplIsStringEqual(hintIOID, "Output")) {
24261 if (fplIsStringEqual(hintName, pcmName)) {
24262 fplCopyString(hintDesc, internalDevice.name, fplArrayCount(internalDevice.name));
24263 foundDevice = true;
24264 }
24265 }
24266
24267 // Unfortunatly the hint strings are malloced, so we have to free it :(
24268 free(hintName);
24269 free(hintDesc);
24270 free(hintIOID);
24271
24272 ++ppNextDeviceHint;
24273
24274 if (foundDevice) {
24275 break;
24276 }
24277 }
24278
24279 alsaApi->snd_device_name_free_hint((void **)ppDeviceHints);
24280 }
24281 }
24282 }
24283 }
24284
24285 //
24286 // Buffer sizes
24287 //
24288
24289 // Some audio devices have high latency, so using the default buffer size will not work.
24290 // We have to scale the buffer sizes for special devices, such as broadcom audio (Raspberry Pi)
24291 // See fpl__AlsaGetBufferScale for details
24292 // Idea comes from miniaudio, which does the same thing - so the code is almost identically here
24293 //
24294 float bufferSizeScaleFactor = 1.0f;
24295 if ((targetFormat->defaultFields & fplAudioDefaultFields_BufferSize) == fplAudioDefaultFields_BufferSize) {
24296 if (fplGetStringLength(internalDevice.name) > 0) {
24297 bufferSizeScaleFactor = fpl__AlsaGetBufferScale(internalDevice.name);
24298 }
24299 }
24300
24301 //
24302 // Get hardware parameters
24303 //
24304
24305 FPL_LOG_DEBUG("ALSA", "Get hardware parameters from device '%s'", internalDeviceId);
24306 size_t hardwareParamsSize = alsaApi->snd_pcm_hw_params_sizeof();
24307 hardwareParams = (snd_pcm_hw_params_t *)fpl__AllocateTemporaryMemory(hardwareParamsSize, 8);
24308 fplMemoryClear(hardwareParams, hardwareParamsSize);
24309 if (alsaApi->snd_pcm_hw_params_any(impl->pcmDevice, hardwareParams) < 0) {
24310 FPL__ALSA_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed getting hardware parameters from device '%s'!", internalDeviceId);
24311 }
24312 FPL_LOG_DEBUG("ALSA", "Successfullyy got hardware parameters from device '%s'", internalDeviceId);
24313
24314 //
24315 // Access mode (Interleaved MMap or Standard readi/writei)
24316 //
24317 impl->isUsingMMap = false;
24318 if (!audioSettings->alsa.noMMap) {
24319 if (alsaApi->snd_pcm_hw_params_set_access(impl->pcmDevice, hardwareParams, SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {
24320 impl->isUsingMMap = true;
24321 } else {
24322 FPL_LOG_ERROR("ALSA", "Failed setting MMap access mode for device '%s', trying fallback to standard mode!", internalDeviceId);
24323 }
24324 }
24325 if (!impl->isUsingMMap) {
24326 if (alsaApi->snd_pcm_hw_params_set_access(impl->pcmDevice, hardwareParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
24327 FPL__ALSA_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed setting default access mode for device '%s'!", internalDeviceId);
24328 }
24329 }
24330
24331 fplAudioFormat internalFormat = fplZeroInit;
24332
24333 //
24334 // Format
24335 //
24336
24337 // Get all supported formats
24338 size_t formatMaskSize = alsaApi->snd_pcm_format_mask_sizeof();
24339 snd_pcm_format_mask_t *formatMask = (snd_pcm_format_mask_t *)fpl__AllocateTemporaryMemory(formatMaskSize, 8);
24340 fplMemoryClear(formatMask, formatMaskSize);
24341 alsaApi->snd_pcm_hw_params_get_format_mask(hardwareParams, formatMask);
24342
24343 snd_pcm_format_t foundFormat;
24344 snd_pcm_format_t preferredFormat = fpl__MapAudioFormatToAlsaFormat(targetFormat->type);
24345 if (!alsaApi->snd_pcm_format_mask_test(formatMask, preferredFormat)) {
24346 // The required format is not supported. Try a list of default formats.
24347 bool isBigEndian = fplIsBigEndian();
24348 snd_pcm_format_t defaultFormats[] = {
24349 isBigEndian ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE,
24350 isBigEndian ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE,
24351 isBigEndian ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE,
24352 isBigEndian ? SND_PCM_FORMAT_S24_3BE : SND_PCM_FORMAT_S24_3LE,
24353 SND_PCM_FORMAT_U8,
24354 };
24355 foundFormat = SND_PCM_FORMAT_UNKNOWN;
24356 for (size_t defaultFormatIndex = 0; defaultFormatIndex < fplArrayCount(defaultFormats); ++defaultFormatIndex) {
24357 snd_pcm_format_t defaultFormat = defaultFormats[defaultFormatIndex];
24358 if (alsaApi->snd_pcm_format_mask_test(formatMask, defaultFormat)) {
24359 foundFormat = defaultFormat;
24360 break;
24361 }
24362 }
24363 } else {
24364 foundFormat = preferredFormat;
24365 }
24366
24367 fpl__ReleaseTemporaryMemory(formatMask);
24368
24369 if (foundFormat == SND_PCM_FORMAT_UNKNOWN) {
24370 FPL__ALSA_INIT_ERROR(fplAudioResultType_UnsuportedDeviceFormat, "No supported audio format for device '%s' found!", internalDeviceId);
24371 }
24372
24373 if (alsaApi->snd_pcm_hw_params_set_format(impl->pcmDevice, hardwareParams, foundFormat) < 0) {
24374 FPL__ALSA_INIT_ERROR(fplAudioResultType_UnsuportedDeviceFormat, "Failed setting PCM format '%s' for device '%s'!", fplGetAudioFormatName(fpl__MapAlsaFormatToAudioFormat(foundFormat)), internalDeviceId);
24375 }
24376 internalFormat.type = fpl__MapAlsaFormatToAudioFormat(foundFormat);
24377
24378 //
24379 // Channels
24380 //
24381 unsigned int internalChannels = targetFormat->channels;
24382 if (alsaApi->snd_pcm_hw_params_set_channels_near(impl->pcmDevice, hardwareParams, &internalChannels) < 0) {
24383 FPL__ALSA_INIT_ERROR(fplAudioResultType_UnsuportedDeviceFormat, "Failed setting PCM channels '%lu' for device '%s'!", internalChannels, internalDeviceId);
24384 }
24385 internalFormat.channels = internalChannels;
24386 internalFormat.channelLayout = fplGetDefaultAudioChannelLayoutFromChannels(internalChannels);
24387
24388 // Initialize channel map
24389 fpl__SetAudioDefaultChannelMapALSA(internalFormat.channels, internalFormat.channelLayout, outputChannelMap);
24390
24391 //
24392 // Sample rate
24393 //
24394
24395 // @NOTE(final): The caller is responsible to convert to the sample rate FPL expects, so we disable any resampling
24396 alsaApi->snd_pcm_hw_params_set_rate_resample(impl->pcmDevice, hardwareParams, 0);
24397 unsigned int actualSampleRate = targetFormat->sampleRate;
24398 fplAssert(actualSampleRate > 0);
24399 if (alsaApi->snd_pcm_hw_params_set_rate_near(impl->pcmDevice, hardwareParams, &actualSampleRate, 0) < 0) {
24400 FPL__ALSA_INIT_ERROR(fplAudioResultType_UnsuportedDeviceFormat, "Failed setting PCM sample rate '%lu' for device '%s'!", actualSampleRate, internalDeviceId);
24401 }
24402 internalFormat.sampleRate = actualSampleRate;
24403
24404 //
24405 // Buffer size + Scaling
24406 //
24407 snd_pcm_uframes_t actualBufferSize;
24408 if ((targetFormat->defaultFields & fplAudioDefaultFields_BufferSize) == fplAudioDefaultFields_BufferSize) {
24409 actualBufferSize = fpl__AlsaScaleBufferSize(targetFormat->bufferSizeInFrames, bufferSizeScaleFactor);
24410 } else {
24411 actualBufferSize = targetFormat->bufferSizeInFrames;
24412 }
24413 fplAssert(actualBufferSize > 0);
24414 if (alsaApi->snd_pcm_hw_params_set_buffer_size_near(impl->pcmDevice, hardwareParams, &actualBufferSize) < 0) {
24415 FPL__ALSA_INIT_ERROR(fplAudioResultType_DeviceFailure, "Failed setting PCM buffer size '%lu' for device '%s'!", actualBufferSize, internalDeviceId);
24416 }
24417 internalFormat.bufferSizeInFrames = actualBufferSize;
24418 internalFormat.bufferSizeInMilliseconds =fplGetAudioBufferSizeInMilliseconds(internalFormat.sampleRate, internalFormat.bufferSizeInFrames);
24419
24420 uint32_t bufferSizeInBytes = fplGetAudioBufferSizeInBytes(internalFormat.type, internalFormat.channels, internalFormat.bufferSizeInFrames);
24421 fplAssert(bufferSizeInBytes > 0);
24422
24423 //
24424 // Periods
24425 //
24426 uint32_t internalPeriods = targetFormat->periods;
24427 int periodsDir = 0;
24428 if (alsaApi->snd_pcm_hw_params_set_periods_near(impl->pcmDevice, hardwareParams, &internalPeriods, &periodsDir) < 0) {
24429 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed setting PCM periods '%lu' for device '%s'!", internalPeriods, internalDeviceId);
24430 }
24431 internalFormat.periods = internalPeriods;
24432
24433 //
24434 // Hardware parameters
24435 //
24436 if (alsaApi->snd_pcm_hw_params(impl->pcmDevice, hardwareParams) < 0) {
24437 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to install PCM hardware parameters for device '%s'!", internalDeviceId);
24438 }
24439
24440 //
24441 // Software parameters
24442 //
24443 size_t softwareParamsSize = alsaApi->snd_pcm_sw_params_sizeof();
24444 softwareParams = (snd_pcm_sw_params_t *)fpl__AllocateTemporaryMemory(softwareParamsSize, 8);
24445 fplMemoryClear(softwareParams, softwareParamsSize);
24446 if (alsaApi->snd_pcm_sw_params_current(impl->pcmDevice, softwareParams) < 0) {
24447 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to get software parameters for device '%s'!", internalDeviceId);
24448 }
24449 snd_pcm_uframes_t minAvailableFrames = fpl__PrevPowerOfTwo(internalFormat.bufferSizeInFrames / internalFormat.periods);
24450 if (alsaApi->snd_pcm_sw_params_set_avail_min(impl->pcmDevice, softwareParams, minAvailableFrames) < 0) {
24451 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to set software available min frames of '%lu' for device '%s'!", minAvailableFrames, internalDeviceId);
24452 }
24453 if (!impl->isUsingMMap) {
24454 snd_pcm_uframes_t threshold = internalFormat.bufferSizeInFrames / internalFormat.periods;
24455 if (alsaApi->snd_pcm_sw_params_set_start_threshold(impl->pcmDevice, softwareParams, threshold) < 0) {
24456 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to set start threshold of '%lu' for device '%s'!", threshold, internalDeviceId);
24457 }
24458 }
24459 if (alsaApi->snd_pcm_sw_params(impl->pcmDevice, softwareParams) < 0) {
24460 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to install PCM software parameters for device '%s'!", internalDeviceId);
24461 }
24462
24463 if (!impl->isUsingMMap) {
24464 fplAssert(bufferSizeInBytes > 0);
24465 impl->intermediaryBuffer = fpl__AllocateDynamicMemory(bufferSizeInBytes, 16);
24466 if (impl->intermediaryBuffer == fpl_null) {
24467 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed allocating intermediary buffer of size '%lu' for device '%s'!", bufferSizeInBytes, internalDeviceId);
24468 }
24469 }
24470
24471 fpl__ReleaseTemporaryMemory(pcmInfo);
24472 fpl__ReleaseTemporaryMemory(softwareParams);
24473 fpl__ReleaseTemporaryMemory(hardwareParams);
24474
24475 *outputFormat = internalFormat;
24476 *outputDevice = internalDevice;
24477
24479
24480#undef FPL__ALSA_INIT_ERROR
24481}
24482
24483fpl_internal FPL_AUDIO_BACKEND_GET_AUDIO_DEVICE_INFO_FUNC(fpl__AudioBackendALSAGetAudioDeviceInfo) {
24484 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24485 fplAssert(impl != fpl_null);
24486
24487 fpl__AlsaAudioApi *alsaApi = &impl->api;
24488 if (alsaApi->libHandle == fpl_null) {
24489 FPL__WARNING(FPL__MODULE_AUDIO_ALSA, "API is not loaded!"); \
24491 }
24492
24493 //
24494 // Open PCM Device
24495 //
24496 fplAudioDeviceID deviceID = fplZeroInit;
24497 if (targetDevice != fpl_null) {
24498 deviceID = *targetDevice;
24499 }
24500
24502
24503 snd_pcm_t *pcmDevice = fpl_null;
24504
24505 char deviceName[256] = fplZeroInit;
24506 snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
24507 int openMode = SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
24508 if (fplGetStringLength(deviceID.alsa) == 0) {
24509 const char *defaultDeviceNames[16] = fplZeroInit;
24510 int defaultDeviceCount = 0;
24511 defaultDeviceNames[defaultDeviceCount++] = "default";
24512 if (shareMode != fplAudioShareMode_Exclusive) {
24513 defaultDeviceNames[defaultDeviceCount++] = "dmix";
24514 defaultDeviceNames[defaultDeviceCount++] = "dmix:0";
24515 defaultDeviceNames[defaultDeviceCount++] = "dmix:0,0";
24516 }
24517 defaultDeviceNames[defaultDeviceCount++] = "hw";
24518 defaultDeviceNames[defaultDeviceCount++] = "hw:0";
24519 defaultDeviceNames[defaultDeviceCount++] = "hw:0,0";
24520
24521 bool isDeviceOpen = false;
24522 for (size_t defaultDeviceIndex = 0; defaultDeviceIndex < defaultDeviceCount; ++defaultDeviceIndex) {
24523 const char *defaultDeviceName = defaultDeviceNames[defaultDeviceIndex];
24524 FPL_LOG_DEBUG("ALSA", "Opening PCM audio device '%s'", defaultDeviceName);
24525 if (alsaApi->snd_pcm_open(&pcmDevice, defaultDeviceName, stream, openMode) == 0) {
24526 FPL_LOG_DEBUG("ALSA", "Successfully opened PCM audio device '%s'", defaultDeviceName);
24527 isDeviceOpen = true;
24528 fplCopyString(defaultDeviceName, deviceName, fplArrayCount(deviceName));
24529 break;
24530 } else {
24531 FPL_LOG_ERROR("ALSA", "Failed opening PCM audio device '%s'!", defaultDeviceName);
24532 }
24533 }
24534 if (!isDeviceOpen) {
24535 FPL__WARNING(FPL__MODULE_AUDIO_ALSA, "No PCM audio device found!");
24537 }
24538 } else {
24539 const char *forcedDeviceId = deviceID.alsa;
24540 // @TODO(final/ALSA): Do we want to allow device ids to be :%d,%d so we can probe "dmix" and "hw" ?
24541 if (alsaApi->snd_pcm_open(&pcmDevice, forcedDeviceId, stream, openMode) < 0) {
24542 FPL__WARNING(FPL__MODULE_AUDIO_ALSA, "PCM audio device by id '%s' not found!", forcedDeviceId);
24544 }
24545 fplCopyString(forcedDeviceId, deviceName, fplArrayCount(deviceName));
24546 }
24547
24548 if (pcmDevice != fpl_null) {
24549 alsaApi->snd_pcm_close(pcmDevice);
24550 }
24551
24552 // @IMPLEMEMENT(final): [ALSA] Implement audio device info extended
24553
24555}
24556
24557fpl_internal FPL_AUDIO_BACKEND_GET_AUDIO_DEVICES_FUNC(fpl__AudioBackendAlsaGetAudioDevices) {
24558 fpl__AlsaAudioBackend *impl = FPL_GET_AUDIO_BACKEND_IMPL(backend, fpl__AlsaAudioBackend);
24559 fplAssert(impl != fpl_null);
24560
24561 const fpl__AlsaAudioApi *alsaApi = &impl->api;
24562
24563 char **ppDeviceHints;
24564 if (alsaApi->snd_device_name_hint(-1, "pcm", (void ***)&ppDeviceHints) < 0) {
24565 return 0;
24566 }
24567
24568 uint32_t capacityOverflow = 0;
24569 uint32_t result = 0;
24570 char **ppNextDeviceHint = ppDeviceHints;
24571 while (*ppNextDeviceHint != fpl_null) {
24572 char *name = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "NAME");
24573 char *ioid = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "IOID");
24574
24575 // Only allow output or default devices
24576 if (name != fpl_null && (fplIsStringEqual(name, "default") || fplIsStringEqual(name, "pulse") || fplIsStringEqual(ioid, "Output"))) {
24577 if (deviceInfos != fpl_null) {
24578 if (result >= maxDeviceCount) {
24579 ++capacityOverflow;
24580 } else {
24581 fplAudioDeviceInfo *outDeviceInfo = (fplAudioDeviceInfo *)((uint8_t *)deviceInfos + (deviceInfoSize * result));
24582 fplClearStruct(outDeviceInfo);
24583 outDeviceInfo->isDefault = fplIsStringEqual(name, "default");
24584 fplCopyString(name, outDeviceInfo->id.alsa, fplArrayCount(outDeviceInfo->id.alsa));
24585 char *desc = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "DESC");
24586 if (desc != fpl_null) {
24587 fplCopyString(desc, outDeviceInfo->name, fplArrayCount(outDeviceInfo->name));
24588 free(desc);
24589 } else {
24590 fplCopyString(name, outDeviceInfo->name, fplArrayCount(outDeviceInfo->name));
24591 }
24592 }
24593 }
24594 ++result;
24595 }
24596 if (ioid != fpl_null) {
24597 free(ioid);
24598 }
24599 if (name != fpl_null) {
24600 free(name);
24601 }
24602 ++ppNextDeviceHint;
24603 }
24604 alsaApi->snd_device_name_free_hint((void **)ppDeviceHints);
24605 if (capacityOverflow > 0) {
24606 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);
24607 }
24608 return(result);
24609}
24610
24611fpl_globalvar fplAudioBackendDescriptor fpl__global_audioBackendALSADescriptor = {
24612
24613 fplStructField(fplAudioBackendDescriptor, header, {
24614 fplStructField(fplAudioBackendDescriptorHeader, idName, {
24615 fplStructField(fplAudioBackendDescriptorIDName, id, { 0x897c80d9, 0xc251, 0x4d48, { 0x80, 0x6e, 0x1a, 0x5b, 0x89, 0xb0, 0x97, 0x4 } }),
24616 fplStructField(fplAudioBackendDescriptorIDName, name, "ALSA"),
24617 }),
24618 fplStructField(fplAudioBackendDescriptorHeader, type, fplAudioBackendType_Alsa),
24619 fplStructField(fplAudioBackendDescriptorHeader, backendSize, sizeof(fpl__AlsaAudioBackend)),
24620 fplStructField(fplAudioBackendDescriptorHeader, isAsync, false),
24621 fplStructField(fplAudioBackendDescriptorHeader, isValid, true),
24622 }),
24623 fplStructField(fplAudioBackendDescriptor, table, {
24624 fplStructField(fplAudioBackendFunctionTable, initialize, fpl__AudioBackendAlsaInitialize),
24625 fplStructField(fplAudioBackendFunctionTable, release, fpl__AudioBackendAlsaRelease),
24626 fplStructField(fplAudioBackendFunctionTable, getAudioDevices, fpl__AudioBackendAlsaGetAudioDevices),
24627 fplStructField(fplAudioBackendFunctionTable, getAudioDeviceInfo, fpl__AudioBackendALSAGetAudioDeviceInfo),
24628 fplStructField(fplAudioBackendFunctionTable, initializeDevice, fpl__AudioBackendAlsaInitializeDevice),
24629 fplStructField(fplAudioBackendFunctionTable, releaseDevice, fpl__AudioBackendAlsaReleaseDevice),
24630 fplStructField(fplAudioBackendFunctionTable, startDevice, fpl__AudioBackendAlsaStartDevice),
24631 fplStructField(fplAudioBackendFunctionTable, stopDevice, fpl__AudioBackendAlsaStopDevice),
24632 fplStructField(fplAudioBackendFunctionTable, mainLoop, fpl__AudioBackendAlsaMainLoop),
24633 fplStructField(fplAudioBackendFunctionTable, stopMainLoop, fpl__AudioBackendAlsaStopMainLoop),
24634 }),
24635};
24636#endif // FPL__ENABLE_AUDIO_ALSA
24637
24638#endif // FPL__AUDIO_BACKENDS_IMPLEMENTED
24639
24640// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24641//
24642// > SYSTEM_AUDIO_L1 (Audio System, Private Implementation)
24643//
24644// The audio system is based on a stripped down version of "mini_al.h" by David Reid.
24645// See: https://github.com/dr-soft/mini_al
24646//
24647// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24648#if defined(FPL__ENABLE_AUDIO)
24649
24650fpl_internal void fpl__SetupAudioDeviceFormat(const fplAudioFormat *inFormat, fplAudioFormat *outFormat) {
24651 FPL__CheckArgumentNullNoRet(inFormat);
24652 FPL__CheckArgumentNullNoRet(outFormat);
24653
24654 fplClearStruct(outFormat);
24655
24656 // Channels / Layout
24657 if (inFormat->channels > 0 && inFormat->channelLayout != fplAudioChannelLayout_Automatic) {
24658 uint16_t layoutChannelCount = fplGetAudioChannelsFromLayout(inFormat->channelLayout);
24659 uint16_t highestChannelCount = fplMax(layoutChannelCount, inFormat->channels);
24660 outFormat->channels = fplMax(0, fplMin(highestChannelCount, FPL_MAX_AUDIO_CHANNEL_COUNT));
24664 } else if (inFormat->channels > 0 && inFormat->channelLayout == fplAudioChannelLayout_Automatic) {
24665 outFormat->channels = fplMin(inFormat->channels, FPL_MAX_AUDIO_CHANNEL_COUNT);
24668 } else if (inFormat->channels == 0 && inFormat->channelLayout != fplAudioChannelLayout_Automatic) {
24669 uint16_t layoutChannelCount = fplGetAudioChannelsFromLayout(inFormat->channelLayout);
24670 if (layoutChannelCount > FPL_MAX_AUDIO_CHANNEL_COUNT) {
24673 } else {
24674 outFormat->channels = layoutChannelCount;
24675 outFormat->channelLayout = inFormat->channelLayout;
24676 }
24678 } else {
24679 outFormat->channels = 2;
24683 }
24684
24685 // Sample rate
24686 if (inFormat->sampleRate > 0) {
24687 outFormat->sampleRate = inFormat->sampleRate;
24689 } else {
24690 outFormat->sampleRate = FPL__DEFAULT_AUDIO_SAMPLERATE;
24692 }
24693
24694 // Format
24695 if (inFormat->type != fplAudioFormatType_None) {
24696 outFormat->type = inFormat->type;
24698 } else {
24699 outFormat->type = FPL__DEFAULT_AUDIO_FORMAT;
24701 }
24702
24703 // Periods
24704 if (inFormat->periods > 0) {
24705 outFormat->periods = inFormat->periods;
24707 } else {
24708 outFormat->periods = FPL__DEFAULT_AUDIO_PERIODS;
24710 }
24711
24712 // Buffer size
24713 if (inFormat->bufferSizeInFrames > 0) {
24714 outFormat->bufferSizeInFrames = inFormat->bufferSizeInFrames;
24717 } else if (inFormat->bufferSizeInMilliseconds > 0) {
24721 } else {
24722 fplAudioLatencyType latencyType = fplGetAudioLatencyType(inFormat->mode);
24723 outFormat->bufferSizeInMilliseconds = (latencyType == fplAudioLatencyType_Conservative) ? FPL__DEFAULT_AUDIO_BUFFERSIZE_CONSERVATIVE_IN_MSECS : FPL__DEFAULT_AUDIO_BUFFERSIZE_LOWLATENCY_IN_MSECS;
24726 }
24727
24728 // Mode
24729 outFormat->mode = inFormat->mode;
24730}
24731
24732#define FPL__AUDIO_RESULT_TYPE_COUNT FPL__ENUM_COUNT(fplAudioResultType_First, fplAudioResultType_Last)
24735fpl_globalvar const char *fpl__global_audioResultTypeNameTable[] = {
24736 FPL__ENUM_NAME("None", fplAudioResultType_None),
24737 FPL__ENUM_NAME("Success", fplAudioResultType_Success),
24738 FPL__ENUM_NAME("Invalid Arguments", fplAudioResultType_InvalidArguments),
24739 FPL__ENUM_NAME("System not initialized", fplAudioResultType_SystemNotInitialized),
24740 FPL__ENUM_NAME("Audio-Device not initialized", fplAudioResultType_DeviceNotInitialized),
24741 FPL__ENUM_NAME("Audio-Device already stopped", fplAudioResultType_DeviceAlreadyStopped),
24742 FPL__ENUM_NAME("Audio-Device already started", fplAudioResultType_DeviceAlreadyStarted),
24743 FPL__ENUM_NAME("Audio-Device is busy", fplAudioResultType_DeviceBusy),
24744 FPL__ENUM_NAME("Audio-Device failure", fplAudioResultType_DeviceFailure),
24745 FPL__ENUM_NAME("No Audio-Device found", fplAudioResultType_NoDeviceFound),
24746 FPL__ENUM_NAME("No Audio-Device by ID found", fplAudioResultType_DeviceByIdNotFound),
24747 FPL__ENUM_NAME("Api failure", fplAudioResultType_ApiFailed),
24748 FPL__ENUM_NAME("Platform not initialized", fplAudioResultType_PlatformNotInitialized),
24749 FPL__ENUM_NAME("Backend already initialized", fplAudioResultType_BackendAlreadyInitialized),
24750 FPL__ENUM_NAME("No audio backends found", fplAudioResultType_NoBackendsFound),
24751 FPL__ENUM_NAME("Audio format was not set", fplAudioResultType_UnsetAudioFormat),
24752 FPL__ENUM_NAME("Number of audio channels was not set", fplAudioResultType_UnsetAudioChannels),
24753 FPL__ENUM_NAME("Audio sample rate was not set", fplAudioResultType_UnsetAudioSampleRate),
24754 FPL__ENUM_NAME("Audio buffer sizes was not set", fplAudioResultType_UnsetAudioBufferSize),
24755 FPL__ENUM_NAME("Audio device format is unsupported", fplAudioResultType_UnsuportedDeviceFormat),
24756 FPL__ENUM_NAME("No memory available", fplAudioResultType_OutOfMemory),
24757 FPL__ENUM_NAME("Not implemented", fplAudioResultType_NotImplemented),
24758 FPL__ENUM_NAME("Unknown audio failure", fplAudioResultType_Failed),
24759};
24760fplStaticAssert(fplArrayCount(fpl__global_audioResultTypeNameTable) == FPL__AUDIO_RESULT_TYPE_COUNT);
24761
24762#define FPL__AUDIO_BACKEND_TYPE_COUNT FPL__ENUM_COUNT(fplAudioBackendType_First, fplAudioResultType_Last)
24763
24764fpl_globalvar fplAudioBackendType fpl__global_defaultAudioBackendTypes[] = {
24768};
24769
24770fpl_internal uint32_t fpl__GetAudioBackendDescriptors(const uint32_t maxDescriptorCount, const fplAudioSettings *audioSettings, fplAudioBackendDescriptor *outputDescriptors) {
24771 fplAudioBackendType probeTypes[8] = fplZeroInit;
24772
24773 uint32_t propeTypeCount = 0;
24774
24776 if (audioSettings != fpl_null) {
24777 userBackendType = audioSettings->backend;
24778 }
24779
24780 if (userBackendType != fplAudioBackendType_Auto && userBackendType != fplAudioBackendType_None) {
24781 probeTypes[propeTypeCount++] = userBackendType;
24782 }
24783 for (uint32_t i = 0; i < fplArrayCount(fpl__global_defaultAudioBackendTypes); ++i) {
24784 if (userBackendType == fpl__global_defaultAudioBackendTypes[i])
24785 continue;
24786 probeTypes[propeTypeCount++] = fpl__global_defaultAudioBackendTypes[i];
24787 }
24788
24789 uint32_t descriptorCount = 0;
24790
24791 uint32_t result = 0;
24792
24793 for (uint32_t propeTypeIndex = 0; propeTypeIndex < propeTypeCount; ++propeTypeIndex) {
24794 fplAudioBackendType type = probeTypes[propeTypeIndex];
24795 const fplAudioBackendDescriptor *desc = fpl_null;
24796 switch (type) {
24799 continue;
24800
24802#if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
24803 desc = &fpl__global_audioBackendDirectShowDescriptor;
24804#endif
24805
24807#if defined(FPL__ENABLE_AUDIO_ALSA)
24808 desc = &fpl__global_audioBackendALSADescriptor;
24809#endif
24810 break;
24811
24813 // @TODO(final): Get audio backend descriptor from audio settings
24814 break;
24815 }
24816
24817 if (desc == fpl_null || !desc->header.isValid) {
24818 continue;
24819 }
24820
24821 uint32_t outputDescriptorIndex = descriptorCount;
24822 if ((outputDescriptors != fpl_null) || (maxDescriptorCount > 0 && (descriptorCount < maxDescriptorCount))) {
24823 outputDescriptors[outputDescriptorIndex] = *desc;
24824 ++result;
24825 }
24826
24827 ++descriptorCount;
24828 }
24829
24830 if (outputDescriptors == fpl_null) {
24831 return(descriptorCount);
24832 }
24833
24834 return(result);
24835}
24836
24837fpl_internal uint32_t fpl__GetMaxAudioBackendSize(const fplAudioSettings *settings) {
24838 static fplAudioBackendDescriptor descriptors[8];
24839 fplClearStruct(descriptors);
24840 uint32_t count = fpl__GetAudioBackendDescriptors(fplArrayCount(descriptors), settings, descriptors);
24841 uint32_t result = 0;
24842 for (size_t i = 0; i < count; ++i) {
24843 uint32_t size = descriptors[i].header.backendSize;
24844 result = fplMax(result, size);
24845 }
24846 return(result);
24847}
24848
24849fpl_common_api const char *fplGetAudioResultName(const fplAudioResultType type) {
24850 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplAudioResultType_First, fplAudioResultType_Last);
24851 const char *result = fpl__global_audioResultTypeNameTable[index];
24852 return(result);
24853}
24854
24855typedef struct fpl__AudioEvent {
24856 fplMutexHandle mutex;
24857 fplConditionVariable cond;
24858 volatile int32_t signaled;
24859} fpl__AudioEvent;
24860
24861fpl_internal bool fpl__InitAudioEvent(fpl__AudioEvent *ev) {
24862 fplClearStruct(ev);
24863 if (!fplMutexInit(&ev->mutex)) {
24864 return(false);
24865 }
24866 if (!fplConditionInit(&ev->cond)) {
24867 return(false);
24868 }
24869 ev->signaled = 0;
24870 return(true);
24871}
24872
24873fpl_internal void fpl__ReleaseAudioEvent(fpl__AudioEvent *ev) {
24874 fplConditionDestroy(&ev->cond);
24875 fplMutexDestroy(&ev->mutex);
24876}
24877
24878fpl_internal void fpl__WaitForAudioEvent(fpl__AudioEvent *ev) {
24879 fplMutexLock(&ev->mutex);
24880 while (!ev->signaled) {
24881 fplConditionWait(&ev->cond, &ev->mutex, FPL_TIMEOUT_INFINITE);
24882 }
24883 ev->signaled = 0;
24884 fplMutexUnlock(&ev->mutex);
24885}
24886
24887fpl_internal void fpl__SetAudioEvent(fpl__AudioEvent *ev) {
24888 fplMutexLock(&ev->mutex);
24889 ev->signaled = 1;
24890 fplConditionSignal(&ev->cond);
24891 fplMutexUnlock(&ev->mutex);
24892}
24893
24894typedef struct fpl__AudioState {
24895 fpl__CommonAudioState common;
24896
24897 fplMutexHandle lock;
24898 fplThreadHandle *workerThread;
24899 fpl__AudioEvent startEvent;
24900 fpl__AudioEvent stopEvent;
24901 fpl__AudioEvent wakeupEvent;
24902 volatile fplAudioResultType workResult;
24903
24904 fplAudioBackendType backendType;
24905 bool isAsyncBackend;
24906} fpl__AudioState;
24907
24908fpl_internal fpl__AudioState *fpl__GetAudioState(fpl__PlatformAppState *appState) {
24909 fplAssert(appState != fpl_null);
24910 fpl__AudioState *result = fpl_null;
24911 if (appState->audio.mem != fpl_null) {
24912 result = (fpl__AudioState *)appState->audio.mem;
24913 }
24914 return(result);
24915}
24916
24917fpl_internal fplAudioBackend *fpl__GetActiveAudioBackend(fpl__AudioState *audioState) {
24918 if (audioState == fpl_null || audioState->common.backend == fpl_null) {
24919 return fpl_null;
24920 }
24921 return audioState->common.backend;
24922}
24923
24924fpl_internal void fpl__AudioSetDeviceState(fplAudioContext *context, fpl__AudioDeviceState newState) {
24925 fplAtomicStoreU32((volatile uint32_t *)&context->state, (uint32_t)newState);
24926}
24927
24928fpl_internal fpl__AudioDeviceState fpl__AudioGetDeviceState(fplAudioContext *context) {
24929 fpl__AudioDeviceState result = (fpl__AudioDeviceState)fplAtomicLoadU32((volatile uint32_t *)&context->state);
24930 return(result);
24931}
24932
24933fpl_internal bool fpl__IsAudioDeviceInitialized(fplAudioContext *context) {
24934 if (context == fpl_null) {
24935 return false;
24936 }
24937 fpl__AudioDeviceState state = fpl__AudioGetDeviceState(context);
24938 return(state != fpl__AudioDeviceState_Uninitialized);
24939}
24940
24941fpl_internal bool fpl__IsAudioDeviceStarted(fplAudioContext *context) {
24942 if (context == fpl_null) {
24943 return false;
24944 }
24945 fpl__AudioDeviceState state = fpl__AudioGetDeviceState(context);
24946 return(state == fpl__AudioDeviceState_Started);
24947}
24948
24949fpl_internal void fpl__StopAudioDeviceMainLoop(fpl__AudioState *audioState) {
24950 fplAssert(audioState != fpl_null && audioState->common.backend != fpl_null);
24951 fplAssert(audioState->common.funcTable.stopMainLoop != fpl_null);
24952 audioState->common.funcTable.stopMainLoop(&audioState->common.context, audioState->common.backend);
24953}
24954
24955fpl_internal bool fpl__ReleaseAudioDevice(fpl__AudioState *audioState) {
24956 if (audioState == fpl_null) {
24957 return false;
24958 }
24959
24960 fpl__CommonAudioState *commonAudioState = &audioState->common;
24961
24962 fplAudioContext *context = &commonAudioState->context;
24963
24964 fplAudioBackend *backend = commonAudioState->backend;
24965
24966 // Wait until the audio device is stopped
24967 if (fpl__IsAudioDeviceStarted(context)) {
24969 fplThreadSleep(1);
24970 }
24971 }
24972
24973 // Putting the device into an uninitialized state will make the worker thread return.
24974 fpl__AudioSetDeviceState(context, fpl__AudioDeviceState_Uninitialized);
24975
24976 // Wake up the worker thread and wait for it to properly terminate.
24977 fpl__SetAudioEvent(&audioState->wakeupEvent);
24978
24979 fplThreadWaitForOne(audioState->workerThread, FPL_TIMEOUT_INFINITE);
24980 fplThreadTerminate(audioState->workerThread);
24981
24982 // Release signals and thread
24983 fpl__ReleaseAudioEvent(&audioState->stopEvent);
24984 fpl__ReleaseAudioEvent(&audioState->startEvent);
24985 fpl__ReleaseAudioEvent(&audioState->wakeupEvent);
24986 fplMutexDestroy(&audioState->lock);
24987
24988 // Call release device from backend
24989 if (backend != fpl_null && commonAudioState->funcTable.releaseDevice != fpl_null) {
24990 if (!commonAudioState->funcTable.releaseDevice(context, backend)) {
24991 return false;
24992 }
24993 }
24994
24995 return true;
24996}
24997
24998fpl_internal bool fpl__StopAudioDevice(fpl__AudioState *audioState) {
24999 fplAssert(audioState != fpl_null && audioState->common.backend != fpl_null);
25000 fplAssert(audioState->common.funcTable.stopDevice != fpl_null);
25001 bool result = audioState->common.funcTable.stopDevice(&audioState->common.context, audioState->common.backend);
25002 return(result);
25003}
25004
25005fpl_internal fplAudioResultType fpl__StartAudioDevice(fpl__AudioState *audioState) {
25006 fplAssert(audioState != fpl_null && audioState->common.backend != fpl_null);
25007 fplAssert(audioState->common.funcTable.startDevice != fpl_null);
25008 fplAudioResultType result = audioState->common.funcTable.startDevice(&audioState->common.context, audioState->common.backend);
25009 return(result);
25010}
25011
25012fpl_internal void fpl__RunAudioDeviceMainLoop(fpl__AudioState *audioState) {
25013 fplAssert(audioState != fpl_null && audioState->common.backend != fpl_null);
25014 fplAssert(audioState->common.funcTable.mainLoop != fpl_null);
25015 audioState->common.funcTable.mainLoop(&audioState->common.context, audioState->common.backend);
25016}
25017
25018fpl_internal void fpl__AudioWorkerThread(const fplThreadHandle *thread, void *data) {
25019#if defined(FPL_PLATFORM_WINDOWS)
25020 fplAssert(fpl__global__AppState != fpl_null);
25021 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
25022#endif
25023
25024 fpl__AudioState *audioState = (fpl__AudioState *)data;
25025 fplAssert(audioState != fpl_null);
25026 fplAssert(audioState->backendType != fplAudioBackendType_None);
25027
25028 fpl__CommonAudioState *commonAudioState = &audioState->common;
25029
25030 fplAudioContext *context = &commonAudioState->context;
25031
25032#if defined(FPL_PLATFORM_WINDOWS)
25033 wapi->ole.CoInitializeEx(fpl_null, 0);
25034#endif
25035
25036 for (;;) {
25037 // Stop the device at the start of the iteration always
25038 fpl__StopAudioDevice(audioState);
25039
25040 // Let the other threads know that the device has been stopped.
25041 fpl__AudioSetDeviceState(context, fpl__AudioDeviceState_Stopped);
25042 fpl__SetAudioEvent(&audioState->stopEvent);
25043
25044 // We wait until the audio device gets wake up
25045 fpl__WaitForAudioEvent(&audioState->wakeupEvent);
25046
25047 // Default result code.
25048 audioState->workResult = fplAudioResultType_Success;
25049
25050 // Just break if we're terminating.
25051 if (fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Uninitialized) {
25052 break;
25053 }
25054
25055 // Expect that the device is currently be started by the client
25056 fplAssert(fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Starting);
25057
25058 // Start audio device
25059 audioState->workResult = fpl__StartAudioDevice(audioState);
25060 if (audioState->workResult != fplAudioResultType_Success) {
25061 fpl__SetAudioEvent(&audioState->startEvent);
25062 continue;
25063 }
25064
25065 // The audio device is started, mark it as such
25066 fpl__AudioSetDeviceState(context, fpl__AudioDeviceState_Started);
25067 fpl__SetAudioEvent(&audioState->startEvent);
25068
25069 // Enter audio device main loop
25070 fpl__RunAudioDeviceMainLoop(audioState);
25071 }
25072
25073 // Signal to stop any audio threads, in case there are some waiting
25074 fpl__SetAudioEvent(&audioState->stopEvent);
25075
25076#if defined(FPL_PLATFORM_WINDOWS)
25077 wapi->ole.CoUninitialize();
25078#endif
25079}
25080
25081fpl_internal void fpl__ReleaseAudio(fpl__AudioState *audioState) {
25082 fplAssert(audioState != fpl_null);
25083
25084#if defined(FPL_PLATFORM_WINDOWS)
25085 fplAssert(fpl__global__AppState != fpl_null);
25086 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
25087#endif
25088
25089 fpl__CommonAudioState *commonAudioState = &audioState->common;
25090
25091 fplAudioContext *context = &commonAudioState->context;
25092
25093 fplAudioBackend *backend = commonAudioState->backend;
25094
25095 if (fpl__IsAudioDeviceInitialized(context)) {
25096 fpl__ReleaseAudioDevice(audioState);
25097 }
25098
25099 if (backend != fpl_null && commonAudioState->funcTable.release != fpl_null) {
25100 commonAudioState->funcTable.release(context, backend);
25101 }
25102
25103 // Clear audio state
25104 fplClearStruct(audioState);
25105
25106#if defined(FPL_PLATFORM_WINDOWS)
25107 wapi->ole.CoUninitialize();
25108#endif
25109}
25110
25111fpl_globalvar const fplAudioDefaultFields fpl__global_AudioFormat_FallbackFields[] = {
25112 fplAudioDefaultFields_None, // Use all fields
25113
25116
25119
25122
25124};
25125
25126fpl_globalvar const fplAudioFormatType fpl__global_AudioFormat_FallbackTypes[] = {
25132};
25133
25134fpl_globalvar const uint32_t fpl__global_AudioFormat_FallbackSampleRates[] = {
25135 48000,
25136 44100,
25137 32100,
25138 24000,
25139 22050,
25140};
25141
25142fpl_globalvar const uint16_t fpl__global_AudioFormat_FallbackChannels[] = {
25143 2,
25144 1,
25145};
25146
25147fpl_internal size_t fpl__PopulateFallbackAudioFormats(const fplAudioDefaultFields defaultFields, const size_t maxOutputFormatCount, fplAudioFormatU64 *outFormats) {
25148 if (defaultFields == fplAudioDefaultFields_None) {
25149 return 0;
25150 }
25151
25152 const uint32_t channelCount = fplArrayCount(fpl__global_AudioFormat_FallbackChannels);
25153 const uint32_t sampleRateCount = fplArrayCount(fpl__global_AudioFormat_FallbackSampleRates);
25154 const uint32_t typeCount = fplArrayCount(fpl__global_AudioFormat_FallbackTypes);
25155
25156 bool isDefaultChannel = defaultFields & fplAudioDefaultFields_Channels;
25157 bool isDefaultType = defaultFields & fplAudioDefaultFields_Type;
25158 bool isDefaultSampleRate = defaultFields & fplAudioDefaultFields_SampleRate;
25159
25160 size_t requiredCount = 0;
25161 if (isDefaultChannel) {
25162 if (requiredCount == 0)
25163 requiredCount = channelCount;
25164 else
25165 requiredCount = requiredCount * channelCount;
25166 }
25167 if (isDefaultType) {
25168 if (requiredCount == 0)
25169 requiredCount = typeCount;
25170 else
25171 requiredCount = requiredCount * typeCount;
25172 }
25173 if (isDefaultSampleRate) {
25174 if (requiredCount == 0)
25175 requiredCount = sampleRateCount;
25176 else
25177 requiredCount = requiredCount * sampleRateCount;
25178 }
25179
25180 if (outFormats == fpl_null) {
25181 return requiredCount;
25182 }
25183
25184 uint32_t actualChannelCount = isDefaultChannel ? channelCount : 1;
25185 uint32_t actualTypeCount = isDefaultType ? typeCount : 1;
25186 uint32_t actualSampleRateCount = isDefaultSampleRate ? sampleRateCount : 1;
25187
25188 size_t totalCount = 0;
25189 for (uint32_t channelIndex = 0; channelIndex < actualChannelCount; ++channelIndex) {
25190 uint16_t channel = isDefaultChannel ? fpl__global_AudioFormat_FallbackChannels[channelIndex] : 0;
25191 for (uint32_t typeIndex = 0; typeIndex < actualTypeCount; ++typeIndex) {
25192 fplAudioFormatType type = isDefaultType ? fpl__global_AudioFormat_FallbackTypes[typeIndex] : fplAudioFormatType_None;
25193 for (uint32_t sampleRateIndex = 0; sampleRateIndex < actualSampleRateCount; ++sampleRateIndex) {
25194 uint32_t sampleRate = isDefaultSampleRate ? fpl__global_AudioFormat_FallbackSampleRates[sampleRateIndex] : 0;
25195 uint64_t format = fplEncodeAudioFormatU64(sampleRate, channel, type);
25196 if (totalCount < maxOutputFormatCount) {
25197 outFormats[totalCount++] = format;
25198 } else {
25199 break;
25200 }
25201 }
25202 }
25203 }
25204
25205 return totalCount;
25206}
25207
25208fpl_internal fplAudioResultType fpl__InitAudio(const fplAudioSettings *audioSettings, fpl__AudioState *audioState) {
25209 fplAssert(audioState != fpl_null);
25210
25211#if defined(FPL_PLATFORM_WINDOWS)
25212 fplAssert(fpl__global__AppState != fpl_null);
25213 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
25214#endif
25215
25216 if (audioState->backendType != fplAudioBackendType_None) {
25217 fpl__ReleaseAudio(audioState);
25219 }
25220
25221 fpl__PlatformAudioState *platformAudioState = &fpl__global__AppState->audio;
25222 fplAssert(platformAudioState->maxBackendSize > 0);
25223 fplAssert(platformAudioState->offsetToBackend > 0);
25224 fplAssert(platformAudioState->mem != fpl_null);
25225
25226 fpl__CommonAudioState *common = &audioState->common;
25227
25228 fplAudioContext *context = &common->context;
25229
25230 fplAudioBackend *backend = (fplAudioBackend *)((uint8_t *)platformAudioState->mem + platformAudioState->offsetToBackend);
25231 common->backend = backend;
25232 fplAssert(backend != fpl_null);
25233
25234 // Clear backend
25235 fplAssert(backend != fpl_null);
25236 fplMemoryClear(backend, platformAudioState->maxBackendSize);
25237
25238 backend->clientReadCallback = audioSettings->clientReadCallback;
25239 backend->clientUserData = audioSettings->clientUserData;
25240
25241#if defined(FPL_PLATFORM_WINDOWS)
25242 wapi->ole.CoInitializeEx(fpl_null, 0);
25243#endif
25244
25245 static fplAudioBackendDescriptor descriptors[8];
25246
25247 size_t audioBackendCount = fpl__GetAudioBackendDescriptors(fplArrayCount(descriptors), audioSettings, descriptors);
25248
25249 if (audioBackendCount == 0) {
25250 fpl__ReleaseAudio(audioState);
25252 }
25253
25254 fplAudioChannelMap channelsMapping = fplZeroInit;
25255
25256 fplAudioFormat currentTargetFormat = fplZeroInit;
25257
25258 const uint32_t defaultFallbackFieldCount = fplArrayCount(fpl__global_AudioFormat_FallbackFields);
25259
25260 static fplAudioDefaultFields fallbackFields[16] = fplZeroInit;
25261 fplAssert(fplArrayCount(fallbackFields) >= defaultFallbackFieldCount);
25262
25263 static fplAudioFormatU64 testFormats[64] = fplZeroInit;
25264
25266 for (size_t backendIndex = 0; backendIndex < audioBackendCount; ++backendIndex) {
25267 const fplAudioBackendDescriptor *descriptor = &descriptors[backendIndex];
25268
25269 // Initialize the backend
25270 fplAssert(descriptor->header.isValid && descriptor->table.initialize != fpl_null);
25271 resultType = descriptor->table.initialize(context, backend);
25272 if (resultType != fplAudioResultType_Success) {
25273 descriptor->table.release(context, backend);
25274 continue;
25275 }
25276
25277 // Initialize desired format once, so we can add a default field if needed
25278 fpl__SetupAudioDeviceFormat(&audioSettings->targetFormat, &backend->desiredFormat);
25279
25280 uint32_t fallbackFieldCount = 0;
25281 if (backend->desiredFormat.defaultFields != fplAudioDefaultFields_None)
25282 fallbackFields[fallbackFieldCount++] = backend->desiredFormat.defaultFields;
25283 for (uint32_t i = 0; i < fplArrayCount(fpl__global_AudioFormat_FallbackFields); ++i) {
25284 fallbackFields[fallbackFieldCount++] = fpl__global_AudioFormat_FallbackFields[i];
25285 }
25286
25288
25289 uint32_t fallbackFieldIndex = 0;
25290
25291 fplAssert(descriptor->header.isValid && descriptor->table.initializeDevice != fpl_null);
25292 while (resultType != fplAudioResultType_Success && fallbackFieldIndex < fallbackFieldCount) {
25293 fplAudioDefaultFields fallbackFieldsMask = fallbackFields[fallbackFieldIndex];
25294
25295 size_t testFormatCount = fpl__PopulateFallbackAudioFormats(fallbackFieldsMask, fplArrayCount(testFormats) - 1, testFormats);
25296 testFormats[++testFormatCount] = fplEncodeAudioFormatU64(audioSettings->targetFormat.sampleRate, audioSettings->targetFormat.channels, audioSettings->targetFormat.type);
25297
25298 uint32_t currentSampleRate;
25299 uint16_t currentChannels;
25300 fplAudioFormatType currentType;
25301
25302 bool formatFound = false;
25303 for (uint32_t testFormatIndex = 0; testFormatIndex < testFormatCount; ++testFormatIndex) {
25304 fplAudioFormatU64 testFormat = testFormats[testFormatIndex];
25305
25306 currentTargetFormat = audioSettings->targetFormat;
25307 currentTargetFormat.defaultFields = fplAudioDefaultFields_None;
25308
25309 if (fplDecodeAudioFormatU64(testFormat, &currentSampleRate, &currentChannels, &currentType)) {
25310 if (fallbackFieldsMask & fplAudioDefaultFields_Channels) {
25311 currentTargetFormat.channels = currentChannels;
25312 currentTargetFormat.channelLayout = fplGetDefaultAudioChannelLayoutFromChannels(currentChannels);
25313 }
25314 if (fallbackFieldsMask & fplAudioDefaultFields_Type) {
25315 currentTargetFormat.type = currentType;
25316 }
25317 if (fallbackFieldsMask & fplAudioDefaultFields_SampleRate) {
25318 currentTargetFormat.sampleRate = currentSampleRate;
25319 }
25320 }
25321
25322 backend->internalDevice = audioSettings->targetDevice;
25323 fpl__SetupAudioDeviceFormat(&currentTargetFormat, &backend->desiredFormat);
25324
25325 const char *formatTypeName = fplGetAudioFormatName(backend->desiredFormat.type);
25326
25327 FPL_LOG_DEBUG(FPL__MODULE_AUDIO, "Initializing audio device with settings (SampleRate=%u, Channels=%u, Type='%s')", backend->desiredFormat.sampleRate, backend->desiredFormat.channels, formatTypeName);
25328 fplClearStruct(&backend->internalFormat);
25329 resultType = descriptor->table.initializeDevice(context, backend, &audioSettings->specific, &backend->desiredFormat, &audioSettings->targetDevice, &backend->internalFormat, &backend->internalDevice, &channelsMapping);
25330 if (resultType != fplAudioResultType_Success) {
25331 const char *resultErrorStr = fplGetAudioResultName(resultType);
25332 FPL_LOG_WARN(FPL__MODULE_AUDIO, "Failed initializing audio device with settings (SampleRate=%u, Channels=%u, Type='%s') -> %s", backend->desiredFormat.sampleRate, backend->desiredFormat.channels, formatTypeName, resultErrorStr);
25333 descriptor->table.releaseDevice(context, backend);
25334 } else {
25335 FPL_LOG_DEBUG(FPL__MODULE_AUDIO, "Successfully initialized audio device with settings (SampleRate=%u, Channels=%u, Type='%s')", backend->desiredFormat.sampleRate, backend->desiredFormat.channels, formatTypeName);
25336 audioState->common.funcTable = descriptor->table;
25337 audioState->common.channelsMapping = channelsMapping;
25338 audioState->backendType = descriptor->header.type;
25339 audioState->isAsyncBackend = descriptor->header.isAsync;
25340 break;
25341 }
25342
25343 }
25344
25345 ++fallbackFieldIndex;
25346 }
25347
25348 if(resultType == fplAudioResultType_Success)
25349 break;
25350 }
25351
25352 if (resultType != fplAudioResultType_Success) {
25353 const char *resultErrorStr = fplGetAudioResultName(resultType);
25354 FPL_LOG_ERROR(FPL__MODULE_AUDIO, "Either no backend was found or the specified audio format is not supported -> %s", resultErrorStr);
25355 fpl__ReleaseAudio(audioState);
25356 return resultType;
25357 }
25358
25359 fpl__global__AppState->currentSettings.audio.backend = audioState->backendType;
25360 fplMemoryCopy(&backend->internalFormat, sizeof(fplAudioFormat), &fpl__global__AppState->currentSettings.audio.targetFormat);
25361 fpl__global__AppState->currentSettings.audio.clientReadCallback = audioState->common.backend->clientReadCallback;
25362 fpl__global__AppState->currentSettings.audio.clientUserData = audioState->common.backend->clientUserData;
25363
25364 // Create mutex and signals
25365 if (!fplMutexInit(&audioState->lock)) {
25366 fpl__ReleaseAudio(audioState);
25368 }
25369 if (!fpl__InitAudioEvent(&audioState->wakeupEvent)) {
25370 fpl__ReleaseAudio(audioState);
25372 }
25373 if (!fpl__InitAudioEvent(&audioState->startEvent)) {
25374 fpl__ReleaseAudio(audioState);
25376 }
25377 if (!fpl__InitAudioEvent(&audioState->stopEvent)) {
25378 fpl__ReleaseAudio(audioState);
25380 }
25381
25382 if (!audioState->isAsyncBackend) {
25383 // Create and start worker thread
25384 fplThreadParameters audioThreadParams = fplZeroInit;
25385 audioThreadParams.priority = fplThreadPriority_RealTime;
25386 audioThreadParams.runFunc = fpl__AudioWorkerThread;
25387 audioThreadParams.userData = audioState;
25388 audioState->workerThread = fplThreadCreateWithParameters(&audioThreadParams);
25389 if (audioState->workerThread == fpl_null) {
25390 fpl__ReleaseAudio(audioState);
25392 }
25393 // Change to realtime thread
25394 fplSetThreadPriority(audioState->workerThread, fplThreadPriority_RealTime);
25395 // Wait for the worker thread to put the device into the stopped state.
25396 fpl__WaitForAudioEvent(&audioState->stopEvent);
25397 } else {
25398 fpl__AudioSetDeviceState(context, fpl__AudioDeviceState_Stopped);
25399 }
25400
25401 fplAssert(fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Stopped);
25402
25404}
25405#endif // FPL__ENABLE_AUDIO
25406
25407// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25408//
25409// > SYSTEM_VIDEO_L1 (Video System, Private Implementation)
25410//
25411// The audio system is based on a stripped down version of "mini_al.h" by David Reid.
25412// See: https://github.com/dr-soft/mini_al
25413//
25414// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25415#if defined(FPL__ENABLE_VIDEO)
25416
25417typedef union fpl__ActiveVideoBackend {
25418 fpl__VideoBackend base;
25419
25420#if defined(FPL_PLATFORM_WINDOWS)
25421# if defined(FPL__ENABLE_VIDEO_OPENGL)
25422 fpl__VideoBackendWin32OpenGL win32_opengl;
25423# endif
25424# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
25425 fpl__VideoBackendWin32Software win32_software;
25426# endif
25427#endif
25428
25429#if defined(FPL_SUBPLATFORM_X11)
25430# if defined(FPL__ENABLE_VIDEO_OPENGL)
25431 fpl__VideoBackendX11OpenGL x11_opengl;
25432# endif
25433# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
25434 fpl__VideoBackendX11Software x11_software;
25435# endif
25436#endif
25437
25438#if defined(FPL__ENABLE_VIDEO_VULKAN)
25439 fpl__VideoBackendVulkan vulkan;
25440#endif
25441
25442} fpl__ActiveVideoBackend;
25443
25444typedef struct fpl__VideoState {
25445 fpl__VideoContext context;
25446 fpl__VideoData data;
25447 fplVideoBackendType backendType;
25448 fpl__ActiveVideoBackend activeBackend;
25449} fpl__VideoState;
25450
25451fpl_internal fpl__VideoState *fpl__GetVideoState(fpl__PlatformAppState *appState) {
25452 fplAssert(appState != fpl_null);
25453 fpl__VideoState *result = fpl_null;
25454 if (appState->video.mem != fpl_null) {
25455 result = (fpl__VideoState *)appState->video.mem;
25456 }
25457 return(result);
25458}
25459
25460fpl_internal void fpl__DestroySurfaceBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
25461 const fpl__VideoContext *ctx = &videoState->context;
25462 fplAssert(ctx->destroyedWindowFunc != fpl_null);
25463 ctx->destroyedWindowFunc(appState, &videoState->activeBackend.base);
25464}
25465
25466fpl_internal void fpl__UnloadVideoBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
25467 const fpl__VideoContext *ctx = &videoState->context;
25468 fplAssert(ctx->unloadFunc != fpl_null);
25469 ctx->unloadFunc(appState, &videoState->activeBackend.base);
25470 fplClearStruct(videoState);
25471}
25472
25473fpl_internal bool fpl__LoadVideoBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
25474 const fpl__VideoContext *ctx = &videoState->context;
25475 fplAssert(ctx->loadFunc != fpl_null);
25476 bool result = ctx->loadFunc(appState, &videoState->activeBackend.base);
25477 return(result);
25478}
25479
25480fpl_internal void fpl__ShutdownVideoBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
25481 fplAssert(appState != fpl_null);
25482 if (videoState != fpl_null) {
25483 const fpl__VideoContext *ctx = &videoState->context;
25484 fplAssert(ctx->shutdownFunc != fpl_null);
25485 ctx->shutdownFunc(appState, &appState->window, &videoState->activeBackend.base);
25486
25487# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
25488 fplVideoBackBuffer *backbuffer = &videoState->data.backbuffer;
25489 if (backbuffer->pixels != fpl_null) {
25490 fpl__ReleaseDynamicMemory(backbuffer->pixels);
25491 }
25492 fplClearStruct(backbuffer);
25493# endif
25494 }
25495}
25496
25497fpl_internal bool fpl__InitializeVideoBackend(const fplVideoBackendType backendType, const fplVideoSettings *videoSettings, const uint32_t windowWidth, const uint32_t windowHeight, fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
25498 // @NOTE(final): video backends are platform independent, so we cannot have to same system as audio.
25499 fplAssert(appState != fpl_null);
25500 fplAssert(videoState != fpl_null);
25501
25502 const fpl__VideoContext *ctx = &videoState->context;
25503
25504 // Allocate backbuffer context if needed
25505# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
25506 if (backendType == fplVideoBackendType_Software) {
25507 fplVideoBackBuffer *backbuffer = &videoState->data.backbuffer;
25508 backbuffer->width = windowWidth;
25509 backbuffer->height = windowHeight;
25510 backbuffer->pixelStride = sizeof(uint32_t);
25511 backbuffer->lineWidth = backbuffer->width * backbuffer->pixelStride;
25512 size_t size = backbuffer->lineWidth * backbuffer->height;
25513 backbuffer->pixels = (uint32_t *)fpl__AllocateDynamicMemory(size, 4);
25514 if (backbuffer->pixels == fpl_null) {
25515 FPL__ERROR(FPL__MODULE_VIDEO_SOFTWARE, "Failed allocating video software backbuffer of size %xu bytes", size);
25516 fpl__ShutdownVideoBackend(appState, videoState);
25517 return false;
25518 }
25519
25520 // Clear to black by default
25521 // @NOTE(final): Bitmap is top-down, 0xAABBGGRR
25522 uint32_t *p = backbuffer->pixels;
25523 uint32_t color = appState->initSettings.window.background.value == 0 ? 0xFF000000 : appState->initSettings.window.background.value;
25524 for (uint32_t y = 0; y < backbuffer->height; ++y) {
25525 for (uint32_t x = 0; x < backbuffer->width; ++x) {
25526 *p++ = color;
25527 }
25528 }
25529 }
25530# endif // FPL__ENABLE_VIDEO_SOFTWARE
25531
25532 fplAssert(ctx->initializeFunc != fpl_null);
25533 bool videoInitResult = ctx->initializeFunc(appState, &appState->window, videoSettings, &videoState->data, &videoState->activeBackend.base);
25534 if (!videoInitResult) {
25536 fpl__ShutdownVideoBackend(appState, videoState);
25537 return false;
25538 }
25539
25540 return true;
25541}
25542
25543fpl_internal fpl__VideoContext fpl__ConstructVideoContext(const fplVideoBackendType backendType) {
25544 switch (backendType) {
25545#if defined(FPL__ENABLE_VIDEO_OPENGL)
25547 {
25548# if defined(FPL_PLATFORM_WINDOWS)
25549 return fpl__VideoBackend_Win32OpenGL_Construct();
25550# elif defined(FPL_SUBPLATFORM_X11)
25551 return fpl__VideoBackend_X11OpenGL_Construct();
25552# endif
25553 } break;
25554#endif
25555
25556#if defined(FPL__ENABLE_VIDEO_VULKAN)
25558 {
25559 return fpl__VideoBackend_Vulkan_Construct();
25560 } break;
25561#endif
25562
25563#if defined(FPL__ENABLE_VIDEO_SOFTWARE)
25565 {
25566# if defined(FPL_PLATFORM_WINDOWS)
25567 return fpl__VideoBackend_Win32Software_Construct();
25568# elif defined(FPL_SUBPLATFORM_X11)
25569 return fpl__VideoBackend_X11Software_Construct();
25570# endif
25571 } break;
25572#endif
25573
25574 default:
25575 {
25576 // No backend found, just return a stub
25577 FPL__ERROR(FPL__MODULE_VIDEO, "The video backend '%s' is not supported for this platform", fplGetVideoBackendName(backendType));
25578 return(fpl__StubVideoContext());
25579 } break;
25580 }
25581}
25582#endif // FPL__ENABLE_VIDEO
25583
25584// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25585//
25586// > SYSTEM_WINDOW (Window System, Private Implementation)
25587//
25588// - Init window
25589// - Release window
25590//
25591// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25592#if defined(FPL__ENABLE_WINDOW)
25593fpl_internal FPL__FUNC_PREPARE_VIDEO_WINDOW(fpl__PrepareVideoWindowDefault) {
25594 fplAssert(appState != fpl_null);
25595
25596# if defined(FPL__ENABLE_VIDEO)
25597 if (initFlags & fplInitFlags_Video) {
25598 fpl__VideoState *videoState = fpl__GetVideoState(appState);
25599 if (videoState->context.prepareWindowFunc != fpl_null) {
25600 bool result = videoState->context.prepareWindowFunc(appState, &initSettings->video, &appState->window, &videoState->activeBackend.base);
25601 return(result);
25602 }
25603 }
25604# endif // FPL__ENABLE_VIDEO
25605
25606 return(true);
25607}
25608
25609fpl_internal FPL__FUNC_FINALIZE_VIDEO_WINDOW(fpl__FinalizeVideoWindowDefault) {
25610 fplAssert(appState != fpl_null);
25611
25612#if defined(FPL__ENABLE_VIDEO)
25613 if (initFlags & fplInitFlags_Video) {
25614 fpl__VideoState *videoState = fpl__GetVideoState(appState);
25615 if (videoState->context.finalizeWindowFunc != fpl_null) {
25616 bool result = videoState->context.finalizeWindowFunc(appState, &initSettings->video, &appState->window, &videoState->activeBackend.base);
25617 return(result);
25618 }
25619 }
25620#endif // FPL__ENABLE_VIDEO
25621
25622 return true;
25623}
25624
25625fpl_internal void fpl__ReleaseWindow(const fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
25626 if (appState != fpl_null) {
25627# if defined(FPL_PLATFORM_WINDOWS)
25628 fpl__Win32ReleaseWindow(&initState->win32, &appState->win32, &appState->window.win32);
25629# elif defined(FPL_SUBPLATFORM_X11)
25630 fpl__X11ReleaseWindow(&appState->x11, &appState->window.x11);
25631# endif
25632 }
25633}
25634
25635fpl_internal bool fpl__InitWindow(const fplSettings *initSettings, fplWindowSettings *currentWindowSettings, fpl__PlatformAppState *appState, const fpl__SetupWindowCallbacks *setupCallbacks) {
25636 bool result = false;
25637 if (appState != fpl_null) {
25638# if defined(FPL_PLATFORM_WINDOWS)
25639 result = fpl__Win32InitWindow(initSettings, currentWindowSettings, appState, &appState->win32, &appState->window.win32, setupCallbacks);
25640# elif defined(FPL_SUBPLATFORM_X11)
25641 result = fpl__X11InitWindow(initSettings, currentWindowSettings, appState, &appState->x11, &appState->window.x11, setupCallbacks);
25642# endif
25643 }
25644 return (result);
25645}
25646#endif // FPL__ENABLE_WINDOW
25647
25648// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25649//
25650// > SYSTEM_AUDIO_L2 (Audio System, Public Implementation)
25651//
25652// - Stop audio
25653// - Play audio
25654//
25655// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25656#if defined(FPL__ENABLE_AUDIO)
25657
25658#define FPL__AUDIOFORMATTYPE_COUNT FPL__ENUM_COUNT(fplAudioFormatType_First, fplAudioFormatType_Last)
25661
25662fpl_globalvar uint32_t fpl__globalAudioFormatSampleSizeTable[] = {
25663 FPL__ENUM_NAME(0, fplAudioFormatType_None),
25664 FPL__ENUM_NAME(1, fplAudioFormatType_U8),
25665 FPL__ENUM_NAME(2, fplAudioFormatType_S16),
25666 FPL__ENUM_NAME(3, fplAudioFormatType_S24),
25667 FPL__ENUM_NAME(4, fplAudioFormatType_S32),
25668 FPL__ENUM_NAME(8, fplAudioFormatType_S64),
25669 FPL__ENUM_NAME(4, fplAudioFormatType_F32),
25670 FPL__ENUM_NAME(8, fplAudioFormatType_F64),
25671};
25672fplStaticAssert(fplArrayCount(fpl__globalAudioFormatSampleSizeTable) == FPL__AUDIOFORMATTYPE_COUNT);
25673
25675 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(format, fplAudioFormatType_First, fplAudioFormatType_Last);
25676 uint32_t result = fpl__globalAudioFormatSampleSizeTable[index];
25677 return(result);
25678}
25679
25680fpl_globalvar const char *fpl__globalAudioFormatNameTable[] = {
25681 FPL__ENUM_NAME("None", fplAudioFormatType_None),
25682 FPL__ENUM_NAME("U8", fplAudioFormatType_U8),
25683 FPL__ENUM_NAME("S16", fplAudioFormatType_S16),
25684 FPL__ENUM_NAME("S24", fplAudioFormatType_S24),
25685 FPL__ENUM_NAME("S32", fplAudioFormatType_S32),
25686 FPL__ENUM_NAME("S64", fplAudioFormatType_S64),
25687 FPL__ENUM_NAME("F32", fplAudioFormatType_F32),
25688 FPL__ENUM_NAME("F64", fplAudioFormatType_F64),
25689};
25690fplStaticAssert(fplArrayCount(fpl__globalAudioFormatNameTable) == FPL__AUDIOFORMATTYPE_COUNT);
25691
25693 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(format, fplAudioFormatType_First, fplAudioFormatType_Last);
25694 const char *result = fpl__globalAudioFormatNameTable[index];
25695 return(result);
25696}
25697
25698#define FPL__AUDIOBACKENDTYPE_COUNT FPL__ENUM_COUNT(fplAudioBackendType_First, fplAudioBackendType_Last)
25701fpl_globalvar const char *fpl__globalAudioBackendNameTable[FPL__AUDIOBACKENDTYPE_COUNT] = {
25702 FPL__ENUM_NAME("None", fplAudioBackendType_None),
25703 FPL__ENUM_NAME("Automatic", fplAudioBackendType_Auto),
25704 FPL__ENUM_NAME("DirectSound", fplAudioBackendType_DirectSound),
25705 FPL__ENUM_NAME("ALSA", fplAudioBackendType_Alsa),
25706 FPL__ENUM_NAME("Custom", fplAudioBackendType_Custom),
25707};
25708fplStaticAssert(fplArrayCount(fpl__globalAudioBackendNameTable) == FPL__AUDIOBACKENDTYPE_COUNT);
25709
25711 FPL__CheckPlatform(fplAudioBackendType_None);
25712 const fpl__PlatformAppState *appState = fpl__global__AppState;
25713 fplAudioBackendType result = appState->currentSettings.audio.backend;
25714 return(result);
25715}
25716
25717fpl_common_api const char *fplGetAudioBackendName(const fplAudioBackendType backendType) {
25718 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(backendType, fplAudioBackendType_First, fplAudioBackendType_Last);
25719 const char *result = fpl__globalAudioBackendNameTable[index];
25720 return(result);
25721}
25722
25723fpl_common_api uint32_t fplGetAudioBufferSizeInFrames(const uint32_t sampleRate, const uint32_t bufferSizeInMilliSeconds) {
25724 if (sampleRate == 0 || bufferSizeInMilliSeconds == 0) return(0);
25725 uint32_t result = bufferSizeInMilliSeconds * sampleRate / 1000UL;
25726 return(result);
25727}
25728
25729fpl_common_api uint32_t fplGetAudioBufferSizeInMilliseconds(const uint32_t sampleRate, const uint32_t frameCount) {
25730 if (sampleRate == 0 || frameCount == 0) return(0);
25731 uint32_t result = frameCount * 1000UL / sampleRate;
25732 return(result);
25733}
25734
25735fpl_common_api uint32_t fplGetAudioFrameSizeInBytes(const fplAudioFormatType format, const uint16_t channelCount) {
25736 if (channelCount == 0) return(0);
25737 uint32_t result = fplGetAudioSampleSizeInBytes(format) * channelCount;
25738 return(result);
25739}
25740
25741fpl_common_api uint32_t fplGetAudioBufferSizeInBytes(const fplAudioFormatType format, const uint16_t channelCount, const uint32_t frameCount) {
25742 if (channelCount == 0 || frameCount == 0) return(0);
25743 uint32_t frameSize = fplGetAudioFrameSizeInBytes(format, channelCount);
25744 uint32_t result = frameSize * frameCount;
25745 return(result);
25746}
25747
25748fpl_common_api uint32_t fplGetTargetAudioFrameCount(const uint32_t inputFrameCount, const uint32_t inputSampleRate, const uint32_t outputSampleRate) {
25749 if (inputFrameCount == 0 || inputSampleRate == 0 || outputSampleRate == 0) {
25750 return 0;
25751 }
25752 if (inputSampleRate == outputSampleRate) {
25753 return inputFrameCount;
25754 }
25755
25756 float inRatio = inputSampleRate / (float)outputSampleRate;
25757 float outRatio = 1.0f / inRatio;
25758
25759 float ratio;
25760
25761 if (inputSampleRate > outputSampleRate) {
25762 ratio = inRatio;
25763 } else {
25764 fplAssert(outputSampleRate > inputSampleRate);
25765 ratio = outRatio;
25766 }
25767
25768 float f = inputFrameCount * ratio;
25769 uint32_t full = (uint32_t)f;
25770 float fraction = full - f;
25771
25772 // @TODO(final): This is not correct to fake round the additional frame, but it works for most cases
25773 uint32_t add = fraction != 0 ? (uint32_t)(fraction + 0.5f) : 0;
25774
25775 uint32_t result = full + add;
25776
25777 return result;
25778}
25779
25780#define FPL__AUDIO_CHANNEL_LAYOUT_COUNT FPL__ENUM_COUNT(fplAudioChannelLayout_First, fplAudioChannelLayout_Last)
25781
25784fpl_globalvar fplAudioChannelLayout fpl__global_AudioChannelCount_To_AudioChannelLayout_Table[] = {
25785 FPL__ENUM_NAME(fplAudioChannelLayout_Unsupported, 0),
25786 FPL__ENUM_NAME(fplAudioChannelLayout_Mono, 1),
25787 FPL__ENUM_NAME(fplAudioChannelLayout_Stereo, 2),
25788 FPL__ENUM_NAME(fplAudioChannelLayout_3_0_Surround, 3),
25789 FPL__ENUM_NAME(fplAudioChannelLayout_4_0_Quad, 4),
25790 FPL__ENUM_NAME(fplAudioChannelLayout_5_0_Surround, 5),
25791 FPL__ENUM_NAME(fplAudioChannelLayout_5_1, 6),
25792 FPL__ENUM_NAME(fplAudioChannelLayout_6_1, 7),
25793 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 8),
25794 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 9),
25795 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 10),
25796 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 11),
25797 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 12),
25798 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 13),
25799 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 14),
25800 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 15),
25801 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 16),
25802 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 17),
25803 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 18),
25804 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 19),
25805 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 20),
25806 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 21),
25807 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 22),
25808 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 23),
25809 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 24),
25810 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 25),
25811 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 26),
25812 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 27),
25813 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 28),
25814 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 29),
25815 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 30),
25816 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 31),
25817 FPL__ENUM_NAME(fplAudioChannelLayout_7_1, 32),
25818};
25819fplStaticAssert(fplArrayCount(fpl__global_AudioChannelCount_To_AudioChannelLayout_Table) == (FPL_MAX_AUDIO_CHANNEL_COUNT + 1));
25820
25821fpl_globalvar uint16_t fpl__global_AudioChannelLayout_To_AudioChannelCount_Table[] = {
25822 FPL__ENUM_NAME(0, fplAudioChannelLayout_Unsupported),
25823 FPL__ENUM_NAME(0, fplAudioChannelLayout_Automatic),
25824 FPL__ENUM_NAME(1, fplAudioChannelLayout_Mono),
25825 FPL__ENUM_NAME(2, fplAudioChannelLayout_Stereo),
25826 FPL__ENUM_NAME(3, fplAudioChannelLayout_2_1),
25827 FPL__ENUM_NAME(3, fplAudioChannelLayout_3_0_Surround),
25828 FPL__ENUM_NAME(4, fplAudioChannelLayout_4_0_Quad),
25829 FPL__ENUM_NAME(4, fplAudioChannelLayout_4_0_Surround),
25830 FPL__ENUM_NAME(5, fplAudioChannelLayout_4_1),
25831 FPL__ENUM_NAME(5, fplAudioChannelLayout_5_0_Surround),
25832 FPL__ENUM_NAME(6, fplAudioChannelLayout_5_1),
25833 FPL__ENUM_NAME(7, fplAudioChannelLayout_6_1),
25834 FPL__ENUM_NAME(8, fplAudioChannelLayout_7_1),
25835};
25836
25837fplStaticAssert(fplArrayCount(fpl__global_AudioChannelLayout_To_AudioChannelCount_Table) == FPL__AUDIO_CHANNEL_LAYOUT_COUNT);
25838
25840 if (channelCount < fplArrayCount(fpl__global_AudioChannelCount_To_AudioChannelLayout_Table)) {
25841 fplAudioChannelLayout result = fpl__global_AudioChannelCount_To_AudioChannelLayout_Table[channelCount];
25842 return result;
25843 }
25845}
25846
25848 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(channelLayout, fplAudioChannelLayout_First, fplAudioChannelLayout_Last);
25849 uint16_t channels = fpl__global_AudioChannelLayout_To_AudioChannelCount_Table[index];
25850 uint16_t result = fplMax(0, fplMin(channels, FPL_MAX_AUDIO_CHANNEL_COUNT));
25851 return result;
25852}
25853
25855 switch (mode) {
25861 default:
25863 }
25864}
25865
25867 switch (mode) {
25871 default:
25873 }
25874}
25875
25877 switch (shareMode) {
25879 switch (latencyType) {
25883 default:
25885 }
25887 default:
25888 switch (latencyType) {
25892 default:
25894 }
25895 }
25896}
25897
25898fpl_common_api fplAudioFormatU64 fplEncodeAudioFormatU64(const uint32_t sampleRate, const uint16_t channels, const fplAudioFormatType type) {
25899 fplAudioFormatU64 result = 0;
25900 result |= ((uint64_t)sampleRate << 32);
25901 result |= ((uint64_t)channels << 16);
25902 result |= ((uint64_t)(type & 0xFF) << 8);
25903 return result;
25904}
25905
25906fpl_common_api bool fplDecodeAudioFormatU64(const fplAudioFormatU64 format64, uint32_t *outSampleRate, uint16_t *outChannels, fplAudioFormatType *outType) {
25907 if (outSampleRate == fpl_null || outChannels == fpl_null || outType == fpl_null) {
25908 return false;
25909 }
25910 *outSampleRate = (uint32_t)((format64 >> 32) & 0xFFFFFFFF);
25911 *outChannels = (uint16_t)((format64 >> 16) & 0xFFFF);
25912 *outType = (fplAudioFormatType)((format64 >> 8) & 0xFF);
25913 return true;
25914}
25915
25918 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
25919 if (audioState == fpl_null) {
25921 }
25922
25923 fpl__CommonAudioState *commonAudioState = &audioState->common;
25924
25925 fplAudioContext *context = &commonAudioState->context;
25926
25927 if (!fpl__IsAudioDeviceInitialized(context)) {
25929 }
25930
25931 fpl__AudioDeviceState firstDeviceState = fpl__AudioGetDeviceState(context);
25932 if (firstDeviceState == fpl__AudioDeviceState_Stopped) {
25934 }
25935
25937 fplMutexLock(&audioState->lock);
25938 {
25939 // Check if the device is already stopped
25940 if (fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Stopping) {
25941 fplMutexUnlock(&audioState->lock);
25943 }
25944 if (fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Stopped) {
25945 fplMutexUnlock(&audioState->lock);
25947 }
25948
25949 // The device needs to be in a started state. If it's not, we just let the caller know the device is busy.
25950 if (fpl__AudioGetDeviceState(context) != fpl__AudioDeviceState_Started) {
25951 fplMutexUnlock(&audioState->lock);
25953 }
25954
25955 fpl__AudioSetDeviceState(context, fpl__AudioDeviceState_Stopping);
25956
25957 if (audioState->isAsyncBackend) {
25958 // Asynchronous backends (Has their own thread)
25959 fpl__StopAudioDevice(audioState);
25960 } else {
25961 // Synchronous backends
25962
25963 // The audio worker thread is most likely in a wait state, so let it stop properly.
25964 fpl__StopAudioDeviceMainLoop(audioState);
25965
25966 // We need to wait for the worker thread to become available for work before returning.
25967 // @NOTE(final): The audio worker thread will be the one who puts the device into the stopped state.
25968 fpl__WaitForAudioEvent(&audioState->stopEvent);
25970 }
25971 }
25972 fplMutexUnlock(&audioState->lock);
25973
25974 return result;
25975}
25976
25979 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
25980 if (audioState == fpl_null) {
25982 }
25983
25984 fpl__CommonAudioState *commonAudioState = &audioState->common;
25985
25986 fplAudioContext *context = &commonAudioState->context;
25987
25988 if (!fpl__IsAudioDeviceInitialized(context)) {
25990 }
25991
25992 if (fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Started) {
25994 }
25995
25997 fplMutexLock(&audioState->lock);
25998 {
25999 // If device is already in started/starting state we cannot start playback of it
26000 if (fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Starting) {
26001 fplMutexUnlock(&audioState->lock);
26003 }
26004 if (fpl__AudioGetDeviceState(context) == fpl__AudioDeviceState_Started) {
26005 fplMutexUnlock(&audioState->lock);
26007 }
26008
26009 // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy.
26010 if (fpl__AudioGetDeviceState(context) != fpl__AudioDeviceState_Stopped) {
26011 fplMutexUnlock(&audioState->lock);
26013 }
26014
26015 fpl__AudioSetDeviceState(context, fpl__AudioDeviceState_Starting);
26016
26017 if (audioState->isAsyncBackend) {
26018 // Asynchronous backends (Has their own thread)
26019 fpl__StartAudioDevice(audioState);
26020 fpl__AudioSetDeviceState(context, fpl__AudioDeviceState_Started);
26021 } else {
26022 // Synchronous backends
26023 fpl__SetAudioEvent(&audioState->wakeupEvent);
26024
26025 // Wait for the worker thread to finish starting the device.
26026 // @NOTE(final): The audio worker thread will be the one who puts the device into the started state.
26027 fpl__WaitForAudioEvent(&audioState->startEvent);
26028 result = audioState->workResult;
26029 }
26030 }
26031 fplMutexUnlock(&audioState->lock);
26032
26033 return result;
26034}
26035
26037 FPL__CheckArgumentNull(audioSettings, fplAudioResultType_InvalidArguments);
26039 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26040 if (audioState == fpl_null) {
26042 }
26043
26044 fpl__ReleaseAudio(audioState);
26045
26046 fplAudioContext *context = &audioState->common.context;
26047
26048 fplAudioBackend *backend = (fplAudioBackend *)(((uint8_t *)fpl__global__AppState->audio.mem) + fpl__global__AppState->audio.offsetToBackend);
26049 fplAssert(backend != fpl_null);
26050
26051 const char *audioBackendName;
26052 const char *audioFormatName;
26053 const char *audioResultName;
26054
26055 audioBackendName = fplGetAudioBackendName(audioSettings->backend);
26056 audioFormatName = fplGetAudioFormatName(audioSettings->targetFormat.type);
26057 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Load Audio with Backend '%s' with Target-Format (Sample Rate=%u, Channels=%u, Format='%s')",
26058 audioBackendName,
26059 audioSettings->targetFormat.sampleRate,
26060 audioSettings->targetFormat.channels,
26061 audioFormatName);
26062
26063 fplAudioResultType initAudioResult = fpl__InitAudio(audioSettings, audioState);
26064 if (initAudioResult != fplAudioResultType_Success) {
26065 audioResultName = fplGetAudioResultName(initAudioResult);
26066 audioFormatName = fplGetAudioFormatName(backend->desiredFormat.type);
26067 FPL__CRITICAL(FPL__MODULE_CORE, "Failed loading Audio with Backend '%s' and Desired-Format (Sample Rate=%u, Channels=%u, Format='%s') -> %s",
26068 audioBackendName,
26069 backend->desiredFormat.sampleRate,
26070 backend->desiredFormat.channels,
26071 audioFormatName,
26072 audioResultName);
26073 fpl__ReleaseAudio(audioState);
26074 return(initAudioResult);
26075 }
26076
26077 audioBackendName = fplGetAudioBackendName(audioState->backendType);
26078 audioFormatName = fplGetAudioFormatName(backend->internalFormat.type);
26079 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully loaded Audio with Backend '%s' and Internal-Format Sample Rate '%u', Channels '%u', Format '%s':", audioBackendName, backend->internalFormat.sampleRate, backend->internalFormat.channels, audioFormatName);
26080
26081 // Auto play audio if needed
26082 if (audioSettings->startAuto && (audioSettings->clientReadCallback != fpl_null)) {
26083 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Play Audio (Auto)");
26084 fplAudioResultType playResult = fplPlayAudio();
26085 audioResultName = fplGetAudioResultName(playResult);
26086 if (playResult != fplAudioResultType_Success) {
26087 FPL__ERROR(FPL__MODULE_CORE, "Failed playing Audio (Auto) -> %s", audioResultName);
26088 fpl__ReleaseAudio(audioState);
26089 return(playResult);
26090 }
26091 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully playing Audio (Auto)");
26092 }
26093
26095}
26096
26098 FPL__CheckPlatform(false);
26099 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26100 if (audioState == fpl_null) {
26101 return false;
26102 }
26103 fplAudioContext *context = &audioState->common.context;
26104 if (!fpl__IsAudioDeviceInitialized(context)) {
26105 return false;
26106 }
26107 fpl__ReleaseAudio(audioState);
26108 return true;
26109}
26110
26112 FPL__CheckArgumentNull(outFormat, false);
26113 FPL__CheckPlatform(false);
26114 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26115 if (audioState == fpl_null) {
26116 return false;
26117 }
26118 fplAudioBackend *backend = audioState->common.backend;
26119 if (backend == fpl_null) {
26120 return false;
26121 }
26122 fplClearStruct(outFormat);
26123 *outFormat = backend->internalFormat;
26124 return true;
26125}
26126
26128 FPL__CheckArgumentNull(outDevice, false);
26129 FPL__CheckPlatform(false);
26130 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26131 if (audioState == fpl_null) {
26132 return false;
26133 }
26134 fplAudioBackend *backend = audioState->common.backend;
26135 if (backend == fpl_null) {
26136 return false;
26137 }
26138 fplClearStruct(outDevice);
26139 *outDevice = backend->internalDevice;
26140 return true;
26141}
26142
26144 FPL__CheckPlatform(fpl_null);
26145 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26146 if (audioState == fpl_null) {
26147 return fpl_null;
26148 }
26149 fplAudioBackend *backend = audioState->common.backend;
26150 if (backend == fpl_null) {
26151 return fpl_null;
26152 }
26153 return backend->internalDevice.name;
26154}
26155
26157 FPL__CheckArgumentNull(outMapping, false);
26158 FPL__CheckPlatform(false);
26159 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26160 if (audioState == fpl_null) {
26161 return false;
26162 }
26163 *outMapping = audioState->common.channelsMapping;
26164 return true;
26165}
26166
26168 FPL__CheckPlatform(false);
26169 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26170 fplAudioBackend *backend = audioState != fpl_null ? audioState->common.backend : fpl_null;
26171 if (audioState == fpl_null || backend == fpl_null) {
26172 return false;
26173 }
26174 fplAudioContext *context = &audioState->common.context;
26175 bool invalidBackend = !(audioState->backendType > fplAudioBackendType_Auto);
26176 bool notStopped = fpl__AudioGetDeviceState(context) != fpl__AudioDeviceState_Stopped;
26177 if (invalidBackend || notStopped) {
26178 return false;
26179 }
26180 backend->clientReadCallback = newCallback;
26181 backend->clientUserData = userData;
26182 return true;
26183}
26184
26185fpl_common_api uint32_t fplGetAudioDevices(const uint32_t maxDeviceCount, const uint32_t deviceInfoSize, fplAudioDeviceInfo *outDevices) {
26186 FPL__CheckPlatform(0);
26187 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26188 fplAudioContext *context = &audioState->common.context;
26189 fplAudioBackend *backend = fpl__GetActiveAudioBackend(audioState);
26190 if (audioState == fpl_null || backend == fpl_null) {
26191 return 0;
26192 }
26193 bool invalidBackend = !(audioState->backendType > fplAudioBackendType_Auto);
26194 if (invalidBackend) {
26195 return 0;
26196 }
26197 uint32_t stride = fplMax(sizeof(fplAudioDeviceInfo), deviceInfoSize);
26198 fplAssert(audioState->common.funcTable.getAudioDevices != fpl_null);
26199 uint32_t result = audioState->common.funcTable.getAudioDevices(context, backend, maxDeviceCount, stride, outDevices);
26200 return(result);
26201}
26202
26204 FPL__CheckArgumentNull(outDeviceInfo, false);
26205 FPL__CheckPlatform(false);
26206 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26207 fplAudioContext *context = &audioState->common.context;
26208 fplAudioBackend *backend = fpl__GetActiveAudioBackend(audioState);
26209 if (audioState == fpl_null || backend == fpl_null) {
26210 return 0;
26211 }
26212 bool invalidBackend = !(audioState->backendType > fplAudioBackendType_Auto);
26213 if (invalidBackend) {
26214 return 0;
26215 }
26216 fplAssert(audioState->common.funcTable.getAudioDeviceInfo != fpl_null);
26217 bool result = audioState->common.funcTable.getAudioDeviceInfo(context, backend, deviceId, outDeviceInfo);
26218 return(result);
26219}
26220#endif // FPL__ENABLE_AUDIO
26221
26222// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26223//
26224// > SYSTEM_VIDEO_L2 (Video System, Public Implementation)
26225//
26226// - Video Backbuffer Access
26227// - Frame flipping
26228// - Utiltity functions
26229//
26230// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26231#if defined(FPL__ENABLE_VIDEO)
26232#define FPL__VIDEOBACKENDTYPE_COUNT FPL__ENUM_COUNT(fplVideoBackendType_First, fplVideoBackendType_Last)
26235fpl_globalvar const char *fpl__globalVideoBackendNameTable[FPL__VIDEOBACKENDTYPE_COUNT] = {
26236 FPL__ENUM_NAME("None", fplVideoBackendType_None),
26237 FPL__ENUM_NAME("Software", fplVideoBackendType_Software),
26238 FPL__ENUM_NAME("OpenGL", fplVideoBackendType_OpenGL),
26239 FPL__ENUM_NAME("Vulkan", fplVideoBackendType_Vulkan),
26240};
26241
26243 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(backendType, fplVideoBackendType_First, fplVideoBackendType_Last);
26244 const char *result = fpl__globalVideoBackendNameTable[index];
26245 return(result);
26246}
26247
26249 FPL__CheckPlatform(fplVideoBackendType_None);
26250 fpl__PlatformAppState *appState = fpl__global__AppState;
26251 fpl__VideoState *videoState = fpl__GetVideoState(appState);
26252 if (videoState != fpl_null) {
26253 return(videoState->backendType);
26254 }
26256}
26257
26259 FPL__CheckPlatform(fpl_null);
26260 fpl__PlatformAppState *appState = fpl__global__AppState;
26261 fpl__VideoState *videoState = fpl__GetVideoState(appState);
26262 fplVideoBackBuffer *result = fpl_null;
26263 if (videoState != fpl_null) {
26264#if defined(FPL__ENABLE_VIDEO_SOFTWARE)
26265 if (appState->currentSettings.video.backend == fplVideoBackendType_Software) {
26266 result = &videoState->data.backbuffer;
26267 }
26268#endif
26269 }
26270 return(result);
26271}
26272
26273fpl_common_api bool fplResizeVideoBackBuffer(const uint32_t width, const uint32_t height) {
26274 FPL__CheckPlatform(false);
26275 fpl__PlatformAppState *appState = fpl__global__AppState;
26276 fpl__VideoState *videoState = fpl__GetVideoState(appState);
26277 bool result = false;
26278 if (videoState != fpl_null) {
26279 fplVideoBackendType backendType = videoState->backendType;
26280 if (backendType != fplVideoBackendType_None && videoState->context.recreateOnResize) {
26281 fpl__ShutdownVideoBackend(appState, videoState);
26282 result = fpl__InitializeVideoBackend(videoState->backendType, &appState->currentSettings.video, width, height, appState, videoState);
26283 }
26284 }
26285 return (result);
26286}
26287
26288fpl_common_api void fplVideoFlip(void) {
26289 FPL__CheckPlatformNoRet();
26290 fpl__PlatformAppState *appState = fpl__global__AppState;
26291 const fpl__VideoState *videoState = fpl__GetVideoState(appState);
26292 if (videoState != fpl_null && videoState->backendType != fplVideoBackendType_None) {
26293 fplAssert(videoState->context.presentFunc != fpl_null);
26294 videoState->context.presentFunc(appState, &appState->window, &videoState->data, &videoState->activeBackend.base);
26295 }
26296}
26297
26298fpl_common_api const void *fplGetVideoProcedure(const char *procName) {
26299 FPL__CheckPlatform(fpl_null);
26300 fpl__PlatformAppState *appState = fpl__global__AppState;
26301 const fpl__VideoState *videoState = fpl__GetVideoState(appState);
26302 const void *result = fpl_null;
26303 if (videoState != fpl_null && videoState->backendType != fplVideoBackendType_None) {
26304 fplAssert(videoState->context.getProcedureFunc != fpl_null);
26305 result = videoState->context.getProcedureFunc(&videoState->activeBackend.base, procName);
26306 }
26307 return(result);
26308}
26309
26311 FPL__CheckPlatform(fpl_null);
26312 fpl__PlatformAppState *appState = fpl__global__AppState;
26313 const fpl__VideoState *videoState = fpl__GetVideoState(appState);
26314 const fplVideoSurface *result = fpl_null;
26315 if (videoState != fpl_null && videoState->backendType != fplVideoBackendType_None) {
26316 result = &videoState->activeBackend.base.surface;
26317 }
26318 return(result);
26319}
26320
26322 fpl__VideoContext context = fpl__ConstructVideoContext(backendType);
26323 bool result = false;
26324 if (context.getRequirementsFunc != fpl_null) {
26325 result = context.getRequirementsFunc(requirements);
26326 }
26327 return(result);
26328}
26329
26330fpl_inline fplVideoRect fplCreateVideoRectFromLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
26331 fplVideoRect result = { left, top, (right - left) + 1, (bottom - top) + 1 };
26332 return(result);
26333}
26334#endif // FPL__ENABLE_VIDEO
26335
26336// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26337//
26338// > SYSTEM_INIT
26339//
26340// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26341#if !defined(FPL__SYSTEM_INIT_DEFINED)
26342#define FPL__SYSTEM_INIT_DEFINED
26343
26344fpl_internal void fpl__ReleasePlatformStates(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
26345 fplAssert(initState != fpl_null);
26346
26347 // Release audio
26348# if defined(FPL__ENABLE_AUDIO)
26349 {
26350 // Auto-Stop audio if enabled
26351 if (appState->currentSettings.audio.stopAuto) {
26352 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
26353 if (audioState != fpl_null) {
26354 fpl__CommonAudioState *commonAudioState = &audioState->common;
26355 fplAudioContext *context = &commonAudioState->context;
26356 fpl__AudioDeviceState deviceState = fpl__AudioGetDeviceState(context);
26357 if (deviceState != fpl__AudioDeviceState_Stopped) {
26358 FPL_LOG_DEBUG("Core", "Stop Audio (Auto)");
26359 fplStopAudio();
26360 }
26361 }
26362 }
26363
26364 FPL_LOG_DEBUG("Core", "Release Audio");
26365 fpl__AudioState *audioState = fpl__GetAudioState(appState);
26366 if (audioState != fpl_null) {
26367 fpl__ReleaseAudio(audioState);
26368 }
26369 }
26370# endif
26371
26372 // Shutdown video backend
26373# if defined(FPL__ENABLE_VIDEO)
26374 {
26375 fpl__VideoState *videoState = fpl__GetVideoState(appState);
26376 if (videoState != fpl_null) {
26377 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Shutdown Video Backend '%s'", fplGetVideoBackendName(videoState->backendType));
26378 fpl__ShutdownVideoBackend(appState, videoState);
26379 }
26380 }
26381# endif
26382
26383 // Release window
26384# if defined(FPL__ENABLE_WINDOW)
26385 {
26386 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Window");
26387 fpl__ReleaseWindow(initState, appState);
26388 fpl__ClearInternalEvents();
26389 }
26390# endif
26391
26392 // Release video backend
26393# if defined(FPL__ENABLE_VIDEO)
26394 {
26395 fpl__VideoState *videoState = fpl__GetVideoState(appState);
26396 if (videoState != fpl_null) {
26397 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Destroy surface for Video Backend '%s'", fplGetVideoBackendName(videoState->backendType));
26398 fpl__DestroySurfaceBackend(appState, videoState);
26399
26400 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Video Backend '%s'", fplGetVideoBackendName(videoState->backendType));
26401 fpl__UnloadVideoBackend(appState, videoState);
26402 }
26403 }
26404# endif
26405
26406 if (appState != fpl_null) {
26407 // Release actual platform (There can only be one platform!)
26408 {
26409# if defined(FPL_PLATFORM_WINDOWS)
26410 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Win32 Platform");
26411 fpl__Win32ReleasePlatform(initState, appState);
26412# elif defined(FPL_PLATFORM_LINUX)
26413 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Linux Platform");
26414 fpl__LinuxReleasePlatform(initState, appState);
26415# elif defined(FPL_PLATFORM_UNIX)
26416 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Unix Platform");
26417 fpl__UnixReleasePlatform(initState, appState);
26418# endif
26419 }
26420
26421 // Release sub platforms
26422 {
26423# if defined(FPL_SUBPLATFORM_X11)
26424 FPL_LOG_DEBUG("Core", "Release X11 Subplatform");
26425 fpl__X11ReleaseSubplatform(&appState->x11);
26426# endif
26427# if defined(FPL_SUBPLATFORM_POSIX)
26428 FPL_LOG_DEBUG("Core", "Release POSIX Subplatform");
26429 fpl__PosixReleaseSubplatform(&appState->posix);
26430# endif
26431 }
26432
26433 // Release platform applicatiom state memory
26434 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release allocated Platform App State Memory");
26435 fplMemoryAlignedFree(appState);
26436 fpl__global__AppState = fpl_null;
26437 }
26438
26439 fplClearStruct(initState);
26440}
26441
26442#define FPL__PLATFORMTYPE_COUNT FPL__ENUM_COUNT(fplPlatformType_First, fplPlatformType_Last)
26445fpl_globalvar const char *fpl__globalPlatformTypeNameTable[] = {
26446 FPL__ENUM_NAME("Unknown", fplPlatformType_Unknown),
26447 FPL__ENUM_NAME("Windows", fplPlatformType_Windows),
26448 FPL__ENUM_NAME("Linux", fplPlatformType_Linux),
26449 FPL__ENUM_NAME("Unix", fplPlatformType_Unix),
26450};
26451fplStaticAssert(fplArrayCount(fpl__globalPlatformTypeNameTable) == FPL__PLATFORMTYPE_COUNT);
26452
26454 fpl__PlatformInitState *initState = &fpl__global__InitState;
26455 bool result = initState->isInitialized;
26456 return(result);
26457}
26458
26459fpl_common_api const char *fplGetPlatformName(const fplPlatformType type) {
26460 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplPlatformType_First, fplPlatformType_Last);
26461 const char *result = fpl__globalPlatformTypeNameTable[index];
26462 return(result);
26463}
26464
26466 fpl__PlatformInitState *initState = &fpl__global__InitState;
26467 return(initState->initResult);
26468}
26469
26470fpl_internal bool fpl__SetPlatformResult(const fplPlatformResultType resultType) {
26471 fpl__PlatformInitState *initState = &fpl__global__InitState;
26472 initState->initResult = resultType;
26473 return(initState->initResult == fplPlatformResultType_Success);
26474}
26475
26477 // Exit out if platform is not initialized
26478 fpl__PlatformInitState *initState = &fpl__global__InitState;
26479 if (!initState->isInitialized) {
26480 FPL__CRITICAL(FPL__MODULE_CORE, "Platform is not initialized");
26481 return;
26482 }
26483 fpl__PlatformAppState *appState = fpl__global__AppState;
26484 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Platform");
26485 fpl__ReleasePlatformStates(initState, appState);
26486 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Platform released");
26487}
26488
26489typedef struct {
26490 size_t offset;
26491 size_t size;
26492} fpl__PlatformMemoryBlock;
26493
26494fpl_internal void fpl__PushPlatformMemory(fpl__PlatformMemoryBlock *block, const size_t size, const size_t alignment, const size_t padding) {
26495 size_t alignedSize = fplGetAlignedSize(size, alignment);
26496 size_t offset = block->offset;
26497 size_t addonSize = padding + alignedSize;
26498 block->size += addonSize;
26499 block->offset += alignedSize + padding;
26500}
26501
26502fpl_common_api bool fplPlatformInit(const fplInitFlags initFlags, const fplSettings *initSettings) {
26503 // Exit out if platform is already initialized
26504 if (fpl__global__InitState.isInitialized) {
26505 FPL__CRITICAL(FPL__MODULE_CORE, "Platform is already initialized");
26506 return(fpl__SetPlatformResult(fplPlatformResultType_AlreadyInitialized));
26507 }
26508
26509 fpl__PlatformInitState *initState = &fpl__global__InitState;
26510 fplClearStruct(initState);
26511
26512 // Copy over init settings, such as memory allocation settings, etc.
26513 if (initSettings != fpl_null) {
26514 fplCopyStruct(&initSettings->memory, &initState->initSettings.memorySettings);
26515 }
26516
26517 fpl__PlatformMemoryBlock platformMemoryBlock = fplZeroInit;
26518 fpl__PlatformMemoryBlock videoMemoryBlock = fplZeroInit;
26519 fpl__PlatformMemoryBlock audioMemoryBlock = fplZeroInit;
26520
26521 // Compute platform memory
26522 fpl__PushPlatformMemory(&platformMemoryBlock, sizeof(fpl__PlatformAppState), 16, 0);
26523
26524 // Compute memory for video
26525# if defined(FPL__ENABLE_VIDEO)
26526 if (initFlags & fplInitFlags_Video) {
26527 fpl__PushPlatformMemory(&videoMemoryBlock, sizeof(fpl__VideoState), 16, 0);
26528 }
26529# endif
26530
26531 // Compute memory for audio
26532# if defined(FPL__ENABLE_AUDIO)
26533 size_t maxAudioBackendSize = 0;
26534 size_t offsetToAudioBackend = 0;
26535 if (initFlags & fplInitFlags_Audio) {
26536 fplAudioSettings audioSettings = fplZeroInit;
26537 if (initSettings != fpl_null) {
26538 audioSettings = initSettings->audio;
26539 } else {
26540 fplSetDefaultAudioSettings(&audioSettings);
26541 }
26542 maxAudioBackendSize = fpl__GetMaxAudioBackendSize(&audioSettings);
26543 fpl__PushPlatformMemory(&audioMemoryBlock, sizeof(fpl__AudioState), 16, FPL__ARBITARY_PADDING);
26544 fpl__PushPlatformMemory(&audioMemoryBlock, maxAudioBackendSize, 16, 0);
26545 offsetToAudioBackend = sizeof(fpl__AudioState) + FPL__ARBITARY_PADDING;
26546 }
26547# endif
26548
26549 fpl__PlatformMemoryBlock *memoryBlocks[3] = fplZeroInit;
26550 uint8_t memoryBlockCount = 0;
26551 memoryBlocks[memoryBlockCount++] = &platformMemoryBlock;
26552 if (videoMemoryBlock.size > 0) {
26553 memoryBlocks[memoryBlockCount++] = &videoMemoryBlock;
26554 }
26555 if (audioMemoryBlock.size > 0) {
26556 memoryBlocks[memoryBlockCount++] = &audioMemoryBlock;
26557 }
26558
26559 size_t totalMemorySize = 0;
26560 for (uint8_t i = 0; i < memoryBlockCount; ++i) {
26561 fpl__PlatformMemoryBlock *currentBlock = memoryBlocks[i];
26562 currentBlock->offset = totalMemorySize;
26563 size_t memoryBlockSize = currentBlock->size;
26564 totalMemorySize += memoryBlockSize;
26565 if (i < memoryBlockCount - 1) {
26566 totalMemorySize += FPL__ARBITARY_PADDING;
26567 }
26568 }
26569
26570 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Allocate Platform Memory of size '%zu':", totalMemorySize);
26571 fplAssert(fpl__global__AppState == fpl_null);
26572 uint8_t *platformMemory = (uint8_t *)fplMemoryAlignedAllocate(totalMemorySize, 16);
26573 if (platformMemory == fpl_null) {
26574 FPL__CRITICAL(FPL__MODULE_CORE, "Failed Allocating Platform Memory of size '%zu'", totalMemorySize);
26575 return(fpl__SetPlatformResult(fplPlatformResultType_OutOfMemory));
26576 }
26577
26578 fplAssert(platformMemoryBlock.offset == 0);
26579 fpl__PlatformAppState *appState = fpl__global__AppState = (fpl__PlatformAppState *)platformMemory + platformMemoryBlock.offset;
26580 appState->initFlags = initFlags;
26581 if (initSettings != fpl_null) {
26582 appState->initSettings = *initSettings;
26583 } else {
26584 fplSetDefaultSettings(&appState->initSettings);
26585 }
26586 appState->currentSettings = appState->initSettings;
26587
26588 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully allocated Platform Memory of size '%zu'", totalMemorySize);
26589
26590 // Force the inclusion of window when Video flags is set or remove the video flags when video is disabled
26591# if defined(FPL__ENABLE_VIDEO)
26592 if (appState->initFlags & fplInitFlags_Video) {
26593 appState->initFlags |= fplInitFlags_Window;
26594 }
26595# else
26596 appState->initFlags &= ~fplInitFlags_Video;
26597# endif
26598
26599 // Window/Video flag are removed when windowing is disabled
26600# if !defined(FPL__ENABLE_WINDOW)
26601 appState->initFlags &= ~fplInitFlags_Window;
26602 appState->initFlags &= ~fplInitFlags_Video;
26603# endif
26604
26605 // Initialize sub-platforms
26606# if defined(FPL_SUBPLATFORM_POSIX)
26607 {
26608 FPL_LOG_DEBUG("Core", "Initialize POSIX Subplatform:");
26609 if (!fpl__PosixInitSubplatform(initFlags, initSettings, &initState->posix, &appState->posix)) {
26610 FPL__CRITICAL("Core", "Failed initializing POSIX Subplatform!");
26611 fpl__ReleasePlatformStates(initState, appState);
26612 return(fpl__SetPlatformResult(fplPlatformResultType_FailedPlatform));
26613 }
26614 FPL_LOG_DEBUG("Core", "Successfully initialized POSIX Subplatform");
26615 }
26616# endif // FPL_SUBPLATFORM_POSIX
26617
26618# if defined(FPL_SUBPLATFORM_X11)
26619 {
26620 FPL_LOG_DEBUG("Core", "Initialize X11 Subplatform:");
26621 if (!fpl__X11InitSubplatform(&appState->x11)) {
26622 FPL__CRITICAL("Core", "Failed initializing X11 Subplatform!");
26623 fpl__ReleasePlatformStates(initState, appState);
26624 return(fpl__SetPlatformResult(fplPlatformResultType_FailedPlatform));
26625 }
26626 FPL_LOG_DEBUG("Core", "Successfully initialized X11 Subplatform");
26627 }
26628# endif // FPL_SUBPLATFORM_X11
26629
26630 // Initialize the actual platform (There can only be one at a time!)
26631 bool isInitialized = false;
26632 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Initialize %s Platform:", FPL_PLATFORM_NAME);
26633# if defined(FPL_PLATFORM_WINDOWS)
26634 isInitialized = fpl__Win32InitPlatform(appState->initFlags, &appState->initSettings, initState, appState);
26635# elif defined(FPL_PLATFORM_LINUX)
26636 isInitialized = fpl__LinuxInitPlatform(appState->initFlags, &appState->initSettings, initState, appState);
26637# elif defined(FPL_PLATFORM_UNIX)
26638 isInitialized = fpl__UnixInitPlatform(appState->initFlags, &appState->initSettings, initState, appState);
26639# endif
26640
26641 if (!isInitialized) {
26642 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initializing %s Platform!", FPL_PLATFORM_NAME);
26643 fpl__ReleasePlatformStates(initState, appState);
26644 return(fpl__SetPlatformResult(fplPlatformResultType_FailedPlatform));
26645 }
26646 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized %s Platform", FPL_PLATFORM_NAME);
26647
26648 // Init video state
26649# if defined(FPL__ENABLE_VIDEO)
26650 if (appState->initFlags & fplInitFlags_Video) {
26651 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Video State with size '%zu'", videoMemoryBlock.size);
26652 fplAssert(videoMemoryBlock.offset > 0);
26653 appState->video.mem = platformMemory + videoMemoryBlock.offset;
26654 appState->video.memSize = videoMemoryBlock.size;
26655 fpl__VideoState *videoState = fpl__GetVideoState(appState);
26656 fplAssert(videoState != fpl_null);
26657
26658 fplVideoBackendType videoBackendType = appState->initSettings.video.backend;
26659
26660 // Construct video context (Function table)
26661 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Construct Video Context:");
26662 videoState->context = fpl__ConstructVideoContext(videoBackendType);
26663 videoState->backendType = videoBackendType;
26664
26665 // Load video backend (API)
26666 const char *videoBackendName = fplGetVideoBackendName(videoBackendType);
26667 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Load Video API for Backend '%s':", videoBackendName);
26668 {
26669 if (!fpl__LoadVideoBackend(appState, videoState)) {
26670 FPL__CRITICAL(FPL__MODULE_CORE, "Failed loading Video API for Backend '%s'!", videoBackendName);
26671 fpl__ReleasePlatformStates(initState, appState);
26672 return(fpl__SetPlatformResult(fplPlatformResultType_FailedVideo));
26673 }
26674 }
26675 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully loaded Video API for Backend '%s'", videoBackendName);
26676 }
26677# endif // FPL__ENABLE_VIDEO
26678
26679 // Init Window & event queue
26680# if defined(FPL__ENABLE_WINDOW)
26681 if (appState->initFlags & fplInitFlags_Window) {
26682 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Window:");
26683 fpl__SetupWindowCallbacks winCallbacks = fplZeroInit;
26684 winCallbacks.preSetup = fpl__PrepareVideoWindowDefault;
26685 winCallbacks.postSetup = fpl__FinalizeVideoWindowDefault;
26686 if (!fpl__InitWindow(&appState->initSettings, &appState->currentSettings.window, appState, &winCallbacks)) {
26687 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initializing Window!");
26688 fpl__ReleasePlatformStates(initState, appState);
26689 return(fpl__SetPlatformResult(fplPlatformResultType_FailedWindow));
26690 }
26691 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized Window");
26692 }
26693# endif // FPL__ENABLE_WINDOW
26694
26695 // Init Video
26696# if defined(FPL__ENABLE_VIDEO)
26697 if (appState->initFlags & fplInitFlags_Video) {
26698 fpl__VideoState *videoState = fpl__GetVideoState(appState);
26699 fplAssert(videoState != fpl_null);
26700 fplWindowSize windowSize = fplZeroInit;
26701 fplGetWindowSize(&windowSize);
26702 const char *videoBackendName = fplGetVideoBackendName(appState->initSettings.video.backend);
26703 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Video with Backend '%s':", videoBackendName);
26704 if (!fpl__InitializeVideoBackend(appState->initSettings.video.backend, &appState->initSettings.video, windowSize.width, windowSize.height, appState, videoState)) {
26705 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initialization Video with Backend '%s' with settings (Width=%d, Height=%d)", videoBackendName, windowSize.width, windowSize.height);
26706 fpl__ReleasePlatformStates(initState, appState);
26707 return(fpl__SetPlatformResult(fplPlatformResultType_FailedVideo));
26708 }
26709 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized Video with Backend '%s'", videoBackendName);
26710 }
26711# endif // FPL__ENABLE_VIDEO
26712
26713 // Init Audio
26714# if defined(FPL__ENABLE_AUDIO)
26715 if (appState->initFlags & fplInitFlags_Audio) {
26716 fplAssert(audioMemoryBlock.offset > 0);
26717 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Audio State with size '%zu'", audioMemoryBlock.size);
26718 appState->audio.mem = platformMemory + audioMemoryBlock.offset;
26719 appState->audio.memSize = audioMemoryBlock.size;
26720 appState->audio.maxBackendSize = maxAudioBackendSize;
26721 appState->audio.offsetToBackend = offsetToAudioBackend;
26722
26723 const char *audioBackendName;
26724 const char *audioDeviceName;
26725 const char *audioFormatName;
26726 const char *audioResultName;
26727
26728 audioBackendName = fplGetAudioBackendName(appState->initSettings.audio.backend);
26729 audioDeviceName = appState->initSettings.audio.targetDevice.name;
26730 audioFormatName = fplGetAudioFormatName(appState->initSettings.audio.targetFormat.type);
26731 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Audio with Backend '%s', Device '%s', Format (Sample Rate=%u, Channels=%u, Format='%s')",
26732 audioBackendName,
26733 audioDeviceName,
26734 appState->initSettings.audio.targetFormat.sampleRate,
26735 appState->initSettings.audio.targetFormat.channels,
26736 audioFormatName);
26737
26738 fpl__AudioState *audioState = fpl__GetAudioState(appState);
26739 fplAssert(audioState != fpl_null);
26740
26741 fplAudioBackend *backend = (fplAudioBackend *)((uint8_t *)appState->audio.mem + offsetToAudioBackend);
26742 fplAssert(backend != fpl_null);
26743
26744 if (!appState->initSettings.audio.manualLoad) {
26745 fplAudioResultType initAudioResult = fpl__InitAudio(&appState->initSettings.audio, audioState);
26746 if (initAudioResult != fplAudioResultType_Success) {
26747 audioResultName = fplGetAudioResultName(initAudioResult);
26748 audioFormatName = fplGetAudioFormatName(backend->desiredFormat.type);
26749 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initialization Audio with Backend '%s', Device '%s', Format (SampleRate=%u, Channels=%u, Format='%s') -> %s",
26750 audioBackendName,
26751 audioDeviceName,
26752 backend->desiredFormat.sampleRate,
26753 backend->desiredFormat.channels,
26754 audioFormatName,
26755 audioResultName);
26756 fpl__ReleasePlatformStates(initState, appState);
26757 return(fpl__SetPlatformResult(fplPlatformResultType_FailedAudio));
26758 }
26759 audioBackendName = fplGetAudioBackendName(audioState->backendType);
26760 audioFormatName = fplGetAudioFormatName(backend->internalFormat.type);
26761 audioDeviceName = backend->internalDevice.name;
26762 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized Audio with Backend '%s', Device '%s', Format (Sample Rate=%u, Channels=%u, Format='%s')", audioBackendName, audioDeviceName, backend->internalFormat.sampleRate, backend->internalFormat.channels, audioFormatName);
26763
26764 // Auto play audio if needed
26765 if (appState->initSettings.audio.startAuto && (appState->initSettings.audio.clientReadCallback != fpl_null)) {
26766 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Play Audio (Auto)");
26767 fplAudioResultType playResult = fplPlayAudio();
26768 audioResultName = fplGetAudioResultName(playResult);
26769 if (playResult != fplAudioResultType_Success) {
26770 FPL__CRITICAL(FPL__MODULE_CORE, "Failed playing Audio (Auto) -> %s", audioResultName);
26771 fpl__ReleasePlatformStates(initState, appState);
26772 return(fpl__SetPlatformResult(fplPlatformResultType_FailedAudio));
26773 }
26774 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully playing Audio (Auto)");
26775 }
26776 } else {
26777 FPL_LOG_INFO(FPL__MODULE_CORE, "Audio backend initialization is skipped due to user settings");
26778 }
26779 }
26780# endif // FPL__ENABLE_AUDIO
26781
26782 initState->isInitialized = true;
26783 return(fpl__SetPlatformResult(fplPlatformResultType_Success));
26784}
26785
26787 fplPlatformType result;
26788#if defined(FPL_PLATFORM_WINDOWS)
26789 result = fplPlatformType_Windows;
26790#elif defined(FPL_PLATFORM_LINUX)
26791 result = fplPlatformType_Linux;
26792#elif defined(FPL_PLATFORM_UNIX)
26793 result = fplPlatformType_Unix;
26794#else
26795 result = fplPlatformType_Unknown;
26796#endif
26797 return(result);
26798}
26799
26800#endif // FPL__SYSTEM_INIT_DEFINED
26801
26802#if defined(FPL_COMPILER_MSVC)
26804# pragma warning( pop )
26805#elif defined(FPL_COMPILER_GCC)
26807# pragma GCC diagnostic pop
26808#elif defined(FPL_COMPILER_CLANG)
26810# pragma clang diagnostic pop
26811#endif
26812
26813#endif // FPL_IMPLEMENTATION && !FPL__IMPLEMENTED
26814
26815// ****************************************************************************
26816//
26817// Entry-Points Implementation
26818//
26819// ****************************************************************************
26820#if defined(FPL_ENTRYPOINT) && !defined(FPL__ENTRYPOINT_IMPLEMENTED)
26821# define FPL__ENTRYPOINT_IMPLEMENTED
26822
26823// ***************************************************************
26824//
26825// Platform includes for entry point
26826//
26827// ***************************************************************
26828#if !defined(FPL__HAS_PLATFORM_INCLUDES)
26829# define FPL__HAS_PLATFORM_INCLUDES
26830
26831# if defined(FPL_PLATFORM_WINDOWS)
26832 // @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!
26833# if !defined(NOMINMAX)
26834# define NOMINMAX
26835# endif
26836 // @NOTE(final): For now we dont want any network, com or gdi stuff at all, maybe later who knows.
26837# if !defined(WIN32_LEAN_AND_MEAN)
26838# define WIN32_LEAN_AND_MEAN 1
26839# endif
26840 // @STUPID(final): Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here"
26841struct IUnknown;
26842# include <windows.h> // Win32 api
26843# if _WIN32_WINNT < 0x0600
26844# error "Windows Vista or higher required!"
26845# endif
26846# endif // FPL_PLATFORM_WINDOWS
26847
26848#endif // !FPL__HAS_PLATFORM_INCLUDES
26849
26850// ***************************************************************
26851//
26852// Win32 Entry point
26853//
26854// ***************************************************************
26855# if defined(FPL_PLATFORM_WINDOWS)
26856
26857#define FPL__FUNC_WIN32_CommandLineToArgvW(name) LPWSTR* WINAPI name(LPCWSTR lpCmdLine, int *pNumArgs)
26858typedef FPL__FUNC_WIN32_CommandLineToArgvW(fpl__win32_func_CommandLineToArgvW);
26859
26860typedef struct fpl__Win32CommandLineUTF8Arguments {
26861 void *mem;
26862 char **args;
26863 uint32_t count;
26864} fpl__Win32CommandLineUTF8Arguments;
26865
26866fpl_internal fpl__Win32CommandLineUTF8Arguments fpl__Win32ParseWideArguments(LPWSTR cmdLine, const bool appendExecutable) {
26867 fpl__Win32CommandLineUTF8Arguments args = fplZeroInit;
26868
26869 // @NOTE(final): Temporary load and unload shell32 for parsing the arguments
26870 HMODULE shellapiLibrary = LoadLibraryA("shell32.dll");
26871 if (shellapiLibrary != fpl_null) {
26872 fpl__win32_func_CommandLineToArgvW *commandLineToArgvW = (fpl__win32_func_CommandLineToArgvW *)(void *)GetProcAddress(shellapiLibrary, "CommandLineToArgvW");
26873 if (commandLineToArgvW != fpl_null) {
26874 // Parse executable arguments
26875 int cmdLineLen = lstrlenW(cmdLine);
26876 int executableFilePathArgumentCount = 0;
26877 wchar_t **executableFilePathArgs = NULL;
26878 size_t executableFilePathLen = 0;
26879 if (appendExecutable || (cmdLineLen == 0)) {
26880 executableFilePathArgumentCount = 0;
26881 executableFilePathArgs = commandLineToArgvW(L"", &executableFilePathArgumentCount);
26882 executableFilePathLen = 0;
26883 for (int i = 0; i < executableFilePathArgumentCount; ++i) {
26884 if (i > 0) {
26885 // Include whitespace
26886 executableFilePathLen++;
26887 }
26888 size_t sourceLen = lstrlenW(executableFilePathArgs[i]);
26889 int destLen = WideCharToMultiByte(CP_UTF8, 0, executableFilePathArgs[i], (int)sourceLen, fpl_null, 0, fpl_null, fpl_null);
26890 executableFilePathLen += destLen;
26891 }
26892 }
26893
26894 // Parse arguments and add to total UTF8 string length
26895 int actualArgumentCount = 0;
26896 wchar_t **actualArgs = fpl_null;
26897 size_t actualArgumentsLen = 0;
26898 if (cmdLine != fpl_null && cmdLineLen > 0) {
26899 actualArgs = commandLineToArgvW(cmdLine, &actualArgumentCount);
26900 for (int i = 0; i < actualArgumentCount; ++i) {
26901 size_t sourceLen = lstrlenW(actualArgs[i]);
26902 int destLen = WideCharToMultiByte(CP_UTF8, 0, actualArgs[i], (int)sourceLen, fpl_null, 0, fpl_null, fpl_null);
26903 actualArgumentsLen += destLen;
26904 }
26905 }
26906
26907 // Calculate argument
26908 uint32_t totalArgumentCount = 0;
26909 if (executableFilePathArgumentCount > 0) {
26910 totalArgumentCount++;
26911 }
26912 totalArgumentCount += actualArgumentCount;
26913
26914 // @NOTE(final): We allocate one memory block that contains
26915 // - The arguments as one string, each terminated by zero-character -> char*
26916 // - A padding
26917 // - The size of the string array -> char**
26918 size_t totalStringLen = executableFilePathLen + actualArgumentsLen + totalArgumentCount;
26919 size_t singleArgStringSize = sizeof(char) * (totalStringLen);
26920 size_t arbitaryPadding = 64;
26921 size_t argArraySize = sizeof(char **) * totalArgumentCount;
26922 size_t totalArgSize = singleArgStringSize + arbitaryPadding + argArraySize;
26923
26924 // @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.
26925 args.count = totalArgumentCount;
26926 args.mem = (uint8_t *)fplMemoryAllocate(totalArgSize);
26927 args.args = (char **)((uint8_t *)args.mem + singleArgStringSize + arbitaryPadding);
26928
26929 // Convert executable path to UTF8 and add it, if needed
26930 char *destArg = (char *)args.mem;
26931 int startArgIndex = 0;
26932 if (executableFilePathArgumentCount > 0)
26933 {
26934 args.args[startArgIndex++] = destArg;
26935 for (int i = 0; i < executableFilePathArgumentCount; ++i) {
26936 if (i > 0) {
26937 *destArg++ = ' ';
26938 }
26939 wchar_t *sourceArg = executableFilePathArgs[i];
26940 size_t sourceArgLen = lstrlenW(sourceArg);
26941 int destArgLen = WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, fpl_null, 0, fpl_null, fpl_null);
26942 WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, destArg, destArgLen, fpl_null, fpl_null);
26943 destArg += destArgLen;
26944 }
26945 *destArg++ = 0;
26946 LocalFree(executableFilePathArgs);
26947 }
26948
26949 // Convert actual arguments to UTF8 and add it, if needed
26950 if (actualArgumentCount > 0) {
26951 fplAssert(actualArgs != fpl_null);
26952 for (int i = 0; i < actualArgumentCount; ++i) {
26953 args.args[startArgIndex++] = destArg;
26954 wchar_t *sourceArg = actualArgs[i];
26955 size_t sourceArgLen = lstrlenW(sourceArg);
26956 int destArgLen = WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, fpl_null, 0, fpl_null, fpl_null);
26957 WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, destArg, destArgLen, fpl_null, fpl_null);
26958 destArg += destArgLen;
26959 *destArg++ = 0;
26960 }
26961 LocalFree(actualArgs);
26962 }
26963 }
26964 FreeLibrary(shellapiLibrary);
26965 shellapiLibrary = fpl_null;
26966 }
26967 return(args);
26968}
26969
26970fpl_internal fpl__Win32CommandLineUTF8Arguments fpl__Win32ParseAnsiArguments(LPSTR cmdLine, const bool appendExecutable) {
26971 fpl__Win32CommandLineUTF8Arguments result;
26972 if (cmdLine != fpl_null) {
26973 size_t ansiSourceLen = fplGetStringLength(cmdLine);
26974 int wideDestLen = MultiByteToWideChar(CP_ACP, 0, cmdLine, (int)ansiSourceLen, fpl_null, 0);
26975 // @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.
26976 wchar_t *wideCmdLine = (wchar_t *)fplMemoryAllocate(sizeof(wchar_t) * (wideDestLen + 1));
26977 MultiByteToWideChar(CP_ACP, 0, cmdLine, (int)ansiSourceLen, wideCmdLine, wideDestLen);
26978 wideCmdLine[wideDestLen] = 0;
26979 result = fpl__Win32ParseWideArguments(wideCmdLine, appendExecutable);
26980 fplMemoryFree(wideCmdLine);
26981 } else {
26982 wchar_t tmp[1] = { 0 };
26983 result = fpl__Win32ParseWideArguments(tmp, appendExecutable);
26984 }
26985 return(result);
26986}
26987
26988#if !defined(FPL_NO_CRT)
26989# include <stdio.h>
26990#endif
26991
26992fpl_internal void fpl__Win32FreeConsole(void) {
26993 HWND consoleHandle = GetConsoleWindow();
26994 if (consoleHandle != fpl_null) {
26995 FreeConsole();
26996 }
26997}
26998
26999fpl_internal void fpl__Win32InitConsole(void) {
27000 HWND consoleHandle = GetConsoleWindow();
27001 if (consoleHandle == fpl_null) {
27002 // Create or attach console
27003 AllocConsole();
27004 AttachConsole(GetCurrentProcessId());
27005
27006 // Redirect out/in/err to console
27007 HANDLE hConOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
27008 HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
27009 SetStdHandle(STD_OUTPUT_HANDLE, hConOut);
27010 SetStdHandle(STD_ERROR_HANDLE, hConOut);
27011 SetStdHandle(STD_INPUT_HANDLE, hConIn);
27012
27013#if !defined(FPL_NO_CRT)
27014 FILE *dummy;
27015 freopen_s(&dummy, "CONIN$", "r", stdin);
27016 freopen_s(&dummy, "CONOUT$", "w", stderr);
27017 freopen_s(&dummy, "CONOUT$", "w", stdout);
27018#endif
27019 }
27020}
27021
27022# if defined(FPL_NO_CRT)
27023//
27024// Win32 without CRT
27025//
27026# if defined(FPL_APPTYPE_WINDOW)
27027# if defined(UNICODE)
27028void __stdcall wWinMainCRTStartup(void) {
27029 fpl__Win32InitConsole();
27030 LPWSTR argsW = GetCommandLineW();
27031 int result = wWinMain(GetModuleHandleW(fpl_null), fpl_null, argsW, SW_SHOW);
27032 fpl__Win32FreeConsole();
27033 ExitProcess(result);
27034}
27035# else
27036void __stdcall WinMainCRTStartup(void) {
27037 fpl__Win32InitConsole();
27038 LPSTR argsA = GetCommandLineA();
27039 int result = WinMain(GetModuleHandleA(fpl_null), fpl_null, argsA, SW_SHOW);
27040 fpl__Win32FreeConsole();
27041 ExitProcess(result);
27042}
27043# endif // UNICODE
27044# elif defined(FPL_APPTYPE_CONSOLE)
27045void __stdcall mainCRTStartup(void) {
27046 fpl__Win32InitConsole();
27047 fpl__Win32CommandLineUTF8Arguments args;
27048# if defined(UNICODE)
27049 LPWSTR argsW = GetCommandLineW();
27050 args = fpl__Win32ParseWideArguments(argsW, false);
27051# else
27052 LPSTR argsA = GetCommandLineA();
27053 args = fpl__Win32ParseAnsiArguments(argsA, false);
27054# endif
27055 int result = main(args.count, args.args);
27056 fplMemoryFree(args.mem);
27057 fpl__Win32FreeConsole();
27058 ExitProcess(result);
27059}
27060# else
27061# error "Application type not set!"
27062# endif // FPL_APPTYPE
27063
27064# else
27065//
27066// Win32 with CRT
27067//
27068# if defined(UNICODE)
27069int WINAPI wWinMain(HINSTANCE appInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow) {
27070 fpl__Win32InitConsole();
27071 fpl__Win32CommandLineUTF8Arguments args = fpl__Win32ParseWideArguments(cmdLine, true);
27072 int result = main(args.count, args.args);
27073 fplMemoryFree(args.mem);
27074 fpl__Win32FreeConsole();
27075 return(result);
27076}
27077# else
27078int WINAPI WinMain(HINSTANCE appInstance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow) {
27079 fpl__Win32InitConsole();
27080 fpl__Win32CommandLineUTF8Arguments args = fpl__Win32ParseAnsiArguments(cmdLine, true);
27081 int result = main(args.count, args.args);
27082 fplMemoryFree(args.mem);
27083 fpl__Win32FreeConsole();
27084 return(result);
27085}
27086# endif // UNICODE
27087
27088# endif // FPL_NO_CRT
27089
27090# endif // FPL_PLATFORM_WINDOWS
27091
27092#endif // FPL_ENTRYPOINT && !FPL__ENTRYPOINT_IMPLEMENTED
27093
27094// Undef useless constants for callers
27095#if !defined(FPL_NO_UNDEF)
27096
27097# if defined(FPL_SUBPLATFORM_X11)
27098# undef Bool
27099# undef Expose
27100# undef KeyPress
27101# undef KeyRelease
27102# undef FocusIn
27103# undef FocusOut
27104# undef FontChange
27105# undef None
27106# undef Success
27107# undef Status
27108# undef Unsorted
27109# endif // FPL_SUBPLATFORM_X11
27110
27111# if defined(FPL_PLATFORM_WINDOWS)
27112# undef TRUE
27113# undef FALSE
27114# undef far
27115# undef near
27116# undef IN
27117# undef OUT
27118# undef OPTIONAL
27119# endif // FPL_SUBPLATFORM_X11
27120
27121#endif // !FPL_NO_UNDEF
27122
27124
27125// end-of-file
int32_t fpl_b32
Defines a integer based boolean that has a width of 32-bit.
fplX86InstructionSetLevel
Defines the X86 instruction sets up to AVX512.
@ fplX86InstructionSetLevel_SSSE3
SSE / SSE2 / SSE3 + (SSSE3)
@ fplX86InstructionSetLevel_None
None or not an x86 instruction set.
@ fplX86InstructionSetLevel_AVX512
SSE / SSE2 / SSE3 / SSE4.1 / SSE4.2 / AVX / AVX2 + (AVX512)
@ fplX86InstructionSetLevel_AVX2
SSE / SSE2 / SSE3 / SSE4.1 / SSE4.2 / AVX + (AVX2)
@ fplX86InstructionSetLevel_SSE3
SSE / SSE2 + (SSE3)
@ fplX86InstructionSetLevel_SSE
SSE.
@ fplX86InstructionSetLevel_IA32
32-Bit Instructions (80386)
@ fplX86InstructionSetLevel_SSE4_1
SSE / SSE2 / SSE3 + (SSE4.1)
@ fplX86InstructionSetLevel_AVX
SSE / SSE2 / SSE3 / SSE4.1 / SSE4.2 + (AVX)
@ fplX86InstructionSetLevel_SSE2
SSE + (SSE2)
@ fplX86InstructionSetLevel_SSE4_2
SSE / SSE2 / SSE3 / SSE4.1 + (SSE4.2)
#define fpl_null
Defines a NULL pointer value that works in C/C++.
#define fplAsm
A assembler compiler instruction (asm).
#define FPL_ENUM_AS_FLAGS_OPERATORS(type)
Internal macro used to create required enum operators for C++.
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 void fplAtomicWriteFence(void)
Inserts a memory write fence/barrier.
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 void fplAtomicReadWriteFence(void)
Inserts a memory read and write fence/barrier.
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 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_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 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 fplAtomicReadFence(void)
Inserts a memory read fence/barrier.
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 fplAudioChannelLayout fplGetDefaultAudioChannelLayoutFromChannels(const uint16_t channelCount)
Returns the default audio channel layout from the specified channel count.
fpl_common_api fplAudioResultType fplPlayAudio(void)
Start playing asynchronous audio.
fpl_common_api const char * fplGetAudioHardwareDeviceName(void)
Retrieves the audio device name for the current audio device.
fpl_common_api fplAudioResultType fplStopAudio(void)
Stop playing asynchronous audio.
fpl_common_api fplAudioResultType fplAudioInit(fplAudioSettings *audioSettings)
Re/Initializes the audio system with the specified audio settings.
fpl_common_api uint32_t fplGetAudioDevices(const uint32_t maxDeviceCount, const uint32_t deviceInfoSize, fplAudioDeviceInfo *outDevices)
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 fplAudioBackendType fplGetAudioBackendType(void)
Gets the current audio backend type.
fpl_common_api fplAudioFormatU64 fplEncodeAudioFormatU64(const uint32_t sampleRate, const uint16_t channels, const fplAudioFormatType type)
Creates an encoded audio format from the specified sample rate, channels, and type.
fpl_common_api uint32_t fplGetAudioFrameSizeInBytes(const fplAudioFormatType format, const uint16_t channelCount)
Computes the number of bytes required for one interleaved audio frame - containing all the channels.
fpl_common_api const char * fplGetAudioBackendName(const fplAudioBackendType backendType)
Gets the string that represents the given audio backend type.
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 fplAudioLatencyType fplGetAudioLatencyType(const fplAudioMode mode)
Gets the audio latency type from the specified audio mode.
fpl_common_api bool fplAudioRelease(void)
Unloads/Releases the audio system.
fpl_common_api uint32_t fplGetAudioBufferSizeInFrames(const uint32_t sampleRate, const uint32_t bufferSizeInMilliSeconds)
Computes the total number of frames for a given sample rate and buffer size.
fpl_common_api uint32_t fplGetAudioBufferSizeInMilliseconds(const uint32_t sampleRate, const uint32_t frameCount)
Computes the duration in milliseconds for the given sample rate and frame count.
fpl_common_api bool fplGetAudioHardwareDevice(fplAudioDeviceInfo *outDevice)
Retrieves the audio device info for the current audio device.
fpl_common_api bool fplGetAudioDeviceInfo(const fplAudioDeviceID *deviceId, fplAudioDeviceInfoExtended *outDeviceInfo)
Gets the full audio device info extended for the specified audio device ID.
fpl_common_api uint16_t fplGetAudioChannelsFromLayout(const fplAudioChannelLayout channelLayout)
Returns the number of channels from the specified audio channel layout.
fpl_common_api uint32_t fplGetTargetAudioFrameCount(const uint32_t inputFrameCount, const uint32_t inputSampleRate, const uint32_t outputSampleRate)
Computes the target number of audio frames from the specified input frame count and sample rate and t...
fpl_common_api fplAudioShareMode fplGetAudioShareMode(const fplAudioMode mode)
Gets the audio share mode from the specified audio mode.
fpl_common_api bool fplDecodeAudioFormatU64(const fplAudioFormatU64 format64, uint32_t *outSampleRate, uint16_t *outChannels, fplAudioFormatType *outType)
Decodes the specified encoded audio format to the specified sample rate, channels,...
fpl_common_api bool fplGetAudioHardwareFormat(fplAudioFormat *outFormat)
Retrieves the native audio format for the current audio device.
fpl_common_api bool fplGetAudioChannelMap(fplAudioChannelMap *outMapping)
Gets the audio channels mapping table.
fpl_common_api fplAudioMode fplCreateAudioMode(const fplAudioLatencyType latencyType, const fplAudioShareMode shareMode)
Creates an audio mode from the specified audio latency type and share mode.
fplAudioResultType
An enumeration of audio results.
fpl_common_api uint32_t fplGetAudioBufferSizeInBytes(const fplAudioFormatType format, const uint16_t channelCount, const uint32_t frameCount)
Computes the total number of bytes for the buffer and the given parameters.
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_NotImplemented
Feature or device is not implemented.
@ fplAudioResultType_NoBackendsFound
No audio backends found.
@ fplAudioResultType_DeviceByIdNotFound
Audio device by id was not found.
@ 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 audio result type.
@ fplAudioResultType_First
First audio result type.
@ fplAudioResultType_OutOfMemory
No memory available.
@ 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_InvalidArguments
Invalid arguments are passed to an audio function.
@ fplAudioResultType_UnsuportedDeviceFormat
The audio device format is unsupported.
@ fplAudioResultType_UnsetAudioBufferSize
The audio buffer size is not set.
@ fplAudioResultType_DeviceFailure
Error occurred while initializing an audio device.
@ fplAudioResultType_UnsetAudioFormat
The audio format type is not set.
@ fplAudioResultType_SystemNotInitialized
The audio system is not initialized.
@ 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(void)
Waits for a character to be typed in the console input and returns 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 a 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 void fplErrorsClear(void)
Clears all the current errors in the platform.
fpl_common_api const char * fplGetErrorByIndex(const size_t index)
Gets the last error string from the given index.
fpl_common_api const char * fplGetLastError(void)
Gets the last internal error string.
fpl_common_api size_t fplGetErrorCount(void)
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)
Moves 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)
Creates 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.
fplFileAttributeFlags
An enumeration 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_platform_api size_t fplCPUGetCoreCount(void)
Retrieves the total number of processor cores.
fpl_common_api uint64_t fplCPUXCR0(void)
Gets the x86 extended control register for index zero.
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_common_api uint64_t fplCPURDTSC(void)
Reads the current time stamp counter (RDTSC).
fplCPUArchType
An enumeration of architecture types.
fpl_platform_api fplCPUArchType fplCPUGetArchitecture(void)
Gets the processor architecture type.
fpl_common_api bool fplCPUGetCapabilities(fplCPUCapabilities *outCaps)
Gets the capabilities of the processor.
fplCPUCapabilitiesType
An enumeration of CPU types.
fpl_common_api const char * fplGetCPUCapabilitiesTypeName(const fplCPUCapabilitiesType type)
Gets the name of the specified fplCPUCapabilitiesType.
fpl_common_api bool fplCPUID(const uint32_t functionId, fplCPUIDLeaf *outLeaf)
Queries the x86 CPUID leaf register (EAX, EBX, ECX, EDX) for the given function id.
@ 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.
@ fplCPUCapabilitiesType_Last
Last fplCPUCapabilitiesType.
@ fplCPUCapabilitiesType_X86
x86 type.
@ fplCPUCapabilitiesType_First
First fplCPUCapabilitiesType.
@ fplCPUCapabilitiesType_ARM
ARM type.
@ fplCPUCapabilitiesType_Unknown
Unknown type.
#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
An 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 void fplSetLogSettings(const fplLogSettings *params)
Overwrites the current log settings.
fpl_common_api const fplLogSettings * fplGetLogSettings(void)
Gets the current log settings.
void fpl_log_func_callback(const char *funcName, const int lineNumber, const fplLogLevel level, const char *message)
A function definition for a callback for printing a log message.
fplLogWriterFlags
An enumeration of log writer flags.
fpl_common_api fplLogLevel fplGetMaxLogLevel(void)
Gets the current maximum allowed log level.
@ 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 fplStackAllocate(size)
Manually allocate the number of specified bytes of memory on the stack.
#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()
Gets a value indicating whether the current platform is big-endian or not.
#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.
#define fplStructField
Defines a single field in a struct.
#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, that is aligned to the operating system...
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 platform's 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 bool fplPlatformInit(const fplInitFlags initFlags, const fplSettings *initSettings)
Initializes the platform layer.
fpl_common_api bool fplIsPlatformInitialized(void)
Gets a value indicating whether the platform is initialized.
fpl_common_api fplPlatformType fplGetPlatformType(void)
Gets the type of the platform.
fpl_common_api void fplPlatformRelease(void)
Releases the resources allocated by the platform layer.
fpl_common_api fplPlatformResultType fplGetPlatformResult(void)
Gets the result type of the platform initialization.
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 information 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.
uint64_t fplAudioFormatU64
Encoded audio format in 64-bit ([63] Unused 8-bit, Type 8-bit, Channels 16-bit, Sample rate 32-bit [0...
fplAudioMode
An enumeration of audio modes that combines conservative/latency and exclusive/shared.
fplImageType
An enumeration of image types.
uint32_t fpl_audio_client_read_callback(const fplAudioFormat *deviceFormat, const uint32_t frameCount, void *outputSamples, void *userData)
A function definition for a callback that is called to read audio samples from the client.
fplInitFlags
An enumeration of initialization flags.
fpl_common_api fplSettings fplMakeDefaultSettings(void)
Creates a full settings structure containing default values.
fplPlatformResultType
An enumeration of platform result types.
fplVulkanValidationLayerMode
An enumeration of Vulkan validation layer modes.
fplAudioFormatType
An enumeration of audio format types.
void fpl_memory_release_callback(void *userData, void *ptr)
A function definition for a custom memory release callback.
fplMemoryAllocationMode
An enumeration of dynamic memory allocation modes.
fpl_common_api const char * fplGetAudioChannelTypeName(const fplAudioChannelType type)
Gets the name of the specified audio channel type.
fplOpenGLCompabilityFlags
An enumeration of OpenGL compatibility flags.
bool fpl_window_event_callback(const fplPlatformType platformType, void *windowState, void *rawEventData, void *userData)
A function definition for a callback executed for each raw window event.
fpl_window_event_callback fpl_window_exposed_callback
A function definition for a callback executed when the window needs to be exposed/repainted.
fplAudioShareMode
An enumeration of audio share modes.
void * fpl_memory_allocate_callback(void *userData, const size_t size, const size_t alignment)
A function definition for a custom memory allocation callback.
fpl_common_api fplColor32 fplCreateColorRGBA(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a)
Creates a 32-bit color structure from the specified R, G, B, A components.
fpl_common_api const fplSettings * fplGetCurrentSettings(void)
Gets the current settings.
#define FPL_MAX_AUDIO_CHANNEL_COUNT
Maximum number of audio channels.
void fplVulkanValidationLayerCallback(void *userData, const char *message, const uint32_t messageSeverity, const uint32_t messageType, const void *debugUtilsMessengerCallbackData)
A function definition for the debug callback that is called, when the validation layer writes somethi...
fpl_common_api void fplSetDefaultConsoleSettings(fplConsoleSettings *console)
Resets the given console settings container to default settings.
fplAudioDefaultFields
An enumeration of audio default fields.
fplAudioChannelType
An enumeration of audio channel types.
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.
fplAudioLatencyType
An enumeration of audio latency types.
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.
fplAudioChannelLayout
An enumeration of audio channel layouts.
fplVulkanValidationSeverity
An enumeration of Vulkan validation layer logging severity.
@ fplAudioMode_Exclusive_LowLatency
Exclusive Low Latency.
@ fplAudioMode_Exclusive_Conservative
Exclusive Conservative.
@ fplAudioMode_Shared_LowLatency
Shared Low Latency.
@ fplAudioMode_Shared_Conservative
Shared Conservative.
@ 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_OutOfMemory
Failed allocating required memory.
@ 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.
@ fplVulkanValidationLayerMode_Required
Enable validations and stop when it's not supported.
@ fplVulkanValidationLayerMode_Disabled
Do not use the validation.
@ fplVulkanValidationLayerMode_Optional
Enable validations when it's 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.
@ fplMemoryAllocationMode_Custom
Use custom memory allocation.
@ fplMemoryAllocationMode_Automatic
Use OS memory allocation.
@ fplOpenGLCompabilityFlags_Legacy
Use legacy context.
@ fplOpenGLCompabilityFlags_Compability
Use compatibility profile.
@ fplOpenGLCompabilityFlags_Core
Use core profile.
@ fplOpenGLCompabilityFlags_Forward
Remove features marked as deprecated.
@ fplAudioShareMode_Exclusive
Exclusive mode.
@ fplAudioShareMode_Shared
Shared mode.
@ fplAudioDefaultFields_Channels
Number of channels is default.
@ fplAudioDefaultFields_Periods
Number of periods is default.
@ fplAudioDefaultFields_ChannelLayout
Audio layout is default.
@ fplAudioDefaultFields_SampleRate
Samples per second is default.
@ fplAudioDefaultFields_Type
Audio format is default.
@ fplAudioDefaultFields_None
No default fields.
@ fplAudioDefaultFields_BufferSize
Buffer size is default.
@ fplAudioChannelType_AUX13
AUX 13.
@ fplAudioChannelType_AUX8
AUX 8.
@ fplAudioChannelType_FrontLeftOfCenter
Front left of center.
@ fplAudioChannelType_AUX6
AUX 6.
@ fplAudioChannelType_FrontLeft
Front left.
@ fplAudioChannelType_FrontRightOfCenter
Front right of center.
@ fplAudioChannelType_SideLeft
Side left.
@ fplAudioChannelType_TopBackCenter
Top back center.
@ fplAudioChannelType_TopBackRight
Top back right.
@ fplAudioChannelType_AUX12
AUX 12.
@ fplAudioChannelType_AUX9
AUX 9.
@ fplAudioChannelType_BackLeft
Back left.
@ fplAudioChannelType_TopCenter
Top center.
@ fplAudioChannelType_AUX5
AUX 5.
@ fplAudioChannelType_SideRight
Side right.
@ fplAudioChannelType_AUX1
AUX 1.
@ fplAudioChannelType_AUX10
AUX 10.
@ fplAudioChannelType_FrontRight
Front right.
@ fplAudioChannelType_AUX11
AUX 11.
@ fplAudioChannelType_First
First audio channel type.
@ fplAudioChannelType_LowFrequency
Low Frequency.
@ fplAudioChannelType_TopFrontLeft
Top front left.
@ fplAudioChannelType_AUX14
AUX 14.
@ fplAudioChannelType_TopFrontCenter
Top front center.
@ fplAudioChannelType_Last
Last audio channel type.
@ fplAudioChannelType_TopFrontRight
Top front right.
@ fplAudioChannelType_BackRight
Back right.
@ fplAudioChannelType_AUX3
AUX 3.
@ fplAudioChannelType_FrontCenter
Front center.
@ fplAudioChannelType_AUX15
AUX 15.
@ fplAudioChannelType_None
No or unknown audio channel.
@ fplAudioChannelType_AUX0
AUX 0.
@ fplAudioChannelType_AUX7
AUX 7.
@ fplAudioChannelType_BackCenter
Back center.
@ fplAudioChannelType_TopBackLeft
Top back left.
@ fplAudioChannelType_AUX2
AUX 2.
@ fplAudioChannelType_AUX4
AUX 4.
@ fplAudioBackendType_First
First fplAudioBackendType.
@ fplAudioBackendType_Last
Last fplAudioBackendType.
@ fplAudioBackendType_Alsa
ALSA audio backend.
@ fplAudioBackendType_Custom
Custom audio backend.
@ fplAudioBackendType_None
No audio backend.
@ fplAudioBackendType_Auto
Auto detect audio backend.
@ fplAudioBackendType_DirectSound
DirectSound audio backend.
@ fplVideoBackendType_Vulkan
Vulkan video backend.
@ fplVideoBackendType_None
No video backend.
@ fplVideoBackendType_Software
Software video backend.
@ fplVideoBackendType_First
First video backend.
@ fplVideoBackendType_OpenGL
OpenGL video backend.
@ fplVideoBackendType_Last
Last video backend.
@ fplPlatformType_Linux
Linux platform.
@ fplPlatformType_Unknown
Unknown platform.
@ fplPlatformType_Last
Last fplPlatformType.
@ fplPlatformType_Windows
Windows platform.
@ fplPlatformType_First
First fplPlatformType.
@ fplPlatformType_Unix
Unix platform.
@ fplAudioLatencyType_Low
Low latency.
@ fplAudioLatencyType_Conservative
Conservative latency.
@ fplAudioChannelLayout_4_1
4.1 Audio Channel Layout (4.1, 5 Channels: Front/LFE/Back).
@ fplAudioChannelLayout_4_0_Surround
4.0 Surround Audio Channel Layout (4.0 Surround, 4 Channels: Front/F-Center/B-Center).
@ fplAudioChannelLayout_Automatic
Automatic Audio Channel Layout (based on number of channels and/or sound device).
@ fplAudioChannelLayout_Last
Last Audio Channel Layout.
@ fplAudioChannelLayout_2_1
2.1 Audio Channel Layout (2.1, 3 Channels: Front, LFE).
@ fplAudioChannelLayout_4_0_Quad
4.0 Quad Audio Channel Layout (4.0 Quad, 4 Channels: Front/Back).
@ fplAudioChannelLayout_First
First Audio Channel Layout.
@ fplAudioChannelLayout_Stereo
Stereo Audio Channel Layout (2.0, 2 Channels: Front).
@ fplAudioChannelLayout_5_0_Surround
5.0 Audio Channel Layout (5.0, 5 Channels: Front/Center/Back).
@ fplAudioChannelLayout_6_1
6.1 Audio Channel Layout (6.1, 7 Channels: Front/F-Center/LFE/B-Center/Side).
@ fplAudioChannelLayout_7_1
7.1 Audio Channel Layout (7.1, 8 Channels: Front/Center/LFE/Back/Side).
@ fplAudioChannelLayout_5_1
5.1 Audio Channel Layout (5.1, 6 Channels: Front/Center/LFE/Side).
@ fplAudioChannelLayout_Unsupported
Unsupported Audio Channel Layout.
@ fplAudioChannelLayout_3_0_Surround
3.0 Surround Audio Channel Layout (3.0, 3 Channels: Front/F-Center).
@ fplAudioChannelLayout_Mono
Mono Audio Channel Layout (1.0, Single Channel: Front).
@ fplVulkanValidationSeverity_Verbose
Log warning, error, info, and verbose.
@ fplVulkanValidationSeverity_Off
Log nothing.
@ fplVulkanValidationSeverity_All
Log everything.
@ fplVulkanValidationSeverity_Info
Log warning, error, and info.
@ 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 into 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 into 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 parameters 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)
Lets 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 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 for the given thread handle.
fpl_platform_api bool fplSetThreadPriority(fplThreadHandle *thread, const fplThreadPriority newPriority)
Changes the thread priority to the given one, for the given thread handle.
struct fplThreadHandle fplThreadHandle
Forward declared thread handle.
fpl_common_api fplThreadState fplGetThreadState(fplThreadHandle *thread)
Gets the current thread state for the given thread.
fplThreadStates
An enumeration of thread states.
fplThreadPriority
Defines all possible thread priorities.
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 woken 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)
Waits 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.
void fpl_run_thread_callback(const fplThreadHandle *thread, void *data)
A function definition for a callback to execute user code inside another thread.
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)
Waits until the given thread is done running or the given timeout has been reached.
fpl_common_api size_t fplGetUsedThreadCount(void)
Gets the number of used/active threads.
fpl_platform_api bool fplSemaphoreTryWait(fplSemaphoreHandle *semaphore)
Tries to wait for the semaphore until it gets signaled or returns immediately.
fpl_platform_api bool fplSignalWaitForOne(fplSignalHandle *signal, const fplTimeoutValue timeout)
Waits until the given signal is woken 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)
Waits until one of the given threads is done running or the given timeout has been reached.
fpl_common_api size_t fplGetAvailableThreadCount(void)
Gets the number of available threads.
fpl_platform_api void fplSignalDestroy(fplSignalHandle *signal)
Releases the given signal and clears the structure to zero.
fpl_platform_api bool fplThreadYield(void)
Lets the current thread yield execution to another thread that is ready to run on this core.
fpl_platform_api bool fplSignalReset(fplSignalHandle *signal)
Resets the signal.
fpl_common_api const fplThreadHandle * fplGetMainThread(void)
Gets the thread handle for the main thread.
fpl_platform_api uint32_t fplGetCurrentThreadId(void)
Gets the thread id for the current thread.
fpl_platform_api bool fplThreadTerminate(fplThreadHandle *thread)
Forces the given thread to stop and release all underlying resources.
fpl_platform_api bool fplMutexTryLock(fplMutexHandle *mutex)
Tries to lock the given mutex without blocking other threads.
fpl_platform_api bool fplConditionInit(fplConditionVariable *condition)
Initializes 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 fplTimestamp fplTimestampQuery(void)
Gets the current fplTimestamp with most precision, used for time delta measurements only.
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.
uint32_t fplTimeoutValue
A type definition for a timeout value in milliseconds.
fpl_platform_api fplMilliseconds fplMillisecondsQuery(void)
Gets the current system clock in milliseconds, since some fixed starting point (OS start,...
#define FPL_TIMEOUT_INFINITE
Infinite timeout constant.
fpl_common_api fplVideoBackBuffer * fplGetVideoBackBuffer(void)
Retrieves the pointer to the current video backbuffer.
fpl_common_api void fplVideoFlip(void)
Forces the window to be redrawn or to swap the back/front buffer.
fpl_common_api const fplVideoSurface * fplGetVideoSurface(void)
Gets the current video surface 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_common_api fplVideoBackendType fplGetVideoBackendType(void)
Gets the current video backend.
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 const char * fplGetVideoBackendName(fplVideoBackendType backendType)
Gets a string that represents the given video backend.
fpl_common_api fplVideoRect fplCreateVideoRectFromLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom)
Makes a video rectangle from a LTRB rectangle.
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 fplWindowState fplGetWindowState(void)
Gets the current window state.
fpl_platform_api bool fplIsWindowDecorated(void)
Gets the window decorated state as boolean.
fpl_platform_api bool fplIsWindowFloating(void)
Gets the window floating state as boolean.
fpl_platform_api bool fplWindowUpdate(void)
Clears the internal event queue and updates input devices if needed.
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.
fplWindowState
An enumeration containing the states of a window.
fpl_platform_api bool fplIsWindowResizable(void)
Gets the window resizable state as boolean.
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 fplWindowShutdown(void)
Closes the window and stops the event loop.
fpl_platform_api void fplSetWindowTitle(const char *title)
Changes the window title to the given string.
fpl_platform_api bool fplIsWindowRunning(void)
Gets the window running state as a boolean.
fpl_platform_api bool fplGetWindowSize(fplWindowSize *outSize)
Retrieves the inner window size.
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 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_common_api void fplSetWindowInputEvents(const bool enabled)
Enables or disables the input events for the window entirely.
fpl_platform_api bool fplDisableWindowFullscreen(void)
Switches the window back to window mode.
fpl_platform_api bool fplEnableWindowFullscreen(void)
Enables fullscreen mode on the nearest display.
fplWindowVisibilityState
An enumeration containing the visibility state of a window.
fpl_platform_api bool fplIsWindowFullscreen(void)
Gets the window fullscreen state as boolean.
@ 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 information 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 bool fplGetWindowDisplay(fplDisplayInfo *outInfo)
Gets information about the display for the FPL window.
fpl_platform_api size_t fplGetDisplayCount(void)
Gets the number of active displays.
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 ...
fplButtonState
An enumeration of button states.
fplMouseEventType
An enumeration of mouse event types (Move, ButtonDown, etc.).
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, etc.).
fplGamepadEventType
An enumeration of gamepad event types (Connected, Disconnected, StateChanged, etc....
fplKeyboardEventType
An enumeration of keyboard event types.
fpl_platform_api void fplPollEvents(void)
Polls all the events from the OS and clears the internal event queue.
fplEventType
An enumeration of event types (Window, Keyboard, Mouse, etc.).
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 held 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_NumPad4
NumPad 4 key.
@ fplKey_Oem3
'‘~’ for US.
@ fplKey_L
Letter L key.
@ fplKey_RightAlt
Right Alt key.
@ fplKey_Apps
Apps key.
@ fplKey_0
Number 0 key.
@ fplKey_NumPad0
NumPad 0 key.
@ fplKey_X
Letter X key.
@ fplKey_Oem4
'[{' for US.
@ fplKey_B
Letter B key.
@ fplKey_9
Number 9 key.
@ fplKey_Left
Left arrow key.
@ fplKey_NumPad6
NumPad 6 key.
@ fplKey_F1
Function key F1.
@ fplKey_Q
Letter Q key.
@ fplKey_Oem5
'|' for US.
@ fplKey_E
Letter E key.
@ fplKey_LeftSuper
Left Super key.
@ fplKey_Decimal
Decimal key.
@ fplKey_LeftAlt
Left Alt key.
@ fplKey_F18
Function key F18.
@ fplKey_Shift
Shift key.
@ fplKey_S
Letter S key.
@ fplKey_End
End key.
@ fplKey_NumLock
Num Lock key.
@ fplKey_Add
Add key.
@ fplKey_MediaPlayPause
Media Play/Pause key.
@ fplKey_F23
Function key F23.
@ fplKey_Multiply
Multiply key.
@ fplKey_RightSuper
Right Super key.
@ fplKey_F12
Function key F12.
@ fplKey_MediaStop
Media Stop key.
@ fplKey_F14
Function key F14.
@ fplKey_Oem7
''"' for US.
@ fplKey_J
Letter J key.
@ fplKey_MediaPrevTrack
Media Previous Track key.
@ fplKey_I
Letter I key.
@ fplKey_Substract
Subtract key.
@ fplKey_Select
Select key.
@ fplKey_F19
Function key F19.
@ fplKey_T
Letter T key.
@ fplKey_F6
Function key F6.
@ fplKey_NumPad7
NumPad 7 key.
@ fplKey_Home
Home key.
@ fplKey_F7
Function key F7.
@ fplKey_1
Number 1 key.
@ fplKey_NumPad2
NumPad 2 key.
@ fplKey_Tab
Tab key.
@ fplKey_Divide
Divide key.
@ fplKey_F8
Function key F8.
@ fplKey_OemPlus
'+' for any country.
@ fplKey_2
Number 2 key.
@ fplKey_PageDown
Page Down key.
@ fplKey_VolumeUp
Volume Up key.
@ fplKey_F10
Function key F10.
@ fplKey_Alt
Alt key.
@ fplKey_Insert
Insert key.
@ fplKey_R
Letter R key.
@ fplKey_Space
Space key.
@ fplKey_Return
Return key.
@ fplKey_Oem8
OEM specific key.
@ fplKey_6
Number 6 key.
@ fplKey_F9
Function key F9.
@ fplKey_Backspace
Backspace key.
@ fplKey_OemMinus
'-' for any country.
@ fplKey_Right
Right arrow key.
@ fplKey_MediaNextTrack
Media Next Track key.
@ fplKey_A
Letter A key.
@ fplKey_LeftControl
Left Control key.
@ fplKey_Oem1
'/?' for US.
@ fplKey_Pause
Pause key.
@ fplKey_OemPeriod
'.' for any country.
@ fplKey_Sleep
Sleep key.
@ fplKey_NumPad5
NumPad 5 key.
@ fplKey_F21
Function key F21.
@ fplKey_F
Letter F key.
@ fplKey_F20
Function key F20.
@ fplKey_M
Letter M key.
@ fplKey_F16
Function key F16.
@ fplKey_NumPad8
NumPad 8 key.
@ fplKey_NumPad3
NumPad 3 key.
@ fplKey_Clear
Clear key.
@ fplKey_U
Letter U key.
@ fplKey_F11
Function key F11.
@ fplKey_RightControl
Right Control key.
@ fplKey_Up
Up arrow key.
@ fplKey_F2
Function key F2.
@ fplKey_D
Letter D key.
@ fplKey_K
Letter K key.
@ fplKey_8
Number 8 key.
@ fplKey_5
Number 5 key.
@ fplKey_F24
Function key F24.
@ fplKey_F13
Function key F13.
@ fplKey_Y
Letter Y key.
@ fplKey_G
Letter G key.
@ fplKey_VolumeDown
Volume Down key.
@ fplKey_H
Letter H key.
@ fplKey_Print
Print key.
@ fplKey_Z
Letter Z key.
@ fplKey_Escape
Escape key.
@ fplKey_7
Number 7 key.
@ fplKey_C
Letter C key.
@ fplKey_F15
Function key F15.
@ fplKey_F22
Function key F22.
@ fplKey_F5
Function key F5.
@ fplKey_Snapshot
Snapshot key.
@ fplKey_F17
Function key F17.
@ fplKey_3
Number 3 key.
@ fplKey_Delete
Delete key.
@ fplKey_Control
Control key.
@ fplKey_NumPad1
NumPad 1 key.
@ fplKey_LeftShift
Left Shift key.
@ fplKey_Scroll
Scroll Lock key.
@ fplKey_Execute
Execute key.
@ fplKey_NumPad9
NumPad 9 key.
@ fplKey_Separator
Separator key.
@ fplKey_Down
Down arrow key.
@ fplKey_O
Letter O key.
@ fplKey_Oem6
']}' for US.
@ fplKey_Help
Help key.
@ fplKey_4
Number 4 key.
@ fplKey_W
Letter W key.
@ fplKey_CapsLock
Caps Lock key.
@ fplKey_F4
Function key F4.
@ fplKey_N
Letter N key.
@ fplKey_F3
Function key F3.
@ fplKey_OemComma
',' for any country.
@ fplKey_P
Letter P key.
@ fplKey_RightShift
Right Shift key.
@ fplKey_V
Letter V key.
@ fplKey_PageUp
Page Up key.
@ fplKey_Oem2
'/?' for US.
@ fplKey_VolumeMute
Volume Mute key.
@ 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.
Container storing the capabilities of an ARM CPU.
bool hasSHA2
Has SHA2 support.
bool hasAES
Has AES support.
bool hasPMULL
Has PMULL support.
bool hasNEON
Has NEON support.
bool hasSHA1
Has SHA1 support.
bool hasCRC32
Has CRC32 support.
Stores the mapping of all audio channels to an audio speaker.
fplAudioChannelType speakers[FPL_MAX_AUDIO_CHANNEL_COUNT]
The mapping from channel 0-31 to an audio speaker.
Stores the fplAudioDeviceInfo and the supported formats.
fplAudioDeviceInfo info
The base fplAudioDeviceInfo.
fplAudioFormatU64 supportedFormats[63]
Supported audio formats.
size_t supportedFormatCount
Number of supported formats.
Stores the name and the ID of the audio device.
bool isDefault
A value indicating whether this is a default device or not.
fplAudioDeviceID id
Device ID.
char name[FPL_MAX_NAME_LENGTH - 1]
Device name.
Stores audio format properties, such as type, sample rate, channels, etc.
fplAudioMode mode
Audio mode.
uint32_t bufferSizeInMilliseconds
Buffer size in milliseconds (uses default when zero, second choice).
uint16_t periods
Number of periods (uses default when zero).
fplAudioDefaultFields defaultFields
Audio default fields flags.
uint16_t channels
Number of channels (uses default when zero).
fplAudioChannelLayout channelLayout
Audio channel layout (uses default when auto).
fplAudioFormatType type
Audio format (uses default when zero).
uint32_t bufferSizeInFrames
Buffer size in frames (uses default when zero, first choice).
uint32_t sampleRate
Samples per second (uses default when zero).
Stores audio settings, such as format, device info, callbacks, backend, etc.
fplAudioDeviceInfo targetDevice
The target device.
void * clientUserData
User data pointer for client read callback.
fpl_b32 startAuto
Start playing of audio samples after platform initialization automatically.
fplAudioFormat targetFormat
The target format.
fpl_audio_client_read_callback * clientReadCallback
The callback for retrieving audio data from the client.
fpl_b32 manualLoad
Manual loading the audio system by fplAudioInit() and unload using fplAudioRelease().
fpl_b32 stopAuto
Stop playing of audio samples before platform release automatically.
fplAudioBackendType backend
The targeted backend.
fplSpecificAudioSettings specific
Specific settings.
Container storing the capabilities of a CPU.
fplCPUCapabilitiesType type
The capabilities type.
fplX86CPUCapabilities x86
x86 CPU capabilities.
uint8_t unused[28]
Unused.
fplARMCPUCapabilities arm
ARM CPU capabilities.
Stores the condition variable structure.
fpl_b32 isValid
Is it valid.
fplInternalConditionVariable internalHandle
The internal condition handle.
Stores the title and options for the console.
char title[FPL_MAX_NAME_LENGTH]
Console title.
A struct containing information 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.
Stores 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 physically 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.
Stores graphics API settings.
fplOpenGLSettings opengl
OpenGL settings.
fplVulkanSettings vulkan
Vulkan settings.
int dummy
Field for preventing union to be empty.
Stores data for working with an image source.
uint32_t width
Width in pixels.
fplImageType type
Image type.
const uint8_t * data
Reference to the source data.
uint32_t height
Height in pixels.
Stores input settings.
uint32_t controllerDetectionFrequency
Frequency in ms for detecting new or removed controllers (Default: 1000).
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.
Stores 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).
Stores console logging properties.
int dummy
Field for preventing struct to be empty.
Stores custom logging properties.
fpl_log_func_callback * callback
User callback.
Stores log writer settings.
fplLogWriterFlags flags
Flags.
fplLogWriterConsole console
Console.
fplLogWriterCustom custom
Custom.
Stores settings for 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.
Stores properties that represents any block of memory.
size_t size
The size of the allocated memory.
void * base
The base reference.
Stores information about the 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.
Stores 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.
Stores the mutex handle structure.
fplInternalMutexHandle internalHandle
The internal mutex handle.
fpl_b32 isValid
Is it valid.
Stores 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).
Stores 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
Compatibility flags.
uint8_t multiSamplingCount
Multisampling count.
uint32_t majorVersion
Desired major version.
Stores the semaphore handle structure.
fplInternalSemaphoreHandle internalHandle
The internal semaphore handle.
fpl_b32 isValid
Is it valid.
Stores 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.
Stores the signal handle structure.
fplInternalSignalHandle internalHandle
The internal signal handle.
fpl_b32 isValid
Is it valid.
Stores 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.
Stores 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.
Stores version information.
fplVersionNumberPart fix
Fix version.
union fplVersionInfo::@365135000334316031133255004035011055373020307002 version
Full version.
fplVersionNumberPart build
Build version.
fplVersionNumberPart values[4]
Version number parts.
fplVersionNumberPart major
Major version.
struct fplVersionInfo::@365135000334316031133255004035011055373020307002::@063163116360264167063231010243176073330000023206 parts
The version parts.
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 rectangle's 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.
Stores 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).
Stores Vulkan video settings.
fplVersionInfo engineVersion
The engine version (only required if fplVulkanSettings::instanceHandle is null).
fplVersionInfo apiVersion
The preferred Vulkan API version (only required if fplVulkanSettings::instanceHandle is null).
fplVulkanValidationLayerMode validationLayerMode
The validation layer mode fplVulkanValidationLayerMode.
const char * appName
The application name (only required if fplVulkanSettings::instanceHandle is null).
void * userData
User opaque user data that is passed to any validation layer 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 null).
const char * engineName
The engine name (only required if fplVulkanSettings::instanceHandle is null).
const char * libraryFile
Custom Vulkan driver library file name/path (null = Default Vulkan library).
fplVulkanValidationSeverity validationSeverity
The validation severity fplVulkanValidationSeverity.
const void * allocator
The Vulkan allocator (VkAllocationCallbacks).
fplVulkanValidationLayerCallback * validationLayerCallback
The validation layer callback fplVulkanValidationLayerCallback.
Stores the window callbacks.
void * eventUserData
User data pointer for the event callback.
fpl_window_event_callback * eventCallback
Event 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, do not touch.
size_t fileCount
Number of dropped in files.
const char ** files
File paths (Do not release this memory, it's 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.
Stores the position of a window.
int32_t left
Left position in screen coordinates.
int32_t top
Top position in screen coordinates.
Stores 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 from 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.
Stores the size of a window.
uint32_t width
Width in screen coordinates.
uint32_t height
Height in screen coordinates.
Container storing the capabilities of an x86 CPU.
bool hasSSE4_2
Has SSE4.2 support.
bool hasSSE
Has SSE support.
bool hasSSSE3
Has SSSE3 support.
bool hasAVX
Has AVX support.
bool hasAVX2
Has AVX2 support.
bool hasEM64T
Has EM64T support.
bool hasSSE4_1
Has SSE4.1 support.
bool hasAVX512
Has AVX512 support.
bool hasSSE3
Has SSE3 support.
bool hasBMI1
Has BMI1 support.
bool hasADX
Has ADX support.
bool hasAES_NI
Has AES-NI support.
bool hasSSE2
Has SSE2 support.
bool hasBMI2
Has BMI2 support.
bool hasSHA
Has SHA support.
bool hasF16C
Has F16C support.
bool hasFMA3
Has FMA3 support.
Stores the ID of the underlying backend.
uint8_t dummy[256]
Field for preventing union to be empty.
Container representing the 4-registers for a CPU-Leaf (EAX, EBX, ECX, EDX).
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.
struct fplColor32::@326013044226003007030237204232371201217365374302 components
The BGRA components.
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.
Stores the internal condition variable for any platform.
int dummy
Field for preventing union to be empty.
Stores the library handle for any platform.
A union containing the internal filehandle for any platform.
A union containing the internal filehandle for any platform.
Stores the internal mutex handle for any platform.
Stores the internal semaphore handle for any platform.
Stores the internal signal handle for any platform.
Stores the thread handle for any platform.
Stores backend-specific audio settings.
int dummy
Field for preventing union to be empty.
Stores 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.