gb
gb
===========================================================================
YOU MUST
#define GB_IMPLEMENTATION
in EXACTLY _one_ C or C++ file that includes this header, BEFORE the
include like this:
#define GB_IMPLEMENTATION
#include "gb.h"
#define GB_PLATFORM
#define GB_PLATFORM
#include "gb.h"
===========================================================================
LICENSE
This software is dual-licensed to the public domain and under the following
license: you are granted a perpetual, irrevocable license to copy, modify,
publish, and distribute this file as you see fit.
WARNING
- This library is _slightly_ experimental and features may not work as
expected.
- This also means that many functions are not documented.
CREDITS
Written by Ginger Bill
TODOS
- Remove CRT dependency for people who want that
- But do I really?
- Or make it only depend on the really needed stuff?
- Older compiler support?
- How old do you wanna go?
- Only support C90+extension and C99 not pure C89.
- File handling
- All files to be UTF-8 (even on windows)
- Better Virtual Memory handling
- Generic Heap Allocator (tcmalloc/dlmalloc/?)
- Fixed Heap Allocator
- Better UTF support and conversion
- Free List, best fit rather than first fit
- More date & time functions
VERSION HISTORY
0.33 - Minor fixes
0.32 - Minor fixes
0.31 - Add gb_file_remove
0.30 - Changes to gbThread (and gbMutex on Windows)
0.29 - Add extras for gbString
0.28 - Handle UCS2 correctly in Win32 part
0.27 - OSX fixes and Linux gbAffinity
0.26d - Minor changes to how gbFile works
0.26c - gb_str_to_f* fix
0.26b - Minor fixes
0.26a - gbString Fix
0.26 - Default allocator flags and generic hash table
0.25a - Fix UTF-8 stuff
0.25 - OS X gbPlatform Support (missing some things)
0.24b - Compile on OSX (excluding platform part)
0.24a - Minor additions
0.24 - Enum convention change
0.23 - Optional Windows.h removal (because I'm crazy)
0.22a - Remove gbVideoMode from gb_platform_init_*
0.22 - gbAffinity - (Missing Linux version)
0.21 - Platform Layer Restructuring
0.20 - Improve file io
0.19 - Clipboard Text
0.18a - Controller vibration
0.18 - Raw keyboard and mouse input for WIN32
0.17d - Fixed printf bug for strings
0.17c - Compile as 32 bit
0.17b - Change formating style because why not?
0.17a - Dropped C90 Support (For numerous reasons)
0.17 - Instantiated Hash Table
0.16a - Minor code layout changes
0.16 - New file API and improved platform layer
0.15d - Linux Experimental Support (DON'T USE IT PLEASE)
0.15c - Linux Experimental Support (DON'T USE IT)
0.15b - C90 Support
0.15a - gb_atomic(32|64)_spin_(lock|unlock)
0.15 - Recursive "Mutex"; Key States; gbRandom
0.14 - Better File Handling and better printf (WIN32 Only)
0.13 - Highly experimental platform layer (WIN32 Only)
0.12b - Fix minor file bugs
0.12a - Compile as C++
0.12 - New File Handing System! No stdio or stdlib! (WIN32 Only)
0.11a - Add string precision and width (experimental)
0.11 - Started making stdio & stdlib optional (Not tested much)
0.10c - Fix gb_endian_swap32()
0.10b - Probable timing bug for gb_time_now()
0.10a - Work on multiple compilers
0.10 - Scratch Memory Allocator
0.09a - Faster Mutex and the Free List is slightly improved
0.09 - Basic Virtual Memory System and Dreadful Free List allocator
0.08a - Fix *_appendv bug
0.08 - Huge Overhaul!
0.07a - Fix alignment in gb_heap_allocator_proc
0.07 - Hash Table and Hashing Functions
0.06c - Better Documentation
0.06b - OS X Support
0.06a - Linux Support
0.06 - Windows GCC Support and MSVC x86 Support
0.05b - Formatting
0.05a - Minor function name changes
0.05 - Radix Sort for unsigned integers (TODO: Other primitives)
0.04 - Better UTF support and search/sort procs
0.03 - Completely change procedure naming convention
0.02a - Bug fixes
0.02 - Change naming convention and gbArray(Type)
0.01 - Initial Version
*/
#ifndef GB_INCLUDE_GB_H
#define GB_INCLUDE_GB_H
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(__cplusplus)
#define GB_EXTERN extern "C"
#else
#define GB_EXTERN extern
#endif
#if defined(_WIN32)
#define GB_DLL_EXPORT GB_EXTERN __declspec(dllexport)
#define GB_DLL_IMPORT GB_EXTERN __declspec(dllimport)
#else
#define GB_DLL_EXPORT GB_EXTERN __attribute__((visibility("default")))
#define GB_DLL_IMPORT GB_EXTERN
#endif
#ifndef GB_ENDIAN_ORDER
#define GB_ENDIAN_ORDER
// TODO(bill): Is the a good way or is it better to test for certain
compilers and macros?
#define GB_IS_BIG_ENDIAN (!*(u8*)&(u16){1})
#define GB_IS_LITTLE_ENDIAN (!GB_IS_BIG_ENDIAN)
#endif
#if defined(__linux__)
#ifndef GB_SYSTEM_LINUX
#define GB_SYSTEM_LINUX 1
#endif
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#ifndef GB_SYSTEM_FREEBSD
#define GB_SYSTEM_FREEBSD 1
#endif
#else
#error This UNIX operating system is not supported
#endif
#else
#error This operating system is not supported
#endif
#if defined(_MSC_VER)
#define GB_COMPILER_MSVC 1
#elif defined(__GNUC__)
#define GB_COMPILER_GCC 1
#elif defined(__clang__)
#define GB_COMPILER_CLANG 1
#else
#error Unknown compiler
#endif
#elif defined(__arm__)
#ifndef GB_CPU_ARM
#define GB_CPU_ARM 1
#endif
#ifndef GB_CACHE_LINE_SIZE
#define GB_CACHE_LINE_SIZE 64
#endif
#else
#error Unknown CPU Type
#endif
#ifndef GB_STATIC_ASSERT
#define GB_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!
(cond))*2-1]
// NOTE(bill): Token pasting madness!!
#define GB_STATIC_ASSERT2(cond, line) GB_STATIC_ASSERT3(cond,
static_assertion_at_line_##line)
#define GB_STATIC_ASSERT1(cond, line) GB_STATIC_ASSERT2(cond, line)
#define GB_STATIC_ASSERT(cond) GB_STATIC_ASSERT1(cond, __LINE__)
#endif
////////////////////////////////////////////////////////////////
//
// Headers
//
//
#if defined(GB_SYSTEM_UNIX)
#define _GNU_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#if defined(GB_CPU_X86)
#include <xmmintrin.h>
#endif
#endif
#if defined(GB_SYSTEM_OSX)
#include <mach/mach.h>
#include <mach/mach_init.h>
#include <mach/mach_time.h>
#include <mach/thread_act.h>
#include <mach/thread_policy.h>
#include <sys/sysctl.h>
#include <copyfile.h>
#include <mach/clock.h>
#endif
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
#endif
////////////////////////////////////////////////////////////////
//
// Base Types
//
//
#if defined(GB_COMPILER_MSVC)
#if _MSC_VER < 1300
typedef unsigned char u8;
typedef signed char i8;
typedef unsigned short u16;
typedef signed short i16;
typedef unsigned int u32;
typedef signed int i32;
#else
typedef unsigned __int8 u8;
typedef signed __int8 i8;
typedef unsigned __int16 u16;
typedef signed __int16 i16;
typedef unsigned __int32 u32;
typedef signed __int32 i32;
#endif
typedef unsigned __int64 u64;
typedef signed __int64 i64;
#else
#include <stdint.h>
typedef uint8_t u8;
typedef int8_t i8;
typedef uint16_t u16;
typedef int16_t i16;
typedef uint32_t u32;
typedef int32_t i32;
typedef uint64_t u64;
typedef int64_t i64;
#endif
GB_STATIC_ASSERT(sizeof(u8) == sizeof(i8));
GB_STATIC_ASSERT(sizeof(u16) == sizeof(i16));
GB_STATIC_ASSERT(sizeof(u32) == sizeof(i32));
GB_STATIC_ASSERT(sizeof(u64) == sizeof(i64));
GB_STATIC_ASSERT(sizeof(u8) == 1);
GB_STATIC_ASSERT(sizeof(u16) == 2);
GB_STATIC_ASSERT(sizeof(u32) == 4);
GB_STATIC_ASSERT(sizeof(u64) == 8);
GB_STATIC_ASSERT(sizeof(usize) == sizeof(isize));
// NOTE(bill): (u)intptr is only here for semantic reasons really as this library
will only support 32/64 bit OSes.
// NOTE(bill): Are there any modern OSes (not 16 bit) where intptr != isize ?
#if defined(_WIN64)
typedef signed __int64 intptr;
typedef unsigned __int64 uintptr;
#elif defined(_WIN32)
// NOTE(bill); To mark types changing their size, e.g. intptr
#ifndef _W64
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) &&
_MSC_VER >= 1300
#define _W64 __w64
#else
#define _W64
#endif
#endif
GB_STATIC_ASSERT(sizeof(uintptr) == sizeof(intptr));
GB_STATIC_ASSERT(sizeof(f32) == 4);
GB_STATIC_ASSERT(sizeof(f64) == 8);
typedef i8 b8;
typedef i16 b16;
typedef i32 b32; // NOTE(bill): Prefer this!!!
// NOTE(bill): These do are not prefixed with gb because the types are not.
#ifndef U8_MIN
#define U8_MIN 0u
#define U8_MAX 0xffu
#define I8_MIN (-0x7f - 1)
#define I8_MAX 0x7f
#define U16_MIN 0u
#define U16_MAX 0xffffu
#define I16_MIN (-0x7fff - 1)
#define I16_MAX 0x7fff
#define U32_MIN 0u
#define U32_MAX 0xffffffffu
#define I32_MIN (-0x7fffffff - 1)
#define I32_MAX 0x7fffffff
#if defined(GB_ARCH_32_BIT)
#define USIZE_MIX U32_MIN
#define USIZE_MAX U32_MAX
#endif
#ifndef NULL
#if defined(__cplusplus)
#if __cplusplus >= 201103L
#define NULL nullptr
#else
#define NULL 0
#endif
#else
#define NULL ((void *)0)
#endif
#endif
#if !defined(gb_restrict)
#if defined(_MSC_VER)
#define gb_restrict __restrict
#elif defined(__STDC_VERSION__)
#define gb_restrict restrict
#else
#define gb_restrict
#endif
#endif
#if !defined(gb_no_inline)
#if defined(_MSC_VER)
#define gb_no_inline __declspec(noinline)
#else
#define gb_no_inline __attribute__ ((noinline))
#endif
#endif
#if !defined(gb_thread_local)
#if defined(_MSC_VER) && _MSC_VER >= 1300
#define gb_thread_local __declspec(thread)
#elif defined(__GNUC__)
#define gb_thread_local __thread
#else
#define gb_thread_local thread_local
#endif
#endif
#ifndef gb_count_of
#define gb_count_of(x) ((gb_size_of(x)/gb_size_of(0[x])) / ((isize)(!(gb_size_of(x)
% gb_size_of(0[x])))))
#endif
#ifndef gb_offset_of
#define gb_offset_of(Type, element) ((isize)&(((Type *)0)->element))
#endif
#if defined(__cplusplus)
#ifndef gb_align_of
#if __cplusplus >= 201103L
#define gb_align_of(Type) (isize)alignof(Type)
#else
extern "C++" {
// NOTE(bill): Fucking Templates!
template <typename T> struct gbAlignment_Trick { char c; T member; };
#define gb_align_of(Type) gb_offset_of(gbAlignment_Trick<Type>, member)
}
#endif
#endif
#else
#ifndef gb_align_of
#define gb_align_of(Type) gb_offset_of(struct { char c; Type member; },
member)
#endif
#endif
// NOTE(bill): Because static means 3/4 different things in C/C++. Great design (!)
#ifndef gb_global
#define gb_global static // Global variables
#define gb_internal static // Internal linkage
#define gb_local_persist static // Local Persisting variables
#endif
#ifndef gb_unused
#if defined(_MSC_VER)
#define gb_unused(x) (__pragma(warning(suppress:4100))(x))
#elif defined (__GCC__)
#define gb_unused(x) __attribute__((__unused__))(x)
#else
#define gb_unused(x) ((void)(gb_size_of(x)))
#endif
#endif
////////////////////////////////////////////////////////////////
//
// Defer statement
// Akin to D's SCOPE_EXIT or
// similar to Go's defer but scope-based
//
// NOTE: C++11 (and above) only!
//
#if !defined(GB_NO_DEFER) && defined(__cplusplus) && ((defined(_MSC_VER) &&
_MSC_VER >= 1400) || (__cplusplus >= 201103L))
extern "C++" {
// NOTE(bill): Stupid fucking templates
template <typename T> struct gbRemoveReference { typedef T Type; };
template <typename T> struct gbRemoveReference<T &> { typedef T Type; };
template <typename T> struct gbRemoveReference<T &&> { typedef T Type; };
/// NOTE(bill): "Move" semantics - invented because the C++ committee are
idiots (as a collective not as indiviuals (well a least some aren't))
template <typename T> inline T &&gb_forward(typename
gbRemoveReference<T>::Type &t) { return static_cast<T &&>(t); }
template <typename T> inline T &&gb_forward(typename
gbRemoveReference<T>::Type &&t) { return static_cast<T &&>(t); }
template <typename T> inline T &&gb_move (T &&t)
{ return static_cast<typename gbRemoveReference<T>::Type &&>(t); }
template <typename F>
struct gbprivDefer {
F f;
gbprivDefer(F &&f) : f(gb_forward<F>(f)) {}
~gbprivDefer() { f(); }
};
template <typename F> gbprivDefer<F> gb__defer_func(F &&f) { return
gbprivDefer<F>(gb_forward<F>(f)); }
// Example
#if 0
gbMutex m;
gb_mutex_init(&m);
{
gb_mutex_lock(&m);
defer (gb_mutex_unlock(&m));
...
}
#endif
#endif
////////////////////////////////////////////////////////////////
//
// Macro Fun!
//
//
#ifndef GB_JOIN_MACROS
#define GB_JOIN_MACROS
#define GB_JOIN2_IND(a, b) a##b
#ifndef gb_min
#define gb_min(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef gb_max
#define gb_max(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef gb_min3
#define gb_min3(a, b, c) gb_min(gb_min(a, b), c)
#endif
#ifndef gb_max3
#define gb_max3(a, b, c) gb_max(gb_max(a, b), c)
#endif
#ifndef gb_clamp
#define gb_clamp(x, lower, upper) gb_min(gb_max((x), (lower)), (upper))
#endif
#ifndef gb_clamp01
#define gb_clamp01(x) gb_clamp((x), 0, 1)
#endif
#ifndef gb_is_between
#define gb_is_between(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper)))
#endif
#ifndef gb_abs
#define gb_abs(x) ((x) < 0 ? -(x) : (x))
#endif
////////////////////////////////////////////////////////////////
//
// Debug
//
//
#ifndef GB_DEBUG_TRAP
#if defined(_MSC_VER)
#if _MSC_VER < 1300
#define GB_DEBUG_TRAP() __asm int 3 /* Trap to debugger! */
#else
#define GB_DEBUG_TRAP() __debugbreak()
#endif
#else
#define GB_DEBUG_TRAP() __builtin_trap()
#endif
#endif
#ifndef GB_ASSERT_MSG
#define GB_ASSERT_MSG(cond, msg, ...) do { \
if (!(cond)) { \
gb_assert_handler("Assertion Failure", #cond, __FILE__,
cast(i64)__LINE__, msg, ##__VA_ARGS__); \
GB_DEBUG_TRAP(); \
} \
} while (0)
#endif
#ifndef GB_ASSERT
#define GB_ASSERT(cond) GB_ASSERT_MSG(cond, NULL)
#endif
#ifndef GB_ASSERT_NOT_NULL
#define GB_ASSERT_NOT_NULL(ptr) GB_ASSERT_MSG((ptr) != NULL, #ptr " must not be
NULL")
#endif
GB_DEF void gb_assert_handler(char const *prefix, char const *condition, char const
*file, i32 line, char const *msg, ...);
////////////////////////////////////////////////////////////////
//
// Memory
//
//
GB_DEF void * gb_memcopy (void *dest, void const *source, isize size);
GB_DEF void * gb_memmove (void *dest, void const *source, isize size);
GB_DEF void * gb_memset (void *data, u8 byte_value, isize size);
GB_DEF i32 gb_memcompare(void const *s1, void const *s2, isize size);
GB_DEF void gb_memswap (void *i, void *j, isize size);
GB_DEF void const *gb_memchr (void const *data, u8 byte_value, isize size);
GB_DEF void const *gb_memrchr (void const *data, u8 byte_value, isize size);
#ifndef gb_memcopy_array
#define gb_memcopy_array(dst, src, count) gb_memcopy((dst), (src),
gb_size_of(*(dst))*(count))
#endif
#ifndef gb_memmove_array
#define gb_memmove_array(dst, src, count) gb_memmove((dst), (src),
gb_size_of(*(dst))*(count))
#endif
#ifndef gb_kilobytes
#define gb_kilobytes(x) ( (x) * (i64)(1024))
#define gb_megabytes(x) (gb_kilobytes(x) * (i64)(1024))
#define gb_gigabytes(x) (gb_megabytes(x) * (i64)(1024))
#define gb_terabytes(x) (gb_gigabytes(x) * (i64)(1024))
#endif
// Atomics
#if defined(GB_COMPILER_MSVC)
typedef struct gbAtomic32 { i32 volatile value; } gbAtomic32;
typedef struct gbAtomic64 { i64 volatile value; } gbAtomic64;
typedef struct gbAtomicPtr { void *volatile value; } gbAtomicPtr;
#else
#if defined(GB_ARCH_32_BIT)
#define GB_ATOMIC_PTR_ALIGNMENT 4
#elif defined(GB_ARCH_64_BIT)
#define GB_ATOMIC_PTR_ALIGNMENT 8
#else
#error Unknown architecture
#endif
// Fences
GB_DEF void gb_yield_thread(void);
GB_DEF void gb_mfence (void);
GB_DEF void gb_sfence (void);
GB_DEF void gb_lfence (void);
#if defined(GB_SYSTEM_WINDOWS)
typedef struct gbSemaphore { void *win32_handle; } gbSemaphore;
#elif defined(GB_SYSTEM_OSX)
typedef struct gbSemaphore { semaphore_t osx_handle; } gbSemaphore;
#elif defined(GB_SYSTEM_UNIX)
typedef struct gbSemaphore { sem_t unix_handle; } gbSemaphore;
#else
#error
#endif
// Mutex
typedef struct gbMutex {
#if defined(GB_SYSTEM_WINDOWS)
CRITICAL_SECTION win32_critical_section;
#else
pthread_mutex_t pthread_mutex;
pthread_mutexattr_t pthread_mutexattr;
#endif
} gbMutex;
// NOTE(bill): If you wanted a Scoped Mutex in C++, why not use the defer()
construct?
// No need for a silly wrapper class and it's clear!
#if 0
gbMutex m = {0};
gb_mutex_init(&m);
{
gb_mutex_lock(&m);
defer (gb_mutex_unlock(&m));
gbThreadProc *proc;
void * user_data;
isize user_index;
isize return_value;
gbSemaphore semaphore;
isize stack_size;
b32 volatile is_running;
} gbThread;
gbMutex start;
gbMutex mutex;
gbSemaphore release;
} gbSync;
#if defined(GB_SYSTEM_WINDOWS)
typedef struct gbAffinity {
b32 is_accurate;
isize core_count;
isize thread_count;
#define GB_WIN32_MAX_THREADS (8 * gb_size_of(usize))
usize core_masks[GB_WIN32_MAX_THREADS];
} gbAffinity;
#elif defined(GB_SYSTEM_OSX)
typedef struct gbAffinity {
b32 is_accurate;
isize core_count;
isize thread_count;
isize threads_per_core;
} gbAffinity;
#elif defined(GB_SYSTEM_LINUX)
typedef struct gbAffinity {
b32 is_accurate;
isize core_count;
isize thread_count;
isize threads_per_core;
} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
////////////////////////////////////////////////////////////////
//
// Virtual Memory
//
//
// NOTE(bill): This is useful so you can define an allocator of the same type and
parameters
#define GB_ALLOCATOR_PROC(name) \
void *name(void *allocator_data, gbAllocationType type, \
isize size, isize alignment, \
void *old_memory, isize old_size, \
u64 flags)
typedef GB_ALLOCATOR_PROC(gbAllocatorProc);
#ifndef GB_DEFAULT_ALLOCATOR_FLAGS
#define GB_DEFAULT_ALLOCATOR_FLAGS (gbAllocatorFlag_ClearToZero)
#endif
// NOTE(bill): These are very useful and the type cast has saved me from numerous
bugs
#ifndef gb_alloc_item
#define gb_alloc_item(allocator_, Type) (Type *)gb_alloc(allocator_,
gb_size_of(Type))
#define gb_alloc_array(allocator_, Type, count) (Type *)gb_alloc(allocator_,
gb_size_of(Type) * (count))
#endif
// TODO(bill): Probably use a custom heap allocator system that doesn't depend on
malloc/free
// Base it off TCMalloc or something else? Or something entirely custom?
GB_DEF gbAllocator gb_heap_allocator(void);
GB_DEF GB_ALLOCATOR_PROC(gb_heap_allocator_proc);
//
// Arena Allocator
//
typedef struct gbArena {
gbAllocator backing;
void * physical_start;
isize total_size;
isize total_allocated;
isize temp_count;
} gbArena;
//
// Pool Allocator
//
// TODO(bill): Find better way of doing this without #if #elif etc.
#if defined(GB_ARCH_32_BIT)
#define GB_ISIZE_HIGH_BIT 0x80000000
#elif defined(GB_ARCH_64_BIT)
#define GB_ISIZE_HIGH_BIT 0x8000000000000000ll
#else
#error
#endif
//
// Free List Allocator
//
// IMPORTANT TODO(bill): Thoroughly test the free list allocator!
// NOTE(bill): This is a very shitty free list as it just picks the first free
block not the best size
// as I am just being lazy. Also, I will probably remove it later; it's only here
because why not?!
//
// NOTE(bill): I may also complete remove this if I completely implement a fixed
heap allocator
gbFreeListBlock *curr_block;
isize total_allocated;
isize allocation_count;
} gbFreeList;
//
// Scratch Memory Allocator - Ring Buffer Based Arena
//
////////////////////////////////////////////////////////////////
//
// Sort & Search
//
//
// Producure pointers
// NOTE(bill): The offset parameter specifies the offset in the structure
// e.g. gb_i32_cmp(gb_offset_of(Thing, value))
// Use 0 if it's just the type instead.
GB_DEF GB_RADIX_SORT_PROC(u8);
GB_DEF GB_RADIX_SORT_PROC(u16);
GB_DEF GB_RADIX_SORT_PROC(u32);
GB_DEF GB_RADIX_SORT_PROC(u64);
////////////////////////////////////////////////////////////////
//
// UTF-8 Handling
//
//
// NOTE(bill): Does not check if utf-8 string is valid
GB_DEF isize gb_utf8_strlen (u8 const *str);
GB_DEF isize gb_utf8_strnlen(u8 const *str, isize max_len);
////////////////////////////////////////////////////////////////
//
// gbString - C Read-Only-Compatible
//
//
/*
Reasoning:
By default, strings in C are null terminated which means you have to count
the number of character up to the null character to calculate the length.
Many "better" C string libraries will create a struct for a string.
i.e.
struct String {
Allocator allocator;
size_t length;
size_t capacity;
char * cstring;
};
This library tries to augment normal C strings in a better way that is still
compatible with C-style strings.
+--------+-----------------------+-----------------+
| Header | Binary C-style String | Null Terminator |
+--------+-----------------------+-----------------+
|
+-> Pointer returned by functions
Due to the meta-data being stored before the string pointer and every gb
string
having an implicit null terminator, gb strings are full compatible with c-
style
strings and read-only functions.
Advantages:
gb_printf("%s\n", gb_str);
Many other libraries do either of these:
gb_printf("%s\n", string->cstr);
gb_printf("%s\n", get_cstring(string));
Disadvantages:
* In the C version of these functions, many return the new string. i.e.
str = gb_string_appendc(str, "another string");
This could be changed to gb_string_appendc(&str, "another string"); but I'm
still not sure.
#if 0
#define GB_IMPLEMENTATION
#include "gb.h"
int main(int argc, char **argv) {
gbString str = gb_string_make("Hello");
gbString other_str = gb_string_make_length(", ", 2);
str = gb_string_append(str, other_str);
str = gb_string_appendc(str, "world!");
gb_string_free(str);
gb_string_free(other_str);
return 0;
}
#endif
// NOTE(bill): If you only need a small string, just use a standard c string or
change the size from isize to u16, etc.
typedef struct gbStringHeader {
gbAllocator allocator;
isize length;
isize capacity;
} gbStringHeader;
////////////////////////////////////////////////////////////////
//
// Fixed Capacity Buffer (POD Types)
//
//
// gbBuffer(Type) works like gbString or gbArray where the actual type is just a
pointer to the first
// element.
//
////////////////////////////////////////////////////////////////
//
// Dynamic Array (POD Types)
//
// NOTE(bill): I know this is a macro hell but C is an old (and shit) language with
no proper arrays
// Also why the fuck not?! It fucking works! And it has custom allocation, which is
already better than C++!
//
// gbArray(Type) works like gbString or gbBuffer where the actual type is just a
pointer to the first
// element.
//
#if 0 // Example
void foo(void) {
isize i;
int test_values[] = {4, 2, 1, 7};
gbAllocator a = gb_heap_allocator();
gbArray(int) items;
gb_array_init(items, a);
gb_array_append(items, 1);
gb_array_append(items, 4);
gb_array_append(items, 9);
gb_array_append(items, 16);
gb_array_clear(items);
gb_array_free(items);
}
#endif
#ifndef GB_ARRAY_GROW_FORMULA
#define GB_ARRAY_GROW_FORMULA(x) (2*(x) + 8)
#endif
#define gb_array_free(x) do { \
gbArrayHeader *gb__ah = GB_ARRAY_HEADER(x); \
gb_free(gb__ah->allocator, gb__ah); \
} while (0)
// NOTE(bill): Do not use the thing below directly, use the macro
GB_DEF void *gb__array_set_capacity(void *array, isize capacity, isize
element_size);
////////////////////////////////////////////////////////////////
//
// Hashing and Checksum Functions
//
//
////////////////////////////////////////////////////////////////
//
// Instantiated Hash Table
//
// This is an attempt to implement a templated hash table
// NOTE(bill): The key is aways a u64 for simplicity and you will _probably_
_never_ need anything bigger.
//
// Hash table type and function declaration, call: GB_TABLE_DECLARE(PREFIX, NAME,
N, VALUE)
// Hash table function definitions, call: GB_TABLE_DEFINE(NAME, N, VALUE)
//
// PREFIX - a prefix for function prototypes e.g. extern, static, etc.
// NAME - Name of the Hash Table
// FUNC - the name will prefix function names
// VALUE - the type of the value to be stored
//
// NOTE(bill): I really wish C had decent metaprogramming capabilities (and no I
don't mean C++'s templates either)
//
////////////////////////////////////////////////////////////////
//
// File Handling
//
struct gbFileOperations {
gbFileReadProc *read_at;
gbFileWriteProc *write_at;
gbFileSeekProc *seek;
gbFileCloseProc *close;
};
// TODO(bill): gbAsyncFile
// TODO(bill): Should these have different na,es as they do not take in a gbFile
* ???
GB_DEF b32 gb_file_exists (char const *filepath);
GB_DEF gbFileTime gb_file_last_write_time(char const *filepath);
GB_DEF b32 gb_file_copy (char const *existing_filename, char const
*new_filename, b32 fail_if_exists);
GB_DEF b32 gb_file_move (char const *existing_filename, char const
*new_filename);
GB_DEF b32 gb_file_remove (char const *filename);
#ifndef GB_PATH_SEPARATOR
#if defined(GB_SYSTEM_WINDOWS)
#define GB_PATH_SEPARATOR '\\'
#else
#define GB_PATH_SEPARATOR '/'
#endif
#endif
////////////////////////////////////////////////////////////////
//
// Printing
//
//
////////////////////////////////////////////////////////////////
//
// DLL Handling
//
//
////////////////////////////////////////////////////////////////
//
// Time
//
//
////////////////////////////////////////////////////////////////
//
// Miscellany
//
//
////////////////////////////////////////////////////////////////
//
// Platform Stuff
//
//
#define GB_PLATFORM
#if defined(GB_PLATFORM)
// NOTE(bill):
// Coordiate system - +ve x - left to right
// - +ve y - bottom to top
// - Relative to window
// TODO(bill): Proper documentation for this with code examples
#ifndef GB_MAX_GAME_CONTROLLER_COUNT
#define GB_MAX_GAME_CONTROLLER_COUNT 4
#endif
// NOTE(bill): Allow the basic printable keys to be aliased with their chars
gbKey_0 = '0',
gbKey_1,
gbKey_2,
gbKey_3,
gbKey_4,
gbKey_5,
gbKey_6,
gbKey_7,
gbKey_8,
gbKey_9,
gbKey_A = 'A',
gbKey_B,
gbKey_C,
gbKey_D,
gbKey_E,
gbKey_F,
gbKey_G,
gbKey_H,
gbKey_I,
gbKey_J,
gbKey_K,
gbKey_L,
gbKey_M,
gbKey_N,
gbKey_O,
gbKey_P,
gbKey_Q,
gbKey_R,
gbKey_S,
gbKey_T,
gbKey_U,
gbKey_V,
gbKey_W,
gbKey_X,
gbKey_Y,
gbKey_Z,
gbKey_Lbracket = '[',
gbKey_Rbracket = ']',
gbKey_Semicolon = ';',
gbKey_Comma = ',',
gbKey_Period = '.',
gbKey_Quote = '\'',
gbKey_Slash = '/',
gbKey_Backslash = '\\',
gbKey_Grave = '`',
gbKey_Equals = '=',
gbKey_Minus = '-',
gbKey_Space = ' ',
gbKey_Escape, // Escape
gbKey_Lcontrol, // Left Control
gbKey_Lshift, // Left Shift
gbKey_Lalt, // Left Alt
gbKey_Lsystem, // Left OS specific: window (Windows and Linux),
apple/cmd (MacOS X), ...
gbKey_Rcontrol, // Right Control
gbKey_Rshift, // Right Shift
gbKey_Ralt, // Right Alt
gbKey_Rsystem, // Right OS specific: window (Windows and Linux),
apple/cmd (MacOS X), ...
gbKey_Menu, // Menu
gbKey_Return, // Return
gbKey_Backspace, // Backspace
gbKey_Tab, // Tabulation
gbKey_Pageup, // Page up
gbKey_Pagedown, // Page down
gbKey_End, // End
gbKey_Home, // Home
gbKey_Insert, // Insert
gbKey_Delete, // Delete
gbKey_Plus, // +
gbKey_Subtract, // -
gbKey_Multiply, // *
gbKey_Divide, // /
gbKey_Left, // Left arrow
gbKey_Right, // Right arrow
gbKey_Up, // Up arrow
gbKey_Down, // Down arrow
gbKey_Numpad0, // Numpad 0
gbKey_Numpad1, // Numpad 1
gbKey_Numpad2, // Numpad 2
gbKey_Numpad3, // Numpad 3
gbKey_Numpad4, // Numpad 4
gbKey_Numpad5, // Numpad 5
gbKey_Numpad6, // Numpad 6
gbKey_Numpad7, // Numpad 7
gbKey_Numpad8, // Numpad 8
gbKey_Numpad9, // Numpad 9
gbKey_NumpadDot, // Numpad .
gbKey_NumpadEnter, // Numpad Enter
gbKey_F1, // F1
gbKey_F2, // F2
gbKey_F3, // F3
gbKey_F4, // F4
gbKey_F5, // F5
gbKey_F6, // F6
gbKey_F7, // F7
gbKey_F8, // F8
gbKey_F9, // F8
gbKey_F10, // F10
gbKey_F11, // F11
gbKey_F12, // F12
gbKey_F13, // F13
gbKey_F14, // F14
gbKey_F15, // F15
gbKey_Pause, // Pause
gbKey_Count,
} gbKeyType;
gbMouseButton_Count
} gbMouseButtonType;
gbControllerAxis_Count
} gbControllerAxisType;
gbControllerButton_Count
} gbControllerButtonType;
f32 axes[gbControllerAxis_Count];
gbKeyState buttons[gbControllerButton_Count];
} gbGameController;
#if defined(GB_SYSTEM_WINDOWS)
typedef struct _XINPUT_GAMEPAD XINPUT_GAMEPAD;
typedef struct _XINPUT_STATE XINPUT_STATE;
typedef struct _XINPUT_VIBRATION XINPUT_VIBRATION;
gbRenderer_Count,
} gbRendererType;
void *window_handle;
i32 window_x, window_y;
i32 window_width, window_height;
u32 window_flags;
b16 window_is_closed, window_has_focus;
#if defined(GB_SYSTEM_WINDOWS)
void *win32_dc;
#elif defined(GB_SYSTEM_OSX)
void *osx_autorelease_pool; // TODO(bill): Is this really needed?
#endif
gbRendererType renderer_type;
union {
struct {
void * context;
i32 major;
i32 minor;
b16 core, compatible;
gbDllHandle dll_handle;
} opengl;
gbKeyState keys[gbKey_Count];
struct {
gbKeyState control;
gbKeyState alt;
gbKeyState shift;
} key_modifiers;
Rune char_buffer[256];
isize char_buffer_count;
b32 mouse_clip;
i32 mouse_x, mouse_y;
i32 mouse_dx, mouse_dy; // NOTE(bill): Not raw mouse movement
i32 mouse_raw_dx, mouse_raw_dy; // NOTE(bill): Raw mouse movement
f32 mouse_wheel_delta;
gbKeyState mouse_buttons[gbMouseButton_Count];
gbGameController game_controllers[GB_MAX_GAME_CONTROLLER_COUNT];
f64 curr_time;
f64 dt_for_frame;
b32 quit_requested;
#if defined(GB_SYSTEM_WINDOWS)
struct {
gbXInputGetStateProc *get_state;
gbXInputSetStateProc *set_state;
} xinput;
#endif
} gbPlatform;
#endif // GB_PLATFORM
#if defined(__cplusplus)
}
#endif
#endif // GB_INCLUDE_GB_H
////////////////////////////////////////////////////////////////
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
// Implementation
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
// It's turtles all the way down!
////////////////////////////////////////////////////////////////
#if defined(GB_IMPLEMENTATION) && !defined(GB_IMPLEMENTATION_DONE)
#define GB_IMPLEMENTATION_DONE
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(_WIN64)
typedef unsigned __int64 ULONG_PTR;
typedef signed __int64 LONG_PTR;
#else
typedef unsigned long ULONG_PTR;
typedef signed long LONG_PTR;
#endif
typedef ULONG_PTR DWORD_PTR;
#ifndef VK_UNKNOWN
#define VK_UNKNOWN 0
#define VK_LBUTTON 0x01
#define VK_RBUTTON 0x02
#define VK_CANCEL 0x03
#define VK_MBUTTON 0x04
#define VK_XBUTTON1 0x05
#define VK_XBUTTON2 0x06
#define VK_BACK 0x08
#define VK_TAB 0x09
#define VK_CLEAR 0x0C
#define VK_RETURN 0x0D
#define VK_SHIFT 0x10
#define VK_CONTROL 0x11 // CTRL key
#define VK_MENU 0x12 // ALT key
#define VK_PAUSE 0x13 // PAUSE key
#define VK_CAPITAL 0x14 // CAPS LOCK key
#define VK_KANA 0x15 // Input Method Editor (IME) Kana mode
#define VK_HANGUL 0x15 // IME Hangul mode
#define VK_JUNJA 0x17 // IME Junja mode
#define VK_FINAL 0x18 // IME final mode
#define VK_HANJA 0x19 // IME Hanja mode
#define VK_KANJI 0x19 // IME Kanji mode
#define VK_ESCAPE 0x1B // ESC key
#define VK_CONVERT 0x1C // IME convert
#define VK_NONCONVERT 0x1D // IME nonconvert
#define VK_ACCEPT 0x1E // IME accept
#define VK_MODECHANGE 0x1F // IME mode change request
#define VK_SPACE 0x20 // SPACE key
#define VK_PRIOR 0x21 // PAGE UP key
#define VK_NEXT 0x22 // PAGE DOWN key
#define VK_END 0x23 // END key
#define VK_HOME 0x24 // HOME key
#define VK_LEFT 0x25 // LEFT ARROW key
#define VK_UP 0x26 // UP ARROW key
#define VK_RIGHT 0x27 // RIGHT ARROW key
#define VK_DOWN 0x28 // DOWN ARROW key
#define VK_SELECT 0x29 // SELECT key
#define VK_PRINT 0x2A // PRINT key
#define VK_EXECUTE 0x2B // EXECUTE key
#define VK_SNAPSHOT 0x2C // PRINT SCREEN key
#define VK_INSERT 0x2D // INS key
#define VK_DELETE 0x2E // DEL key
#define VK_HELP 0x2F // HELP key
#define VK_0 0x30
#define VK_1 0x31
#define VK_2 0x32
#define VK_3 0x33
#define VK_4 0x34
#define VK_5 0x35
#define VK_6 0x36
#define VK_7 0x37
#define VK_8 0x38
#define VK_9 0x39
#define VK_A 0x41
#define VK_B 0x42
#define VK_C 0x43
#define VK_D 0x44
#define VK_E 0x45
#define VK_F 0x46
#define VK_G 0x47
#define VK_H 0x48
#define VK_I 0x49
#define VK_J 0x4A
#define VK_K 0x4B
#define VK_L 0x4C
#define VK_M 0x4D
#define VK_N 0x4E
#define VK_O 0x4F
#define VK_P 0x50
#define VK_Q 0x51
#define VK_R 0x52
#define VK_S 0x53
#define VK_T 0x54
#define VK_U 0x55
#define VK_V 0x56
#define VK_W 0x57
#define VK_X 0x58
#define VK_Y 0x59
#define VK_Z 0x5A
#define VK_LWIN 0x5B // Left Windows key (Microsoft Natural keyboard)
#define VK_RWIN 0x5C // Right Windows key (Natural keyboard)
#define VK_APPS 0x5D // Applications key (Natural keyboard)
#define VK_SLEEP 0x5F // Computer Sleep key
// Num pad keys
#define VK_NUMPAD0 0x60
#define VK_NUMPAD1 0x61
#define VK_NUMPAD2 0x62
#define VK_NUMPAD3 0x63
#define VK_NUMPAD4 0x64
#define VK_NUMPAD5 0x65
#define VK_NUMPAD6 0x66
#define VK_NUMPAD7 0x67
#define VK_NUMPAD8 0x68
#define VK_NUMPAD9 0x69
#define VK_MULTIPLY 0x6A
#define VK_ADD 0x6B
#define VK_SEPARATOR 0x6C
#define VK_SUBTRACT 0x6D
#define VK_DECIMAL 0x6E
#define VK_DIVIDE 0x6F
#define VK_F1 0x70
#define VK_F2 0x71
#define VK_F3 0x72
#define VK_F4 0x73
#define VK_F5 0x74
#define VK_F6 0x75
#define VK_F7 0x76
#define VK_F8 0x77
#define VK_F9 0x78
#define VK_F10 0x79
#define VK_F11 0x7A
#define VK_F12 0x7B
#define VK_F13 0x7C
#define VK_F14 0x7D
#define VK_F15 0x7E
#define VK_F16 0x7F
#define VK_F17 0x80
#define VK_F18 0x81
#define VK_F19 0x82
#define VK_F20 0x83
#define VK_F21 0x84
#define VK_F22 0x85
#define VK_F23 0x86
#define VK_F24 0x87
#define VK_NUMLOCK 0x90
#define VK_SCROLL 0x91
#define VK_LSHIFT 0xA0
#define VK_RSHIFT 0xA1
#define VK_LCONTROL 0xA2
#define VK_RCONTROL 0xA3
#define VK_LMENU 0xA4
#define VK_RMENU 0xA5
#define VK_BROWSER_BACK 0xA6 // Windows 2000/XP: Browser Back key
#define VK_BROWSER_FORWARD 0xA7 // Windows 2000/XP: Browser Forward key
#define VK_BROWSER_REFRESH 0xA8 // Windows 2000/XP: Browser Refresh key
#define VK_BROWSER_STOP 0xA9 // Windows 2000/XP: Browser Stop key
#define VK_BROWSER_SEARCH 0xAA // Windows 2000/XP: Browser Search key
#define VK_BROWSER_FAVORITES 0xAB // Windows 2000/XP: Browser Favorites key
#define VK_BROWSER_HOME 0xAC // Windows 2000/XP: Browser Start and Home key
#define VK_VOLUME_MUTE 0xAD // Windows 2000/XP: Volume Mute key
#define VK_VOLUME_DOWN 0xAE // Windows 2000/XP: Volume Down key
#define VK_VOLUME_UP 0xAF // Windows 2000/XP: Volume Up key
#define VK_MEDIA_NEXT_TRACK 0xB0 // Windows 2000/XP: Next Track key
#define VK_MEDIA_PREV_TRACK 0xB1 // Windows 2000/XP: Previous Track key
#define VK_MEDIA_STOP 0xB2 // Windows 2000/XP: Stop Media key
#define VK_MEDIA_PLAY_PAUSE 0xB3 // Windows 2000/XP: Play/Pause Media key
#define VK_MEDIA_LAUNCH_MAIL 0xB4 // Windows 2000/XP: Start Mail key
#define VK_MEDIA_LAUNCH_MEDIA_SELECT 0xB5 // Windows 2000/XP: Select Media
key
#define VK_MEDIA_LAUNCH_APP1 0xB6 // VK_LAUNCH_APP1 (B6) Windows 2000/XP:
Start Application 1 key
#define VK_MEDIA_LAUNCH_APP2 0xB7 // VK_LAUNCH_APP2 (B7) Windows 2000/XP:
Start Application 2 key
#define VK_OEM_1 0xBA
#define VK_OEM_PLUS 0xBB
#define VK_OEM_COMMA 0xBC
#define VK_OEM_MINUS 0xBD
#define VK_OEM_PERIOD 0xBE
#define VK_OEM_2 0xBF
#define VK_OEM_3 0xC0
#define VK_OEM_4 0xDB
#define VK_OEM_5 0xDC
#define VK_OEM_6 0xDD
#define VK_OEM_7 0xDE
#define VK_OEM_8 0xDF
#define VK_OEM_102 0xE2
#define VK_PROCESSKEY 0xE5
#define VK_PACKET 0xE7
#define VK_ATTN 0xF6 // Attn key
#define VK_CRSEL 0xF7 // CrSel key
#define VK_EXSEL 0xF8 // ExSel key
#define VK_EREOF 0xF9 // Erase EOF key
#define VK_PLAY 0xFA // Play key
#define VK_ZOOM 0xFB // Zoom key
#define VK_NONAME 0xFC // Reserved for future use
#define VK_PA1 0xFD // VK_PA1 (FD) PA1 key
#define VK_OEM_CLEAR 0xFE // Clear key
#endif // VK_UNKNOWN
#define MAPVK_VK_TO_VSC 0
#define MAPVK_VSC_TO_VK 1
#define MAPVK_VK_TO_CHAR 2
#define MAPVK_VSC_TO_VK_EX 3
#define SW_HIDE 0
#define SW_SHOWNORMAL 1
#define SW_NORMAL 1
#define SW_SHOWMINIMIZED 2
#define SW_SHOWMAXIMIZED 3
#define SW_MAXIMIZE 3
#define SW_SHOWNOACTIVATE 4
#define SW_SHOW 5
#define SW_MINIMIZE 6
#define SW_SHOWMINNOACTIVE 7
#define SW_SHOWNA 8
#define SW_RESTORE 9
#define SW_SHOWDEFAULT 10
#define SW_FORCEMINIMIZE 11
#define SW_MAX 11
#define PFD_TYPE_RGBA 0
#define PFD_TYPE_COLORINDEX 1
#define PFD_MAIN_PLANE 0
#define PFD_OVERLAY_PLANE 1
#define PFD_UNDERLAY_PLANE (-1)
#define PFD_DOUBLEBUFFER 1
#define PFD_STEREO 2
#define PFD_DRAW_TO_WINDOW 4
#define PFD_DRAW_TO_BITMAP 8
#define PFD_SUPPORT_GDI 16
#define PFD_SUPPORT_OPENGL 32
#define PFD_GENERIC_FORMAT 64
#define PFD_NEED_PALETTE 128
#define PFD_NEED_SYSTEM_PALETTE 0x00000100
#define PFD_SWAP_EXCHANGE 0x00000200
#define PFD_SWAP_COPY 0x00000400
#define PFD_SWAP_LAYER_BUFFERS 0x00000800
#define PFD_GENERIC_ACCELERATED 0x00001000
#define PFD_DEPTH_DONTCARE 0x20000000
#define PFD_DOUBLEBUFFER_DONTCARE 0x40000000
#define PFD_STEREO_DONTCARE 0x80000000
#define PM_NOREMOVE 0
#define PM_REMOVE 1
typedef enum
{
DIB_RGB_COLORS = 0x00,
DIB_PAL_COLORS = 0x01,
DIB_PAL_INDICES = 0x02
} DIBColors;
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4201)
#pragma warning(disable:4127) // Conditional expression is constant
#endif
void gb_assert_handler(char const *prefix, char const *condition, char const *file,
i32 line, char const *msg, ...) {
gb_printf_err("%s(%d): %s: ", file, line, prefix);
if (condition)
gb_printf_err( "`%s` ", condition);
if (msg) {
va_list va;
va_start(va, msg);
gb_printf_err_va(msg, va);
va_end(va);
}
gb_printf_err("\n");
}
b32 gb_is_power_of_two(isize x) {
if (x <= 0)
return false;
return !(x & (x-1));
}
GB_ASSERT(gb_is_power_of_two(alignment));
p = cast(uintptr)ptr;
return cast(void *)((p + (alignment-1)) &~ (alignment-1));
}
#if defined(_MSC_VER)
#pragma intrinsic(__movsb)
#endif
if (dest == NULL) {
return NULL;
}
if (cast(uintptr)d % 4 == 0) {
for (; n >= 16;
s += 16, d += 16, n -= 16) {
*cast(u32 *)(d+ 0) = *cast(u32 *)(s+ 0);
*cast(u32 *)(d+ 4) = *cast(u32 *)(s+ 4);
*cast(u32 *)(d+ 8) = *cast(u32 *)(s+ 8);
*cast(u32 *)(d+12) = *cast(u32 *)(s+12);
}
if (n & 8) {
*cast(u32 *)(d+0) = *cast(u32 *)(s+0);
*cast(u32 *)(d+4) = *cast(u32 *)(s+4);
d += 8;
s += 8;
}
if (n&4) {
*cast(u32 *)(d+0) = *cast(u32 *)(s+0);
d += 4;
s += 4;
}
if (n&2) {
*d++ = *s++; *d++ = *s++;
}
if (n&1) {
*d = *s;
}
return dest;
}
if (n >= 32) {
#if __BYTE_ORDER == __BIG_ENDIAN
#define LS <<
#define RS >>
#else
#define LS >>
#define RS <<
#endif
switch (cast(uintptr)d % 4) {
case 1: {
w = *cast(u32 *)s;
*d++ = *s++;
*d++ = *s++;
*d++ = *s++;
n -= 3;
while (n > 16) {
x = *cast(u32 *)(s+1);
*cast(u32 *)(d+0) = (w LS 24) | (x RS 8);
w = *cast(u32 *)(s+5);
*cast(u32 *)(d+4) = (x LS 24) | (w RS 8);
x = *cast(u32 *)(s+9);
*cast(u32 *)(d+8) = (w LS 24) | (x RS 8);
w = *cast(u32 *)(s+13);
*cast(u32 *)(d+12) = (x LS 24) | (w RS 8);
s += 16;
d += 16;
n -= 16;
}
} break;
case 2: {
w = *cast(u32 *)s;
*d++ = *s++;
*d++ = *s++;
n -= 2;
while (n > 17) {
x = *cast(u32 *)(s+2);
*cast(u32 *)(d+0) = (w LS 16) | (x RS 16);
w = *cast(u32 *)(s+6);
*cast(u32 *)(d+4) = (x LS 16) | (w RS 16);
x = *cast(u32 *)(s+10);
*cast(u32 *)(d+8) = (w LS 16) | (x RS 16);
w = *cast(u32 *)(s+14);
*cast(u32 *)(d+12) = (x LS 16) | (w RS 16);
s += 16;
d += 16;
n -= 16;
}
} break;
case 3: {
w = *cast(u32 *)s;
*d++ = *s++;
n -= 1;
while (n > 18) {
x = *cast(u32 *)(s+3);
*cast(u32 *)(d+0) = (w LS 8) | (x RS 24);
w = *cast(u32 *)(s+7);
*cast(u32 *)(d+4) = (x LS 8) | (w RS 24);
x = *cast(u32 *)(s+11);
*cast(u32 *)(d+8) = (w LS 8) | (x RS 24);
w = *cast(u32 *)(s+15);
*cast(u32 *)(d+12) = (x LS 8) | (w RS 24);
s += 16;
d += 16;
n -= 16;
}
} break;
default: break; // NOTE(bill): Do nowt!
}
#undef LS
#undef RS
if (n & 16) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n & 8) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n & 4) {
*d++ = *s++; *d++ = *s++; *d++ = *s++; *d++ = *s++;
}
if (n & 2) {
*d++ = *s++; *d++ = *s++;
}
if (n & 1) {
*d = *s;
}
}
#endif
return dest;
}
if (dest == NULL) {
return NULL;
}
if (d == s) {
return d;
}
if (s+n <= d || d+n <= s) { // NOTE(bill): Non-overlapping
return gb_memcopy(d, s, n);
}
if (d < s) {
if (cast(uintptr)s % gb_size_of(isize) == cast(uintptr)d %
gb_size_of(isize)) {
while (cast(uintptr)d % gb_size_of(isize)) {
if (!n--) return dest;
*d++ = *s++;
}
while (n>=gb_size_of(isize)) {
*cast(isize *)d = *cast(isize *)s;
n -= gb_size_of(isize);
d += gb_size_of(isize);
s += gb_size_of(isize);
}
}
for (; n; n--) *d++ = *s++;
} else {
if ((cast(uintptr)s % gb_size_of(isize)) == (cast(uintptr)d %
gb_size_of(isize))) {
while (cast(uintptr)(d+n) % gb_size_of(isize)) {
if (!n--)
return dest;
d[n] = s[n];
}
while (n >= gb_size_of(isize)) {
n -= gb_size_of(isize);
*cast(isize *)(d+n) = *cast(isize *)(s+n);
}
}
while (n) n--, d[n] = s[n];
}
return dest;
}
if (dest == NULL) {
return NULL;
}
if (n == 0)
return dest;
s[0] = s[n-1] = c;
if (n < 3)
return dest;
s[1] = s[n-2] = c;
s[2] = s[n-3] = c;
if (n < 7)
return dest;
s[3] = s[n-4] = c;
if (n < 9)
return dest;
k = -cast(intptr)s & 3;
s += k;
n -= k;
n &= -4;
{
u64 c64 = (cast(u64)c32 << 32) | c32;
while (n > 31) {
*cast(u64 *)(s+0) = c64;
*cast(u64 *)(s+8) = c64;
*cast(u64 *)(s+16) = c64;
*cast(u64 *)(s+24) = c64;
n -= 32;
s += 32;
}
}
return dest;
}
gb_inline i32 gb_memcompare(void const *s1, void const *s2, isize size) {
// TODO(bill): Heavily optimize
u8 const *s1p8 = cast(u8 const *)s1;
u8 const *s2p8 = cast(u8 const *)s2;
while (size--) {
if (*s1p8 != *s2p8) {
return (*s1p8 - *s2p8);
}
s1p8++, s2p8++;
}
return 0;
}
if (size == 4) {
gb_swap(u32, *cast(u32 *)i, *cast(u32 *)j);
} else if (size == 8) {
gb_swap(u64, *cast(u64 *)i, *cast(u64 *)j);
} else if (size < 8) {
u8 *a = cast(u8 *)i;
u8 *b = cast(u8 *)j;
if (a != b) {
while (size--) {
gb_swap(u8, *a, *b);
a++, b++;
}
}
} else {
char buffer[256];
gb_memcopy(buffer, i, size);
gb_memcopy(i, j, size);
gb_memcopy(j, buffer, size);
}
}
if (new_size == 0) {
gb_free(a, old_memory);
return NULL;
}
if (old_size == new_size) {
return old_memory;
} else {
void *new_memory = gb_alloc_align(a, new_size, alignment);
if (!new_memory) return NULL;
gb_memmove(new_memory, old_memory, gb_min(new_size, old_size));
gb_free(a, old_memory);
return new_memory;
}
}
////////////////////////////////////////////////////////////////
//
// Concurrency
//
//
// IMPORTANT TODO(bill): Use compiler intrinsics for the atomics
#elif defined(GB_CPU_X86)
#else
#error TODO(bill): Implement Atomics for this CPU
#endif
#elif defined(GB_ARCH_64_BIT)
#if defined(GB_SYSTEM_WINDOWS)
gb_inline void gb_semaphore_init (gbSemaphore *s) { s-
>win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL); }
gb_inline void gb_semaphore_destroy(gbSemaphore *s)
{ CloseHandle(s->win32_handle); }
gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count)
{ ReleaseSemaphore(s->win32_handle, count, NULL); }
gb_inline void gb_semaphore_wait (gbSemaphore *s)
{ WaitForSingleObject(s->win32_handle, INFINITE); }
#elif defined(GB_SYSTEM_OSX)
gb_inline void gb_semaphore_init (gbSemaphore *s)
{ semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); }
gb_inline void gb_semaphore_destroy(gbSemaphore *s)
{ semaphore_destroy(mach_task_self(), s->osx_handle); }
gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { while (count
--> 0) semaphore_signal(s->osx_handle); }
gb_inline void gb_semaphore_wait (gbSemaphore *s)
{ semaphore_wait(s->osx_handle); }
#elif defined(GB_SYSTEM_UNIX)
gb_inline void gb_semaphore_init (gbSemaphore *s) { sem_init(&s-
>unix_handle, 0, 0); }
gb_inline void gb_semaphore_destroy(gbSemaphore *s)
{ sem_destroy(&s->unix_handle); }
gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { while (count
--> 0) sem_post(&s->unix_handle); }
gb_inline void gb_semaphore_wait (gbSemaphore *s) { int i; do
{ i = sem_wait(&s->unix_handle); } while (i == -1 && errno == EINTR); }
#else
#error
#endif
#if defined(GB_SYSTEM_WINDOWS)
t->win32_handle = CreateThread(NULL, stack_size, gb__thread_proc, t, 0,
NULL);
GB_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError");
#else
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (stack_size != 0) {
pthread_attr_setstacksize(&attr, stack_size);
}
pthread_create(&t->posix_handle, &attr, gb__thread_proc, t);
pthread_attr_destroy(&attr);
}
#endif
gb_semaphore_wait(&t->semaphore);
}
#if defined(GB_SYSTEM_WINDOWS)
WaitForSingleObject(t->win32_handle, INFINITE);
CloseHandle(t->win32_handle);
t->win32_handle = INVALID_HANDLE_VALUE;
#else
pthread_join(t->posix_handle, NULL);
t->posix_handle = 0;
#endif
t->is_running = false;
}
return thread_id;
}
__try {
RaiseException(0x406d1388, 0, gb_size_of(tn)/4, cast(ULONG_PTR
*)&tn);
} __except(1 /*EXCEPTION_EXECUTE_HANDLER*/) {
}
gb_mutex_destroy(&s->mutex);
gb_mutex_destroy(&s->start);
gb_semaphore_destroy(&s->release);
}
gb_mutex_lock(&s->mutex);
GB_ASSERT(s->target == 0);
s->target = count;
s->current = 0;
s->waiting = 0;
gb_mutex_unlock(&s->mutex);
}
GB_ALLOCATOR_PROC(gb_heap_allocator_proc) {
void *ptr = NULL;
gb_unused(allocator_data);
gb_unused(old_size);
// TODO(bill): Throughly test!
switch (type) {
#if defined(GB_COMPILER_MSVC)
case gbAllocation_Alloc:
ptr = _aligned_malloc(size, alignment);
if (flags & gbAllocatorFlag_ClearToZero)
gb_zero_size(ptr, size);
break;
case gbAllocation_Free:
_aligned_free(old_memory);
break;
case gbAllocation_Resize:
ptr = _aligned_realloc(old_memory, size, alignment);
break;
#elif defined(GB_SYSTEM_LINUX)
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
ptr = aligned_alloc(alignment, size);
// ptr = malloc(size+alignment);
case gbAllocation_Free: {
free(old_memory);
} break;
case gbAllocation_Resize: {
// ptr = realloc(old_memory, size);
ptr = gb_default_resize_align(gb_heap_allocator(), old_memory,
old_size, size, alignment);
} break;
#else
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
posix_memalign(&ptr, alignment, size);
case gbAllocation_Free: {
free(old_memory);
} break;
case gbAllocation_Resize: {
ptr = gb_default_resize_align(gb_heap_allocator(), old_memory,
old_size, size, alignment);
} break;
#endif
case gbAllocation_FreeAll:
break;
}
return ptr;
}
#if defined(GB_SYSTEM_WINDOWS)
void gb_affinity_init(gbAffinity *a) {
SYSTEM_LOGICAL_PROCESSOR_INFORMATION *start_processor_info = NULL;
DWORD length = 0;
b32 result = GetLogicalProcessorInformation(NULL, &length);
gb_zero_item(a);
a->is_accurate = true;
a->core_count = 0;
a->thread_count = 0;
end_processor_info = cast(SYSTEM_LOGICAL_PROCESSOR_INFORMATION
*)gb_pointer_add(start_processor_info, length);
gb_free(gb_heap_allocator(), start_processor_info);
}
}
void gb_affinity_destroy(gbAffinity *a) {
gb_unused(a);
}
available_mask = a->core_masks[core];
for (;;) {
if ((available_mask & check_mask) != 0) {
if (thread-- == 0) {
usize result = SetThreadAffinityMask(GetCurrentThread(),
check_mask);
return result != 0;
}
}
check_mask <<= 1; // NOTE(bill): Onto the next bit
}
}
#elif defined(GB_SYSTEM_OSX)
void gb_affinity_init(gbAffinity *a) {
usize count = 0;
usize count_size = sizeof(count);
a->is_accurate = false;
a->thread_count = 1;
a->core_count = 1;
a->threads_per_core = 1;
#elif defined(GB_SYSTEM_LINUX)
// IMPORTANT TODO(bill): This gbAffinity stuff for linux needs be improved a lot!
// NOTE(zangent): I have to read /proc/cpuinfo to get the number of threads per
core.
#include <stdio.h>
a->thread_count = 1;
a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
a->threads_per_core = 1;
if(a->core_count <= 0) {
a->core_count = 1;
accurate = false;
}
if (cpu_info != NULL) {
for (;;) {
// The 'temporary char'. Everything goes into this char,
// so that we can check against EOF at the end of this loop.
char c;
fclose(cpu_info);
}
if (threads == 0) {
threads = 1;
accurate = false;
}
a->threads_per_core = threads;
a->thread_count = a->threads_per_core * a->core_count;
a->is_accurate = accurate;
////////////////////////////////////////////////////////////////
//
// Virtual Memory
//
//
#if defined(GB_SYSTEM_WINDOWS)
gb_inline gbVirtualMemory gb_vm_alloc(void *addr, isize size) {
gbVirtualMemory vm;
GB_ASSERT(size > 0);
vm.data = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
vm.size = size;
return vm;
}
gb_vm_free(vm);
new_vm = gb_vm_alloc(ptr, size);
if (new_vm.data == ptr)
return new_vm;
if (new_vm.data)
gb_vm_free(new_vm);
return new_vm;
}
#else
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
if (lead_size != 0)
gb_vm_free(gb_virtual_memory(vm.data, lead_size));
if (trail_size != 0)
gb_vm_free(gb_virtual_memory(ptr, trail_size));
return gb_virtual_memory(ptr, size);
#endif
////////////////////////////////////////////////////////////////
//
// Custom Allocation
//
//
//
// Arena Allocator
//
alignment_offset = 0;
result_pointer = cast(isize)arena->physical_start + arena->total_allocated;
mask = alignment - 1;
if (result_pointer & mask)
alignment_offset = alignment - (result_pointer & mask);
return alignment_offset;
}
GB_ALLOCATOR_PROC(gb_arena_allocator_proc) {
gbArena *arena = cast(gbArena *)allocator_data;
void *ptr = NULL;
gb_unused(old_size);
switch (type) {
case gbAllocation_Alloc: {
void *end = gb_pointer_add(arena->physical_start, arena-
>total_allocated);
isize total_size = size + alignment;
case gbAllocation_Free:
// NOTE(bill): Free all at once
// Use Temp_Arena_Memory if you want to free a block
break;
case gbAllocation_FreeAll:
arena->total_allocated = 0;
break;
case gbAllocation_Resize: {
// TODO(bill): Check if ptr is on top of stack and just extend
gbAllocator a = gb_arena_allocator(arena);
ptr = gb_default_resize_align(a, old_memory, old_size, size,
alignment);
} break;
}
return ptr;
}
gb_inline gbTempArenaMemory gb_temp_arena_memory_begin(gbArena *arena) {
gbTempArenaMemory tmp;
tmp.arena = arena;
tmp.original_count = arena->total_allocated;
arena->temp_count++;
return tmp;
}
//
// Pool Allocator
//
gb_zero_item(pool);
pool->backing = backing;
pool->block_size = block_size;
pool->block_align = block_align;
pool->physical_start = data;
pool->free_list = data;
}
gb_unused(old_size);
switch (type) {
case gbAllocation_Alloc: {
uintptr next_free;
GB_ASSERT(size == pool->block_size);
GB_ASSERT(alignment == pool->block_align);
GB_ASSERT(pool->free_list != NULL);
case gbAllocation_Free: {
uintptr *next;
if (old_memory == NULL) return NULL;
case gbAllocation_FreeAll:
// TODO(bill):
break;
case gbAllocation_Resize:
// NOTE(bill): Cannot resize
GB_PANIC("You cannot resize something allocated by with a pool.");
break;
}
return ptr;
}
gb_inline gbAllocationHeader *gb_allocation_header(void *data) {
isize *p = cast(isize *)data;
while (p[-1] == cast(isize)(-1)) {
p--;
}
return cast(gbAllocationHeader *)p - 1;
}
//
// Free List Allocator
//
fl->physical_start = start;
fl->total_size = size;
fl->curr_block = cast(gbFreeListBlock *)start;
fl->curr_block->size = size;
fl->curr_block->next = NULL;
}
GB_ALLOCATOR_PROC(gb_free_list_allocator_proc) {
gbFreeList *fl = cast(gbFreeList *)allocator_data;
void *ptr = NULL;
GB_ASSERT_NOT_NULL(fl);
switch (type) {
case gbAllocation_Alloc: {
gbFreeListBlock *prev_block = NULL;
gbFreeListBlock *curr_block = fl->curr_block;
while (curr_block) {
isize total_size;
gbAllocationHeader *header;
if (prev_block)
prev_block->next = curr_block->next;
else
fl->curr_block = curr_block->next;
} else {
// NOTE(bill): Create a new block for the remaining memory
gbFreeListBlock *next_block;
next_block = cast(gbFreeListBlock
*)gb_pointer_add(curr_block, total_size);
if (prev_block)
prev_block->next = next_block;
else
fl->curr_block = next_block;
}
fl->total_allocated += total_size;
fl->allocation_count++;
case gbAllocation_Free: {
gbAllocationHeader *header = gb_allocation_header(old_memory);
isize block_size = header->size;
uintptr block_start, block_end;
gbFreeListBlock *prev_block = NULL;
gbFreeListBlock *curr_block = fl->curr_block;
block_start = cast(uintptr)header;
block_end = cast(uintptr)block_start + block_size;
while (curr_block) {
if (cast(uintptr)curr_block >= block_end)
break;
prev_block = curr_block;
curr_block = curr_block->next;
}
if (prev_block == NULL) {
prev_block = cast(gbFreeListBlock *)block_start;
prev_block->size = block_size;
prev_block->next = fl->curr_block;
fl->curr_block = prev_block;
} else if ((cast(uintptr)prev_block + prev_block->size) == block_start)
{
prev_block->size += block_size;
} else {
gbFreeListBlock *tmp = cast(gbFreeListBlock *)block_start;
tmp->size = block_size;
tmp->next = prev_block->next;
prev_block->next = tmp;
prev_block = tmp;
}
fl->allocation_count--;
fl->total_allocated -= block_size;
} break;
case gbAllocation_FreeAll:
gb_free_list_init(fl, fl->physical_start, fl->total_size);
break;
case gbAllocation_Resize:
ptr = gb_default_resize_align(gb_free_list_allocator(fl), old_memory,
old_size, size, alignment);
break;
}
return ptr;
}
GB_ALLOCATOR_PROC(gb_scratch_allocator_proc) {
gbScratchMemory *s = cast(gbScratchMemory *)allocator_data;
void *ptr = NULL;
GB_ASSERT_NOT_NULL(s);
switch (type) {
case gbAllocation_Alloc: {
void *pt = s->alloc_point;
gbAllocationHeader *header = cast(gbAllocationHeader *)pt;
void *data = gb_align_forward(header+1, alignment);
void *end = gb_pointer_add(s->physical_start, s->total_size);
GB_ASSERT(alignment % 4 == 0);
size = ((size + 3)/4)*4;
pt = gb_pointer_add(pt, size);
if (!gb_scratch_memory_is_in_use(s, pt)) {
gb_allocation_header_fill(header, pt, gb_pointer_diff(header,
pt));
s->alloc_point = cast(u8 *)pt;
ptr = data;
}
if (flags & gbAllocatorFlag_ClearToZero)
gb_zero_size(ptr, size);
} break;
case gbAllocation_Free: {
if (old_memory) {
void *end = gb_pointer_add(s->physical_start, s->total_size);
if (old_memory < s->physical_start || old_memory >= end) {
GB_ASSERT(false);
} else {
// NOTE(bill): Mark as free
gbAllocationHeader *h = gb_allocation_header(old_memory);
GB_ASSERT((h->size & GB_ISIZE_HIGH_BIT) == 0);
h->size = h->size | GB_ISIZE_HIGH_BIT;
case gbAllocation_FreeAll:
s->alloc_point = s->physical_start;
s->free_point = s->physical_start;
break;
case gbAllocation_Resize:
ptr = gb_default_resize_align(gb_scratch_allocator(s), old_memory,
old_size, size, alignment);
break;
}
return ptr;
}
////////////////////////////////////////////////////////////////
//
// Sorting
//
//
#define GB__COMPARE_PROC(Type) \
gb_global isize gb__##Type##_cmp_offset; GB_COMPARE_PROC(gb__##Type##_cmp) { \
Type const p = *cast(Type const *)gb_pointer_add_const(a,
gb__##Type##_cmp_offset); \
Type const q = *cast(Type const *)gb_pointer_add_const(b,
gb__##Type##_cmp_offset); \
return p < q ? -1 : p > q; \
} \
GB_COMPARE_PROC_PTR(gb_##Type##_cmp(isize offset)) { \
gb__##Type##_cmp_offset = offset; \
return &gb__##Type##_cmp; \
}
GB__COMPARE_PROC(i16);
GB__COMPARE_PROC(i32);
GB__COMPARE_PROC(i64);
GB__COMPARE_PROC(isize);
GB__COMPARE_PROC(f32);
GB__COMPARE_PROC(f64);
GB__COMPARE_PROC(char);
#undef GB__COMPARE_PROC
for (;;) {
if ((limit-base) > threshold) {
// NOTE(bill): Quick sort
i = base + size;
j = limit - size;
for (;;) {
do i += size; while (cmp(i, base) < 0);
do j -= size; while (cmp(j, base) > 0);
if (i > j) break;
gb_memswap(i, j, size);
}
gb_memswap(base, j, size);
#undef GB__SORT_PUSH
#undef GB__SORT_POP
GB_RADIX_SORT_PROC_GEN(u8);
GB_RADIX_SORT_PROC_GEN(u16);
GB_RADIX_SORT_PROC_GEN(u32);
GB_RADIX_SORT_PROC_GEN(u64);
gb_inline isize gb_binary_search(void const *base, isize count, isize size, void
const *key, gbCompareProc compare_proc) {
isize start = 0;
isize end = count;
return -1;
}
////////////////////////////////////////////////////////////////
//
// Char things
//
//
str += inc;
}
return count;
}
str += inc;
max_len -= inc;
}
return count;
}
gb_inline char const *gb_strtok(char *output, char const *src, char const *delimit)
{
while (*src && gb_char_first_occurence(delimit, *src) != NULL) {
*output++ = *src++;
}
*output = 0;
return *src ? src+1 : src;
}
return result;
}
if (*text == '-') {
negative = true;
text++;
}
for (;;) {
i64 v;
if (gb_char_is_digit(*text)) {
v = *text - '0';
} else if (base == 16 && gb_char_is_hex_digit(*text)) {
v = gb_hex_digit_to_int(*text);
} else {
break;
}
result *= base;
result += v;
text++;
}
if (value) {
if (negative) result = -result;
*value = result;
}
for (;;) {
u64 v;
if (gb_char_is_digit(*text)) {
v = *text - '0';
} else if (base == 16 && gb_char_is_hex_digit(*text)) {
v = gb_hex_digit_to_int(*text);
} else {
break;
}
result *= base;
result += v;
text++;
}
if (!base) {
if ((gb_strlen(str) > 2) && (gb_strncmp(str, "0x", 2) == 0)) {
base = 16;
} else {
base = 10;
}
}
if (!base) {
if ((gb_strlen(str) > 2) && (gb_strncmp(str, "0x", 2) == 0)) {
base = 16;
} else {
base = 10;
}
}
if (value) {
while (value > 0) {
*buf++ = gb__num_to_char_table[value % base];
value /= base;
}
} else {
*buf++ = '0';
}
*buf = '\0';
gb_strrev(string);
}
while (gb_char_is_space(*str)) {
str++;
}
sign = 1.0;
if (*str == '-') {
sign = -1.0;
str++;
} else if (*str == '+') {
str++;
}
if (*str == '.') {
f64 pow10 = 10.0;
str++;
while (gb_char_is_digit(*str)) {
value += (*str-'0') / pow10;
pow10 *= 10.0;
str++;
}
}
frac = 0;
scale = 1.0;
if ((*str == 'e') || (*str == 'E')) {
u32 exp;
str++;
if (*str == '-') {
frac = 1;
str++;
} else if (*str == '+') {
str++;
}
return result;
}
gb_inline void gb__set_string_length (gbString str, isize len)
{ GB_STRING_HEADER(str)->length = len; }
gb_inline void gb__set_string_capacity(gbString str, isize cap)
{ GB_STRING_HEADER(str)->capacity = cap; }
gbString str;
gbStringHeader *header;
return str;
}
gbString str;
gbStringHeader *header;
return str;
}
return str;
}
}
return true;
}
if (str != start_pos)
gb_memmove(str, start_pos, len);
str[len] = '\0';
gb__set_string_length(str, len);
return str;
}
if (str_len > 0) {
u8 s0 = str[0];
u8 x = gb__utf8_first[s0], sz;
u8 b1, b2, b3;
gbUtf8AcceptRange accept;
if (x >= 0xf0) {
Rune mask = (cast(Rune)x << 31) >> 31;
codepoint = (cast(Rune)s0 & (~mask)) | (GB_RUNE_INVALID & mask);
width = 1;
goto end;
}
if (s0 < 0x80) {
codepoint = s0;
width = 1;
goto end;
}
sz = x&7;
accept = gb__utf8_accept_ranges[x>>4];
if (str_len < gb_size_of(sz))
goto invalid_codepoint;
b1 = str[1];
if (b1 < accept.lo || accept.hi < b1)
goto invalid_codepoint;
if (sz == 2) {
codepoint = (cast(Rune)s0&0x1f)<<6 | (cast(Rune)b1&0x3f);
width = 2;
goto end;
}
b2 = str[2];
if (!gb_is_between(b2, 0x80, 0xbf))
goto invalid_codepoint;
if (sz == 3) {
codepoint = (cast(Rune)s0&0x1f)<<12 | (cast(Rune)b1&0x3f)<<6 |
(cast(Rune)b2&0x3f);
width = 3;
goto end;
}
b3 = str[3];
if (!gb_is_between(b3, 0x80, 0xbf))
goto invalid_codepoint;
invalid_codepoint:
codepoint = GB_RUNE_INVALID;
width = 1;
}
end:
if (codepoint_out) *codepoint_out = codepoint;
return width;
}
if (i <= (1<<16)-1) {
buf[0] = 0xe0 | cast(u8)(r>>12);
buf[1] = 0x80 | (cast(u8)(r>>6)&mask);
buf[2] = 0x80 | (cast(u8)(r)&mask);
return 3;
}
////////////////////////////////////////////////////////////////
//
// gbArray
//
//
if (capacity == h->capacity)
return array;
{
isize size = gb_size_of(gbArrayHeader) + element_size*capacity;
gbArrayHeader *nh = cast(gbArrayHeader *)gb_alloc(h->allocator, size);
gb_memmove(nh, h, gb_size_of(gbArrayHeader) + element_size*h->count);
nh->allocator = h->allocator;
nh->count = h->count;
nh->capacity = capacity;
gb_free(h->allocator, h);
return nh+1;
}
}
////////////////////////////////////////////////////////////////
//
// Hashing functions
//
//
while (len) {
for (i = 0; i+7 < block_len; i += 8) {
a += bytes[0], b += a;
a += bytes[1], b += a;
a += bytes[2], b += a;
a += bytes[3], b += a;
a += bytes[4], b += a;
a += bytes[5], b += a;
a += bytes[6], b += a;
a += bytes[7], b += a;
bytes += 8;
}
for (; i < block_len; i++) {
a += *bytes++, b += a;
}
a %= MOD_ALDER, b %= MOD_ALDER;
len -= block_len;
block_len = 5552;
}
return h;
}
return h;
}
return h;
}
return h;
}
hash ^= k;
hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
}
k1 *= c1;
k1 = (k1 << r1) | (k1 >> (32 - r1));
k1 *= c2;
hash ^= k1;
}
hash ^= len;
hash ^= (hash >> 16);
hash *= 0x85ebca6b;
hash ^= (hash >> 13);
hash *= 0xc2b2ae35;
hash ^= (hash >> 16);
return hash;
}
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
}
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
#else
u64 h;
u32 const m = 0x5bd1e995;
i32 const r = 24;
k2 = *data++;
k2 *= m;
k2 ^= k2 >> r;
k2 *= m;
h2 *= m;
h2 ^= k2;
len -= 4;
}
if (len >= 4) {
u32 k1 = *data++;
k1 *= m;
k1 ^= k1 >> r;
k1 *= m;
h1 *= m;
h1 ^= k1;
len -= 4;
}
switch (len) {
case 3: h2 ^= (cast(u8 const *)data)[2] << 16;
case 2: h2 ^= (cast(u8 const *)data)[1] << 8;
case 1: h2 ^= (cast(u8 const *)data)[0] << 0;
h2 *= m;
};
h1 ^= h2 >> 18;
h1 *= m;
h2 ^= h1 >> 22;
h2 *= m;
h1 ^= h2 >> 17;
h1 *= m;
h2 ^= h1 >> 19;
h2 *= m;
h = h1;
h = (h << 32) | h2;
return h;
#endif
}
////////////////////////////////////////////////////////////////
//
// File Handling
//
//
#if defined(GB_SYSTEM_WINDOWS)
gb_internal GB_FILE_SEEK_PROC(gb__win32_file_seek) {
LARGE_INTEGER li_offset;
li_offset.QuadPart = offset;
if (!SetFilePointerEx(fd.p, li_offset, &li_offset, whence)) {
return false;
}
gb_internal GB_FILE_READ_AT_PROC(gb__win32_file_read) {
b32 result = false;
DWORD size_ = cast(DWORD)(size > I32_MAX ? I32_MAX : size);
DWORD bytes_read_;
gb__win32_file_seek(fd, offset, gbSeekWhence_Begin, NULL);
if (ReadFile(fd.p, buffer, size_, &bytes_read_, NULL)) {
if (bytes_read) *bytes_read = bytes_read_;
result = true;
}
return result;
}
gb_internal GB_FILE_WRITE_AT_PROC(gb__win32_file_write) {
DWORD size_ = cast(DWORD)(size > I32_MAX ? I32_MAX : size);
DWORD bytes_written_;
gb__win32_file_seek(fd, offset, gbSeekWhence_Begin, NULL);
if (WriteFile(fd.p, buffer, size_, &bytes_written_, NULL)) {
if (bytes_written) *bytes_written = bytes_written_;
return true;
}
return false;
}
gb_internal GB_FILE_CLOSE_PROC(gb__win32_file_close) {
CloseHandle(fd.p);
}
gb_no_inline GB_FILE_OPEN_PROC(gb__win32_file_open) {
DWORD desired_access;
DWORD creation_disposition;
void *handle;
wchar_t *w_text;
gb_free(gb_heap_allocator(), w_text);
if (handle == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
switch (err) {
case ERROR_FILE_NOT_FOUND: return gbFileError_NotExists;
case ERROR_FILE_EXISTS: return gbFileError_Exists;
case ERROR_ALREADY_EXISTS: return gbFileError_Exists;
case ERROR_ACCESS_DENIED: return gbFileError_Permission;
}
return gbFileError_Invalid;
}
fd->p = handle;
*ops = gbDefaultFileOperations;
return gbFileError_None;
}
#else // POSIX
gb_internal GB_FILE_SEEK_PROC(gb__posix_file_seek) {
#if defined(GB_SYSTEM_OSX)
i64 res = lseek(fd.i, offset, whence);
#else
i64 res = lseek64(fd.i, offset, whence);
#endif
if (res < 0) return false;
if (new_offset) *new_offset = res;
return true;
}
gb_internal GB_FILE_READ_AT_PROC(gb__posix_file_read) {
isize res = pread(fd.i, buffer, size, offset);
if (res < 0) return false;
if (bytes_read) *bytes_read = res;
return true;
}
gb_internal GB_FILE_WRITE_AT_PROC(gb__posix_file_write) {
isize res;
i64 curr_offset = 0;
gb__posix_file_seek(fd, 0, gbSeekWhence_Current, &curr_offset);
if (curr_offset == offset) {
// NOTE(bill): Writing to stdout et al. doesn't like pwrite for
numerous reasons
res = write(cast(int)fd.i, buffer, size);
} else {
res = pwrite(cast(int)fd.i, buffer, size, offset);
}
if (res < 0) return false;
if (bytes_written) *bytes_written = res;
return true;
}
gb_internal GB_FILE_CLOSE_PROC(gb__posix_file_close) {
close(fd.i);
}
gb_no_inline GB_FILE_OPEN_PROC(gb__posix_file_open) {
i32 os_mode;
switch (mode & gbFileMode_Modes) {
case gbFileMode_Read:
os_mode = O_RDONLY;
break;
case gbFileMode_Write:
os_mode = O_WRONLY | O_CREAT | O_TRUNC;
break;
case gbFileMode_Append:
os_mode = O_WRONLY | O_APPEND | O_CREAT;
break;
case gbFileMode_Read | gbFileMode_Rw:
os_mode = O_RDWR;
break;
case gbFileMode_Write | gbFileMode_Rw:
os_mode = O_RDWR | O_CREAT | O_TRUNC;
break;
case gbFileMode_Append | gbFileMode_Rw:
os_mode = O_RDWR | O_APPEND | O_CREAT;
break;
default:
GB_PANIC("Invalid file mode");
return gbFileError_Invalid;
}
*ops = gbDefaultFileOperations;
return gbFileError_None;
}
#endif
f->ops = ops;
f->fd = fd;
f->filename = gb_alloc_array(gb_heap_allocator(), char, len+1);
gb_memcopy(cast(char *)f->filename, cast(char *)filename, len+1);
f->last_write_time = gb_file_last_write_time(f->filename);
return err;
}
#if defined(GB_COMPILER_MSVC)
if (f->filename != NULL) {
gb_free(gb_heap_allocator(), cast(char *)f->filename);
}
#else
// TODO HACK(bill): Memory Leak!!!
#endif
#if defined(GB_SYSTEM_WINDOWS)
if (f->fd.p == INVALID_HANDLE_VALUE) {
return gbFileError_Invalid;
}
#else
if (f->fd.i < 0) {
return gbFileError_Invalid;
}
#endif
return gbFileError_None;
}
gb_inline b32 gb_file_write_at_check(gbFile *f, void const *buffer, isize size, i64
offset, isize *bytes_written) {
if (!f->ops.read_at) f->ops = gbDefaultFileOperations;
return f->ops.write_at(f->fd, buffer, size, offset, bytes_written);
}
gb_inline b32 gb_file_read_at(gbFile *f, void *buffer, isize size, i64 offset) {
return gb_file_read_at_check(f, buffer, size, offset, NULL);
}
gb_inline b32 gb_file_write_at(gbFile *f, void const *buffer, isize size, i64
offset) {
return gb_file_write_at_check(f, buffer, size, offset, NULL);
}
#if defined(GB_SYSTEM_WINDOWS)
#if defined(GB_SYSTEM_WINDOWS)
gbFileTime gb_file_last_write_time(char const *filepath) {
ULARGE_INTEGER li = {0};
FILETIME last_write_time = {0};
WIN32_FILE_ATTRIBUTE_DATA data = {0};
gbAllocator a = gb_heap_allocator();
li.LowPart = last_write_time.dwLowDateTime;
li.HighPart = last_write_time.dwHighDateTime;
return cast(gbFileTime)li.QuadPart;
}
#else
gbFileTime gb_file_last_write_time(char const *filepath) {
time_t result = 0;
struct stat file_stat;
if (stat(filepath, &file_stat) == 0) {
result = file_stat.st_mtime;
}
return cast(gbFileTime)result;
}
close(new_fd);
close(existing_fd);
#endif
result.allocator = a;
return result;
}
len = gb_strlen(fullpath);
return result;
#endif
}
////////////////////////////////////////////////////////////////
//
// Printing
//
//
gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) {
gb_local_persist char buf[4096];
isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace
return len;
}
enum {
gbFmt_Minus = GB_BIT(0),
gbFmt_Plus = GB_BIT(1),
gbFmt_Alt = GB_BIT(2),
gbFmt_Space = GB_BIT(3),
gbFmt_Zero = GB_BIT(4),
gbFmt_Char = GB_BIT(5),
gbFmt_Short = GB_BIT(6),
gbFmt_Int = GB_BIT(7),
gbFmt_Long = GB_BIT(8),
gbFmt_Llong = GB_BIT(9),
gbFmt_Size = GB_BIT(10),
gbFmt_Intptr = GB_BIT(11),
gbFmt_Unsigned = GB_BIT(12),
gbFmt_Lower = GB_BIT(13),
gbFmt_Upper = GB_BIT(14),
gbFmt_Done = GB_BIT(30),
gbFmt_Ints = gbFmt_Char|gbFmt_Short|gbFmt_Int|gbFmt_Long|gbFmt_Llong|
gbFmt_Size|gbFmt_Intptr
};
typedef struct {
i32 base;
i32 flags;
i32 width;
i32 precision;
} gbprivFmtInfo;
if (info) {
if (info->flags & gbFmt_Upper) {
gb_str_to_upper(text);
} else if (info->flags & gbFmt_Lower) {
gb_str_to_lower(text);
}
}
return res;
}
if (arg) {
u64 value;
if (arg < 0) {
if (remaining > 1) {
*text = '-', remaining--;
}
text++;
arg = -arg;
} else if (info->flags & gbFmt_Minus) {
if (remaining > 1) {
*text = '+', remaining--;
}
text++;
}
value = cast(u64)arg;
len = gb__print_u64(text, remaining, NULL, value);
text += len;
if (info->precision < 0) {
info->precision = 6;
}
while (len--) {
if (text_begin+len < end) {
text_begin[len] = fill;
}
}
}
while (*fmt) {
gbprivFmtInfo info = {0};
isize len = 0;
info.precision = -1;
if (*fmt == '%') {
do {
switch (*++fmt) {
case '-': info.flags |= gbFmt_Minus; break;
case '+': info.flags |= gbFmt_Plus; break;
case '#': info.flags |= gbFmt_Alt; break;
case ' ': info.flags |= gbFmt_Space; break;
case '0': info.flags |= gbFmt_Zero; break;
default: info.flags |= gbFmt_Done; break;
}
} while (!(info.flags & gbFmt_Done));
}
switch (*fmt++) {
case 'h':
if (*fmt == 'h') { // hh => char
info.flags |= gbFmt_Char;
fmt++;
} else { // h => short
info.flags |= gbFmt_Short;
}
break;
case 'l':
if (*fmt == 'l') { // ll => long long
info.flags |= gbFmt_Llong;
fmt++;
} else { // l => long
info.flags |= gbFmt_Long;
}
break;
break;
switch (*fmt) {
case 'u':
info.flags |= gbFmt_Unsigned;
// fallthrough
case 'd':
case 'i':
info.base = 10;
break;
case 'o':
info.base = 8;
break;
case 'x':
info.base = 16;
info.flags |= (gbFmt_Unsigned | gbFmt_Lower);
break;
case 'X':
info.base = 16;
info.flags |= (gbFmt_Unsigned | gbFmt_Upper);
break;
case 'f':
case 'F':
case 'g':
case 'G':
len = gb__print_f64(text, remaining, &info, va_arg(va, f64));
break;
case 'a':
case 'A':
// TODO(bill):
break;
case 'c':
len = gb__print_char(text, remaining, &info, cast(char)va_arg(va,
int));
break;
case 's':
len = gb__print_string(text, remaining, &info, va_arg(va, char
*));
break;
case 'p':
info.base = 16;
info.flags |= (gbFmt_Lower|gbFmt_Unsigned|gbFmt_Alt|
gbFmt_Intptr);
break;
case '%':
len = gb__print_char(text, remaining, &info, '%');
break;
fmt++;
if (info.base != 0) {
if (info.flags & gbFmt_Unsigned) {
u64 value = 0;
switch (info.flags & gbFmt_Ints) {
case gbFmt_Char: value = cast(u64)cast(u8) va_arg(va,
int); break;
case gbFmt_Short: value = cast(u64)cast(u16)va_arg(va,
int); break;
case gbFmt_Long: value = cast(u64)va_arg(va, unsigned
long); break;
case gbFmt_Llong: value = cast(u64)va_arg(va, unsigned
long long); break;
case gbFmt_Size: value = cast(u64)va_arg(va, usize);
break;
case gbFmt_Intptr: value = cast(u64)va_arg(va, uintptr);
break;
default: value = cast(u64)va_arg(va, unsigned
int); break;
}
} else {
i64 value = 0;
switch (info.flags & gbFmt_Ints) {
case gbFmt_Char: value = cast(i64)cast(i8) va_arg(va,
int); break;
case gbFmt_Short: value = cast(i64)cast(i16)va_arg(va,
int); break;
case gbFmt_Long: value = cast(i64)va_arg(va, long);
break;
case gbFmt_Llong: value = cast(i64)va_arg(va, long long);
break;
case gbFmt_Size: value = cast(i64)va_arg(va, usize);
break;
case gbFmt_Intptr: value = cast(i64)va_arg(va, uintptr);
break;
default: value = cast(i64)va_arg(va, int);
break;
}
text += len;
if (len >= remaining) {
remaining = gb_min(remaining, 1);
} else {
remaining -= len;
}
}
*text++ = '\0';
res = (text - text_begin);
return (res >= max_len || res < 0) ? -1 : res;
}
////////////////////////////////////////////////////////////////
//
// DLL Handling
//
//
#if defined(GB_SYSTEM_WINDOWS)
#else // POSIX
#endif
////////////////////////////////////////////////////////////////
//
// Time
//
//
#if defined(GB_SYSTEM_WINDOWS)
QueryPerformanceCounter(&counter);
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
return li.QuadPart/10;
}
#else
if (!gb__timestart) {
mach_timebase_info_data_t tb = {0};
mach_timebase_info(&tb);
gb__timebase = tb.numer;
gb__timebase /= tb.denom;
gb__timestart = mach_absolute_time();
}
#endif
////////////////////////////////////////////////////////////////
//
// Miscellany
//
//
start = gb_time_now();
remaining = (interval - cast(u64)(interval*start)%interval) /
cast(f64)interval;
end = start + remaining;
do {
curr = gb_time_now();
accum += cast(u32)curr;
} while (curr >= end);
return accum;
}
r->offsets[0] = gb__get_noise_from_time();
r->offsets[1] = gb_atomic32_fetch_add(&gb__random_shared_counter, 1);
r->offsets[2] = gb_thread_current_id();
r->offsets[3] = gb_thread_current_id() * 3 + 1;
time = gb_utc_time_now();
r->offsets[4] = cast(u32)(time >> 32);
r->offsets[5] = cast(u32)time;
r->offsets[6] = gb__get_noise_from_time();
tick = gb_rdtsc();
r->offsets[7] = cast(u32)(tick ^ (tick >> 32));
return x;
}
// NOTE(bill): Semi-cc'ed from gb_math to remove need for fmod and math.h
f64 gb__copy_sign64(f64 x, f64 y) {
i64 ix, iy;
ix = *(i64 *)&x;
iy = *(i64 *)&y;
ix &= 0x7fffffffffffffff;
ix |= iy & 0x8000000000000000;
return *cast(f64 *)&ix;
}
#if defined(GB_SYSTEM_WINDOWS)
gb_inline void gb_exit(u32 code) { ExitProcess(code); }
#else
gb_inline void gb_exit(u32 code) { exit(code); }
#endif
////////////////////////////////////////////////////////////////
//
// Platform
//
//
#if defined(GB_PLATFORM)
#if defined(GB_SYSTEM_WINDOWS)
#ifndef ERROR_DEVICE_NOT_CONNECTED
#define ERROR_DEVICE_NOT_CONNECTED 1167
#endif
GB_XINPUT_GET_STATE(gbXInputGetState_Stub) {
gb_unused(dwUserIndex); gb_unused(pState);
return ERROR_DEVICE_NOT_CONNECTED;
}
GB_XINPUT_SET_STATE(gbXInputSetState_Stub) {
gb_unused(dwUserIndex); gb_unused(pVibration);
return ERROR_DEVICE_NOT_CONNECTED;
}
return result;
}
if (width == 0 || height == 0) {
return;
}
p->window_width = width;
p->window_height = height;
bmi.bmiHeader.biSize = gb_size_of(bmi.bmiHeader);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = height; // NOTE(bill): -ve is top-down,
+ve is bottom-up
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = cast(u16)p-
>sw_framebuffer.bits_per_pixel;
bmi.bmiHeader.biCompression = 0 /*BI_RGB*/;
p->sw_framebuffer.win32_bmi = bmi;
if (p->sw_framebuffer.memory) {
gb_vm_free(gb_virtual_memory(p->sw_framebuffer.memory, p-
>sw_framebuffer.memory_size));
}
{
isize memory_size = p->sw_framebuffer.pitch * height;
gbVirtualMemory vm = gb_vm_alloc(0, memory_size);
p->sw_framebuffer.memory = vm.data;
p->sw_framebuffer.memory_size = vm.size;
}
}
}
// NOTE(bill): Keyboard
rid[0].usUsagePage = 0x01;
rid[0].usUsage = 0x06;
rid[0].dwFlags = 0x00000030/*RIDEV_NOLEGACY*/; // NOTE(bill): Do
not generate legacy messages such as WM_KEYDOWN
rid[0].hwndTarget = hWnd;
// NOTE(bill): Mouse
rid[1].usUsagePage = 0x01;
rid[1].usUsage = 0x02;
rid[1].dwFlags = 0; // NOTE(bill): adds HID mouse and also allows
legacy mouse messages to allow for window movement etc.
rid[1].hwndTarget = hWnd;
if (RegisterRawInputDevices(rid, gb_count_of(rid), gb_size_of(rid[0]))
== false) {
DWORD err = GetLastError();
GB_PANIC("Failed to initialize raw input device for win32."
"Err: %u", err);
}
}
if (!platform) {
return DefWindowProcW(hWnd, msg, wParam, lParam);
}
switch (msg) {
case WM_CLOSE:
case WM_DESTROY:
platform->window_is_closed = true;
return 0;
case WM_QUIT: {
platform->quit_requested = true;
} break;
case WM_UNICHAR: {
if (window_has_focus) {
if (wParam == '\r') {
wParam = '\n';
}
// TODO(bill): Does this need to be thread-safe?
platform->char_buffer[platform->char_buffer_count++] =
cast(Rune)wParam;
}
} break;
case WM_INPUT: {
RAWINPUT raw = {0};
unsigned int size = gb_size_of(RAWINPUT);
if (vk == 255) {
// NOTE(bill): Discard "fake keys"
return 0;
} else if (vk == VK_SHIFT) {
// NOTE(bill): Correct left/right shift
vk = MapVirtualKeyW(scan_code, MAPVK_VSC_TO_VK_EX);
} else if (vk == VK_NUMLOCK) {
// NOTE(bill): Correct PAUSE/BREAK and NUM LOCK and set the
extended bit
scan_code = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC) | 0x100;
}
if (is_e1) {
// NOTE(bill): Escaped sequences, turn vk into the correct
scan code
// except for VK_PAUSE (it's a bug)
if (vk == VK_PAUSE) {
scan_code = 0x45;
} else {
scan_code = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC);
}
}
switch (vk) {
case VK_CONTROL: vk = (is_e0) ? VK_RCONTROL : VK_LCONTROL; break;
case VK_MENU: vk = (is_e0) ? VK_RMENU : VK_LMENU; break;
} break;
case RIM_TYPEMOUSE: {
RAWMOUSE *raw_mouse = &raw.data.mouse;
u16 flags = raw_mouse->usButtonFlags;
long dx = +raw_mouse->lLastX;
long dy = -raw_mouse->lLastY;
platform->mouse_raw_dx = dx;
platform->mouse_raw_dy = dy;
} break;
}
} break;
default: break;
}
if (RegisterClassExW(&wc) == 0) {
MessageBoxW(NULL, L"Failed to register the window class", L"ERROR",
MB_OK | MB_ICONEXCLAMATION);
return false;
}
if (ChangeDisplaySettingsW(&screen_settings, CDS_FULLSCREEN) !=
DISP_CHANGE_SUCCESSFUL) {
if (MessageBoxW(NULL, L"The requested fullscreen mode is not
supported by\n"
L"your video card. Use windowed mode instead?",
L"",
MB_YESNO|MB_ICONEXCLAMATION) == IDYES) {
window_flags &= ~gbWindow_Fullscreen;
} else {
mode = gb_video_mode_get_desktop();
screen_settings.dmPelsWidth = mode.width;
screen_settings.dmPelsHeight = mode.height;
screen_settings.dmBitsPerPel = mode.bits_per_pixel;
ChangeDisplaySettingsW(&screen_settings, CDS_FULLSCREEN);
}
}
}
style |= WS_VISIBLE;
wr.left = 0;
wr.top = 0;
wr.right = mode.width;
wr.bottom = mode.height;
AdjustWindowRect(&wr, style, false);
p->window_flags = window_flags;
p->window_handle = CreateWindowExW(ex_style,
wc.lpszClassName,
cast(wchar_t const
*)gb_utf8_to_ucs2(title_buffer, gb_size_of(title_buffer), window_title),
style,
CW_USEDEFAULT, CW_USEDEFAULT,
wr.right - wr.left, wr.bottom - wr.top,
0, 0,
GetModuleHandleW(NULL),
NULL);
if (!p->window_handle) {
MessageBoxW(NULL, L"Window creation failed", L"Error", MB_OK|
MB_ICONEXCLAMATION);
return false;
}
p->win32_dc = GetDC(cast(HWND)p->window_handle);
p->renderer_type = type;
switch (p->renderer_type) {
case gbRenderer_Opengl: {
wglCreateContextAttribsARB_Proc *wglCreateContextAttribsARB;
i32 attribs[8] = {0};
isize c = 0;
SetPixelFormat(cast(HDC)p->win32_dc, ChoosePixelFormat(cast(HDC)p-
>win32_dc, &pfd), NULL);
p->opengl.context = cast(void *)wglCreateContext(cast(HDC)p->win32_dc);
wglMakeCurrent(cast(HDC)p->win32_dc, cast(HGLRC)p->opengl.context);
if (p->opengl.major > 0) {
attribs[c++] = 0x2091; // WGL_CONTEXT_MAJOR_VERSION_ARB
attribs[c++] = gb_max(p->opengl.major, 1);
}
if (p->opengl.major > 0 && p->opengl.minor >= 0) {
attribs[c++] = 0x2092; // WGL_CONTEXT_MINOR_VERSION_ARB
attribs[c++] = gb_max(p->opengl.minor, 0);
}
if (p->opengl.core) {
attribs[c++] = 0x9126; // WGL_CONTEXT_PROFILE_MASK_ARB
attribs[c++] = 0x0001; // WGL_CONTEXT_CORE_PROFILE_BIT_ARB
} else if (p->opengl.compatible) {
attribs[c++] = 0x9126; // WGL_CONTEXT_PROFILE_MASK_ARB
attribs[c++] = 0x0002; //
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
}
attribs[c++] = 0; // NOTE(bill): tells the proc that this is the end of
attribs
wglCreateContextAttribsARB = cast(wglCreateContextAttribsARB_Proc
*)wglGetProcAddress("wglCreateContextAttribsARB");
if (wglCreateContextAttribsARB) {
HGLRC rc = cast(HGLRC)wglCreateContextAttribsARB(p->win32_dc, 0,
attribs);
if (rc && wglMakeCurrent(cast(HDC)p->win32_dc, rc)) {
p->opengl.context = rc;
} else {
// TODO(bill): Handle errors from GetLastError
// ERROR_INVALID_VERSION_ARB 0x2095
// ERROR_INVALID_PROFILE_ARB 0x2096
}
}
} break;
case gbRenderer_Software:
gb__platform_resize_dib_section(p, mode.width, mode.height);
break;
default:
GB_PANIC("Unknown window type");
break;
}
SetForegroundWindow(cast(HWND)p->window_handle);
SetFocus(cast(HWND)p->window_handle);
SetWindowLongPtrW(cast(HWND)p->window_handle, GWLP_USERDATA,
cast(LONG_PTR)p);
p->window_width = mode.width;
p->window_height = mode.height;
if (p->renderer_type == gbRenderer_Opengl) {
p->opengl.dll_handle = gb_dll_load("opengl32.dll");
}
{ // Load XInput
// TODO(bill): What other dlls should I look for?
gbDllHandle xinput_library = gb_dll_load("xinput1_4.dll");
p->xinput.get_state = gbXInputGetState_Stub;
p->xinput.set_state = gbXInputSetState_Stub;
// Init keys
gb_zero_array(p->keys, gb_count_of(p->keys));
p->is_initialized = true;
return true;
}
#ifndef _XINPUT_H_
typedef struct _XINPUT_GAMEPAD {
u16 wButtons;
u8 bLeftTrigger;
u8 bRightTrigger;
u16 sThumbLX;
u16 sThumbLY;
u16 sThumbRX;
u16 sThumbRY;
} XINPUT_GAMEPAD;
#ifndef XUSER_MAX_COUNT
#define XUSER_MAX_COUNT 4
#endif
GetClientRect(cast(HWND)p->window_handle, &window_rect);
x = window_rect.left;
y = window_rect.top;
w = window_rect.right - window_rect.left;
h = window_rect.bottom - window_rect.top;
p->window_x = x;
p->window_y = y;
p->window_width = w;
p->window_height = h;
GB_MASK_SET(p->window_flags, IsIconic(cast(HWND)p->window_handle) != 0,
gbWindow_Minimized);
GetCursorPos(&mouse_pos);
ScreenToClient(cast(HWND)p->window_handle, &mouse_pos);
{
i32 x = mouse_pos.x;
i32 y = p->window_height-1 - mouse_pos.y;
p->mouse_dx = x - p->mouse_x;
p->mouse_dy = y - p->mouse_y;
p->mouse_x = x;
p->mouse_y = y;
}
if (p->mouse_clip) {
b32 update = false;
i32 x = p->mouse_x;
i32 y = p->mouse_y;
if (p->mouse_x < 0) {
x = 0;
update = true;
} else if (p->mouse_y > p->window_height-1) {
y = p->window_height-1;
update = true;
}
if (p->mouse_y < 0) {
y = 0;
update = true;
} else if (p->mouse_x > p->window_width-1) {
x = p->window_width-1;
update = true;
}
if (update) {
gb_platform_set_mouse_position(p, x, y);
}
}
p->key_modifiers.control = p->keys[gbKey_Lcontrol] | p-
>keys[gbKey_Rcontrol];
p->key_modifiers.alt = p->keys[gbKey_Lalt] | p-
>keys[gbKey_Ralt];
p->key_modifiers.shift = p->keys[gbKey_Lshift] | p-
>keys[gbKey_Rshift];
}
{ // NOTE(bill): Set Controller states
isize max_controller_count = XUSER_MAX_COUNT;
if (max_controller_count > gb_count_of(p->game_controllers)) {
max_controller_count = gb_count_of(p->game_controllers);
}
controller->is_connected = true;
controller->axes[gbControllerAxis_LeftTrigger] =
cast(f32)pad->bLeftTrigger / 255.0f;
controller->axes[gbControllerAxis_RightTrigger] =
cast(f32)pad->bRightTrigger / 255.0f;
if ((controller->axes[gbControllerAxis_LeftX] != 0.0f) ||
(controller->axes[gbControllerAxis_LeftY] != 0.0f)) {
controller->is_analog = true;
}
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_A,
XINPUT_GAMEPAD_A);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_B,
XINPUT_GAMEPAD_B);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_X,
XINPUT_GAMEPAD_X);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Y,
XINPUT_GAMEPAD_Y);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_LeftShoulder,
XINPUT_GAMEPAD_LEFT_SHOULDER);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_RightShoulder,
XINPUT_GAMEPAD_RIGHT_SHOULDER);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Start,
XINPUT_GAMEPAD_START);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Back,
XINPUT_GAMEPAD_BACK);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Left,
XINPUT_GAMEPAD_DPAD_LEFT);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Right,
XINPUT_GAMEPAD_DPAD_RIGHT);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Down,
XINPUT_GAMEPAD_DPAD_DOWN);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_Up,
XINPUT_GAMEPAD_DPAD_UP);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_LeftThumb,
XINPUT_GAMEPAD_LEFT_THUMB);
GB__PROCESS_DIGITAL_BUTTON(gbControllerButton_RightThumb,
XINPUT_GAMEPAD_RIGHT_THUMB);
#undef GB__PROCESS_DIGITAL_BUTTON
}
}
}
switch (message.message) {
case WM_QUIT:
p->quit_requested = true;
break;
default:
TranslateMessage(&message);
DispatchMessageW(&message);
break;
}
}
}
}
{
f64 prev_time = p->curr_time;
f64 curr_time = gb_time_now();
p->dt_for_frame = curr_time - prev_time;
p->curr_time = curr_time;
}
}
DestroyWindow(cast(HWND)p->window_handle);
}
p->mouse_x = point.x;
p->mouse_y = p->window_height-1 - point.y;
}
p->xinput.set_state(cast(DWORD)index, &vibration);
}
}
GetClientRect(cast(HWND)p->window_handle, &rect);
width = rect.right - rect.left;
height = rect.bottom - rect.top;
MoveWindow(cast(HWND)p->window_handle, x, y, width, height, false);
}
if (str[0] != '\0') {
SetWindowTextW(cast(HWND)p->window_handle, cast(wchar_t const
*)gb_utf8_to_ucs2(buffer, gb_size_of(buffer), str));
}
}
if (fullscreen_desktop) {
p->window_flags |= gbWindow_FullscreenDesktop;
} else {
p->window_flags |= gbWindow_Fullscreen;
}
}
} else {
style &= ~WS_POPUP;
style |= WS_OVERLAPPEDWINDOW | WS_CAPTION;
SetWindowLongW(handle, GWL_STYLE, style);
SetWindowPlacement(handle, &placement);
SetWindowPos(handle, 0, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
if (IsClipboardFormatAvailable(1/*CF_TEXT*/) &&
OpenClipboard(cast(HWND)p->window_handle)) {
HANDLE mem = GetClipboardData(1/*CF_TEXT*/);
if (mem) {
char *str = cast(char *)GlobalLock(mem);
if (str && str[0] != '\0') {
result = true;
}
GlobalUnlock(mem);
} else {
return false;
}
CloseClipboard();
}
return result;
}
EmptyClipboard();
if (!SetClipboardData(1/*CF_TEXT*/, mem)) {
return;
}
CloseClipboard();
}
}
if (IsClipboardFormatAvailable(1/*CF_TEXT*/) &&
OpenClipboard(cast(HWND)p->window_handle)) {
HANDLE mem = GetClipboardData(1/*CF_TEXT*/);
if (mem) {
char *str = cast(char *)GlobalLock(mem);
text = gb_alloc_str(a, str);
GlobalUnlock(mem);
} else {
return NULL;
}
CloseClipboard();
}
return text;
}
#elif defined(GB_SYSTEM_OSX)
#include <CoreGraphics/CoreGraphics.h>
#include <objc/objc.h>
#include <objc/message.h>
#include <objc/NSObjCRuntime.h>
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#else
typedef CGPoint NSPoint;
typedef CGSize NSSize;
typedef CGRect NSRect;
extern id NSApp;
extern id const NSDefaultRunLoopMode;
#endif
#if defined(ARC_AVAILABLE)
#error TODO(bill): This code should be compiled as C for now
#else
id poolAlloc =
objc_msgSend_id(cast(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("alloc"));
p->osx_autorelease_pool = objc_msgSend_id(poolAlloc,
sel_registerName("init"));
#endif
objc_msgSend_id(cast(id)objc_getClass("NSApplication"),
sel_registerName("sharedApplication"));
((void (*)(id, SEL, NSInteger))objc_msgSend)(NSApp,
sel_registerName("setActivationPolicy:"), 0);
appDelegateClass =
objc_allocateClassPair((Class)objc_getClass("NSObject"), "AppDelegate", 0);
resultAddProtoc = class_addProtocol(appDelegateClass,
objc_getProtocol("NSApplicationDelegate"));
assert(resultAddProtoc);
resultAddMethod = class_addMethod(appDelegateClass,
sel_registerName("applicationShouldTerminate:"),
cast(IMP)gb__osx_application_should_terminate, NSUIntegerEncoding "@:@");
assert(resultAddMethod);
dgAlloc = objc_msgSend_id(cast(id)appDelegateClass,
sel_registerName("alloc"));
dg = objc_msgSend_id(dgAlloc, sel_registerName("init"));
#ifndef ARC_AVAILABLE
objc_msgSend_void(dg, sel_registerName("autorelease"));
#endif
menubarAlloc = objc_msgSend_id(cast(id)objc_getClass("NSMenu"),
sel_registerName("alloc"));
menubar = objc_msgSend_id(menubarAlloc, sel_registerName("init"));
#ifndef ARC_AVAILABLE
objc_msgSend_void(menubar, sel_registerName("autorelease"));
#endif
appMenuItemAlloc = objc_msgSend_id(cast(id)objc_getClass("NSMenuItem"),
sel_registerName("alloc"));
appMenuItem = objc_msgSend_id(appMenuItemAlloc,
sel_registerName("init"));
#ifndef ARC_AVAILABLE
objc_msgSend_void(appMenuItem, sel_registerName("autorelease"));
#endif
objc_msgSend_void_id(menubar, sel_registerName("addItem:"),
appMenuItem);
((id (*)(id, SEL, id))objc_msgSend)(NSApp,
sel_registerName("setMainMenu:"), menubar);
appMenuAlloc = objc_msgSend_id(cast(id)objc_getClass("NSMenu"),
sel_registerName("alloc"));
appMenu = objc_msgSend_id(appMenuAlloc, sel_registerName("init"));
#ifndef ARC_AVAILABLE
objc_msgSend_void(appMenu, sel_registerName("autorelease"));
#endif
{
id processInfo =
objc_msgSend_id(cast(id)objc_getClass("NSProcessInfo"),
sel_registerName("processInfo"));
id appName = objc_msgSend_id(processInfo,
sel_registerName("processName"));
id quitTitlePrefixString =
objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"), "Quit ");
id quitTitle = ((id (*)(id, SEL, id))objc_msgSend)
(quitTitlePrefixString, sel_registerName("stringByAppendingString:"), appName);
id quitMenuItemKey =
objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"), "q");
id quitMenuItemAlloc =
objc_msgSend_id(cast(id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
id quitMenuItem = ((id (*)(id, SEL, id, SEL, id))objc_msgSend)
(quitMenuItemAlloc, sel_registerName("initWithTitle:action:keyEquivalent:"),
quitTitle, sel_registerName("terminate:"), quitMenuItemKey);
#ifndef ARC_AVAILABLE
objc_msgSend_void(quitMenuItem, sel_registerName("autorelease"));
#endif
objc_msgSend_void_id(appMenu, sel_registerName("addItem:"),
quitMenuItem);
objc_msgSend_void_id(appMenuItem,
sel_registerName("setSubmenu:"), appMenu);
}
}
{ // Init Window
NSRect rect = {{0, 0}, {cast(CGFloat)mode.width,
cast(CGFloat)mode.height}};
id windowAlloc, window, wdgAlloc, wdg, contentView, titleString;
Class WindowDelegateClass;
b32 resultAddProtoc, resultAddIvar, resultAddMethod;
windowAlloc = objc_msgSend_id(cast(id)objc_getClass("NSWindow"),
sel_registerName("alloc"));
window = ((id (*)(id, SEL, NSRect, NSUInteger, NSUInteger,
BOOL))objc_msgSend)(windowAlloc,
sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect, 15, 2, NO);
#ifndef ARC_AVAILABLE
objc_msgSend_void(window, sel_registerName("autorelease"));
#endif
// when we are not using ARC, than window will be added to autorelease
pool
// so if we close it by hand (pressing red button), we don't want it to
be released for us
// so it will be released by autorelease pool later
objc_msgSend_void_bool(window,
sel_registerName("setReleasedWhenClosed:"), NO);
WindowDelegateClass =
objc_allocateClassPair((Class)objc_getClass("NSObject"), "WindowDelegate", 0);
resultAddProtoc = class_addProtocol(WindowDelegateClass,
objc_getProtocol("NSWindowDelegate"));
GB_ASSERT(resultAddProtoc);
resultAddIvar = class_addIvar(WindowDelegateClass, "closed",
gb_size_of(NSUInteger), rint(log2(gb_size_of(NSUInteger))), NSUIntegerEncoding);
GB_ASSERT(resultAddIvar);
resultAddIvar = class_addIvar(WindowDelegateClass, "gbPlatform",
gb_size_of(void *), rint(log2(gb_size_of(void *))), "ˆv");
GB_ASSERT(resultAddIvar);
resultAddMethod = class_addMethod(WindowDelegateClass,
sel_registerName("windowWillClose:"), cast(IMP)gb__osx_window_will_close, "v@:@");
GB_ASSERT(resultAddMethod);
resultAddMethod = class_addMethod(WindowDelegateClass,
sel_registerName("windowDidBecomeKey:"), cast(IMP)gb__osx_window_did_become_key,
"v@:@");
GB_ASSERT(resultAddMethod);
wdgAlloc = objc_msgSend_id(cast(id)WindowDelegateClass,
sel_registerName("alloc"));
wdg = objc_msgSend_id(wdgAlloc, sel_registerName("init"));
#ifndef ARC_AVAILABLE
objc_msgSend_void(wdg, sel_registerName("autorelease"));
#endif
titleString =
objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"), window_title);
objc_msgSend_void_id(window, sel_registerName("setTitle:"),
titleString);
if (type == gbRenderer_Opengl) {
// TODO(bill): Make sure this works correctly
u32 opengl_hex_version = (p->opengl.major << 12) | (p-
>opengl.minor << 8);
u32 gl_attribs[] = {
8, 24, // NSOpenGLPFAColorSize, 24,
11, 8, // NSOpenGLPFAAlphaSize, 8,
5, // NSOpenGLPFADoubleBuffer,
73, // NSOpenGLPFAAccelerated,
//72, // NSOpenGLPFANoRecovery,
//55, 1, // NSOpenGLPFASampleBuffers, 1,
//56, 4, // NSOpenGLPFASamples, 4,
99, opengl_hex_version, // NSOpenGLPFAOpenGLProfile,
NSOpenGLProfileVersion3_2Core,
0
};
id pixel_format_alloc, pixel_format;
id opengl_context_alloc, opengl_context;
pixel_format_alloc =
objc_msgSend_id(cast(id)objc_getClass("NSOpenGLPixelFormat"),
sel_registerName("alloc"));
pixel_format = ((id (*)(id, SEL, const uint32_t*))objc_msgSend)
(pixel_format_alloc, sel_registerName("initWithAttributes:"), gl_attribs);
#ifndef ARC_AVAILABLE
objc_msgSend_void(pixel_format, sel_registerName("autorelease"));
#endif
opengl_context_alloc =
objc_msgSend_id(cast(id)objc_getClass("NSOpenGLContext"),
sel_registerName("alloc"));
opengl_context = ((id (*)(id, SEL, id, id))objc_msgSend)
(opengl_context_alloc, sel_registerName("initWithFormat:shareContext:"),
pixel_format, nil);
#ifndef ARC_AVAILABLE
objc_msgSend_void(opengl_context,
sel_registerName("autorelease"));
#endif
objc_msgSend_void_id(opengl_context,
sel_registerName("setView:"), contentView);
objc_msgSend_void_id(window,
sel_registerName("makeKeyAndOrderFront:"), window);
objc_msgSend_void_bool(window,
sel_registerName("setAcceptsMouseMovedEvents:"), YES);
p->window_handle = cast(void *)window;
p->opengl.context = cast(void *)opengl_context;
} else {
GB_PANIC("TODO(bill): Software rendering");
}
{
id blackColor = objc_msgSend_id(cast(id)objc_getClass("NSColor"),
sel_registerName("blackColor"));
objc_msgSend_void_id(window,
sel_registerName("setBackgroundColor:"), blackColor);
objc_msgSend_void_bool(NSApp,
sel_registerName("activateIgnoringOtherApps:"), YES);
}
object_setInstanceVariable(wdg, "gbPlatform", cast(void *)p);
p->is_initialized = true;
}
return true;
}
p->opengl.major = major;
p->opengl.minor = minor;
p->opengl.core = core;
p->opengl.compatible = compatible;
return gb__platform_init(p, window_title, gb_video_mode(width, height, 32),
gbRenderer_Opengl, window_flags);
}
} break;
// gb_printf("%u\n", keys.mask);
// gb_printf("%x\n", cast(u32)modifiers);
} break;
default: break;
}
window = cast(id)p->window_handle;
key_window = objc_msgSend_id(NSApp, sel_registerName("keyWindow"));
p->window_has_focus = key_window == window; // TODO(bill): Is this right
if (p->window_has_focus) {
isize i;
p->char_buffer_count = 0; // TODO(bill): Reset buffer count here or
else where?
{ // Handle Events
id distant_past = objc_msgSend_id(cast(id)objc_getClass("NSDate"),
sel_registerName("distantPast"));
id event = ((id (*)(id, SEL, NSUInteger, id, id, BOOL))objc_msgSend)
(NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
NSUIntegerMax, distant_past, NSDefaultRunLoopMode, YES);
gb__osx_on_cocoa_event(p, event, window);
}
if (p->window_has_focus) {
p->key_modifiers.control = p->keys[gbKey_Lcontrol] | p-
>keys[gbKey_Rcontrol];
p->key_modifiers.alt = p->keys[gbKey_Lalt] | p-
>keys[gbKey_Ralt];
p->key_modifiers.shift = p->keys[gbKey_Lshift] | p-
>keys[gbKey_Rshift];
}
{ // Window
NSRect frame = original_frame;
frame = ((NSRect (*)(id, SEL, NSRect))abi_objc_msgSend_stret)
(content_view, sel_registerName("convertRectToBacking:"), frame);
p->window_width = frame.size.width;
p->window_height = frame.size.height;
frame = ((NSRect (*)(id, SEL, NSRect))abi_objc_msgSend_stret)(window,
sel_registerName("convertRectToScreen:"), frame);
p->window_x = frame.origin.x;
p->window_y = frame.origin.y;
}
{ // Mouse
NSRect frame = original_frame;
NSPoint mouse_pos = ((NSPoint (*)(id, SEL))objc_msgSend)(window,
sel_registerName("mouseLocationOutsideOfEventStream"));
mouse_pos.x = gb_clamp(mouse_pos.x, 0, frame.size.width-1);
mouse_pos.y = gb_clamp(mouse_pos.y, 0, frame.size.height-1);
{
i32 x = mouse_pos.x;
i32 y = mouse_pos.y;
p->mouse_dx = x - p->mouse_x;
p->mouse_dy = y - p->mouse_y;
p->mouse_x = x;
p->mouse_y = y;
}
if (p->mouse_clip) {
b32 update = false;
i32 x = p->mouse_x;
i32 y = p->mouse_y;
if (p->mouse_x < 0) {
x = 0;
update = true;
} else if (p->mouse_y > p->window_height-1) {
y = p->window_height-1;
update = true;
}
if (p->mouse_y < 0) {
y = 0;
update = true;
} else if (p->mouse_x > p->window_width-1) {
x = p->window_width-1;
update = true;
}
if (update) {
gb_platform_set_mouse_position(p, x, y);
}
}
}
{ // TODO(bill): Controllers
{
f64 prev_time = p->curr_time;
f64 curr_time = gb_time_now();
p->dt_for_frame = curr_time - prev_time;
p->curr_time = curr_time;
}
}
objc_msgSend_void(cast(id)p->window_handle, sel_registerName("close"));
#if defined(ARC_AVAILABLE)
// TODO(bill): autorelease pool
#else
objc_msgSend_void(cast(id)p->osx_autorelease_pool,
sel_registerName("drain"));
#endif
}
title_string = objc_msgSend_id_char_const(cast(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"), buf);
objc_msgSend_void_id(cast(id)p->window_handle, sel_registerName("setTitle:"),
title_string);
}
return bits_per_pixel;
}
gbVideoMode gb_video_mode_get_desktop(void) {
CGDirectDisplayID display = CGMainDisplayID();
return gb_video_mode(CGDisplayPixelsWide(display),
CGDisplayPixelsHigh(display),
gb__osx_display_bits_per_pixel(display));
}
CFRelease(cg_modes);
#endif
if (!is_set) {
mode_count = gb_video_mode_get_fullscreen_modes(modes,
gb_count_of(modes));
is_set = true;
}
GB_COMPARE_PROC(gb_video_mode_cmp) {
gbVideoMode const *x = cast(gbVideoMode const *)a;
gbVideoMode const *y = cast(gbVideoMode const *)b;
if (x->bits_per_pixel == y->bits_per_pixel) {
if (x->width == y->width) {
return x->height < y->height ? -1 : x->height > y->height;
}
return x->width < y->width ? -1 : x->width > y->width;
}
return x->bits_per_pixel < y->bits_per_pixel ? -1 : +1;
}
GB_COMPARE_PROC(gb_video_mode_dsc_cmp) {
return gb_video_mode_cmp(b, a);
}
#endif // defined(GB_PLATFORM)
#if defined(GB_COMPILER_MSVC)
#pragma warning(pop)
#endif
#if defined(__cplusplus)
}
#endif
#endif // GB_IMPLEMENTATION