|
@@ -4,126 +4,8 @@
|
|
This is a single header file with a bunch of useful stuff
|
|
This is a single header file with a bunch of useful stuff
|
|
to replace the C/C++ standard library
|
|
to replace the C/C++ standard library
|
|
|
|
|
|
-===========================================================================
|
|
|
|
- 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"
|
|
|
|
-
|
|
|
|
- All other files should just #include "gb.h" without #define
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- If you want the platform layer, YOU MUST
|
|
|
|
-
|
|
|
|
- #define GB_PLATFORM
|
|
|
|
-
|
|
|
|
- BEFORE the include like this:
|
|
|
|
-
|
|
|
|
- #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
|
|
|
|
|
|
+ IMPORTANT NOTE: THIS IS A HEAVILY MODIFIED VERSION OF THE ORIGINAL
|
|
|
|
+ DO NO REPLACE IT WITH THE ORIGINAL
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
|
|
@@ -2179,374 +2061,6 @@ GB_DEF u64 gb_endian_swap64(u64 i);
|
|
|
|
|
|
GB_DEF isize gb_count_set_bits(u64 mask);
|
|
GB_DEF isize gb_count_set_bits(u64 mask);
|
|
|
|
|
|
-////////////////////////////////////////////////////////////////
|
|
|
|
-//
|
|
|
|
-// Platform Stuff
|
|
|
|
-//
|
|
|
|
-//
|
|
|
|
-
|
|
|
|
-#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
|
|
|
|
-
|
|
|
|
-// Window Support - Complete
|
|
|
|
-// OS X Support - Missing:
|
|
|
|
-// * Sofware framebuffer
|
|
|
|
-// * (show|hide) window
|
|
|
|
-// * show_cursor
|
|
|
|
-// * toggle (fullscreen|borderless)
|
|
|
|
-// * set window position
|
|
|
|
-// * Clipboard
|
|
|
|
-// * GameControllers
|
|
|
|
-// Linux Support - None
|
|
|
|
-// Other OS Support - None
|
|
|
|
-
|
|
|
|
-#ifndef GB_MAX_GAME_CONTROLLER_COUNT
|
|
|
|
-#define GB_MAX_GAME_CONTROLLER_COUNT 4
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-typedef enum gbKeyType {
|
|
|
|
- gbKey_Unknown = 0, // Unhandled key
|
|
|
|
-
|
|
|
|
- // 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__Pad = 128, // NOTE(bill): make sure ASCII is reserved
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
-/* TODO(bill): Change name? */
|
|
|
|
-typedef u8 gbKeyState;
|
|
|
|
-typedef enum gbKeyStateFlag {
|
|
|
|
- gbKeyState_Down = GB_BIT(0),
|
|
|
|
- gbKeyState_Pressed = GB_BIT(1),
|
|
|
|
- gbKeyState_Released = GB_BIT(2)
|
|
|
|
-} gbKeyStateFlag;
|
|
|
|
-
|
|
|
|
-GB_DEF void gb_key_state_update(gbKeyState *s, b32 is_down);
|
|
|
|
-
|
|
|
|
-typedef enum gbMouseButtonType {
|
|
|
|
- gbMouseButton_Left,
|
|
|
|
- gbMouseButton_Middle,
|
|
|
|
- gbMouseButton_Right,
|
|
|
|
- gbMouseButton_X1,
|
|
|
|
- gbMouseButton_X2,
|
|
|
|
-
|
|
|
|
- gbMouseButton_Count
|
|
|
|
-} gbMouseButtonType;
|
|
|
|
-
|
|
|
|
-typedef enum gbControllerAxisType {
|
|
|
|
- gbControllerAxis_LeftX,
|
|
|
|
- gbControllerAxis_LeftY,
|
|
|
|
- gbControllerAxis_RightX,
|
|
|
|
- gbControllerAxis_RightY,
|
|
|
|
- gbControllerAxis_LeftTrigger,
|
|
|
|
- gbControllerAxis_RightTrigger,
|
|
|
|
-
|
|
|
|
- gbControllerAxis_Count
|
|
|
|
-} gbControllerAxisType;
|
|
|
|
-
|
|
|
|
-typedef enum gbControllerButtonType {
|
|
|
|
- gbControllerButton_Up,
|
|
|
|
- gbControllerButton_Down,
|
|
|
|
- gbControllerButton_Left,
|
|
|
|
- gbControllerButton_Right,
|
|
|
|
- gbControllerButton_A,
|
|
|
|
- gbControllerButton_B,
|
|
|
|
- gbControllerButton_X,
|
|
|
|
- gbControllerButton_Y,
|
|
|
|
- gbControllerButton_LeftShoulder,
|
|
|
|
- gbControllerButton_RightShoulder,
|
|
|
|
- gbControllerButton_Back,
|
|
|
|
- gbControllerButton_Start,
|
|
|
|
- gbControllerButton_LeftThumb,
|
|
|
|
- gbControllerButton_RightThumb,
|
|
|
|
-
|
|
|
|
- gbControllerButton_Count
|
|
|
|
-} gbControllerButtonType;
|
|
|
|
-
|
|
|
|
-typedef struct gbGameController {
|
|
|
|
- b16 is_connected, is_analog;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
- #define GB_XINPUT_GET_STATE(name) unsigned long __stdcall name(unsigned long dwUserIndex, XINPUT_STATE *pState)
|
|
|
|
- typedef GB_XINPUT_GET_STATE(gbXInputGetStateProc);
|
|
|
|
-
|
|
|
|
- #define GB_XINPUT_SET_STATE(name) unsigned long __stdcall name(unsigned long dwUserIndex, XINPUT_VIBRATION *pVibration)
|
|
|
|
- typedef GB_XINPUT_SET_STATE(gbXInputSetStateProc);
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-typedef enum gbWindowFlag {
|
|
|
|
- gbWindow_Fullscreen = GB_BIT(0),
|
|
|
|
- gbWindow_Hidden = GB_BIT(1),
|
|
|
|
- gbWindow_Borderless = GB_BIT(2),
|
|
|
|
- gbWindow_Resizable = GB_BIT(3),
|
|
|
|
- gbWindow_Minimized = GB_BIT(4),
|
|
|
|
- gbWindow_Maximized = GB_BIT(5),
|
|
|
|
- gbWindow_FullscreenDesktop = gbWindow_Fullscreen | gbWindow_Borderless,
|
|
|
|
-} gbWindowFlag;
|
|
|
|
-
|
|
|
|
-typedef enum gbRendererType {
|
|
|
|
- gbRenderer_Opengl,
|
|
|
|
- gbRenderer_Software,
|
|
|
|
-
|
|
|
|
- gbRenderer_Count,
|
|
|
|
-} gbRendererType;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-#if defined(GB_SYSTEM_WINDOWS) && !defined(_WINDOWS_)
|
|
|
|
-typedef struct tagBITMAPINFOHEADER {
|
|
|
|
- unsigned long biSize;
|
|
|
|
- long biWidth;
|
|
|
|
- long biHeight;
|
|
|
|
- u16 biPlanes;
|
|
|
|
- u16 biBitCount;
|
|
|
|
- unsigned long biCompression;
|
|
|
|
- unsigned long biSizeImage;
|
|
|
|
- long biXPelsPerMeter;
|
|
|
|
- long biYPelsPerMeter;
|
|
|
|
- unsigned long biClrUsed;
|
|
|
|
- unsigned long biClrImportant;
|
|
|
|
-} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
|
|
|
|
-typedef struct tagRGBQUAD {
|
|
|
|
- u8 rgbBlue;
|
|
|
|
- u8 rgbGreen;
|
|
|
|
- u8 rgbRed;
|
|
|
|
- u8 rgbReserved;
|
|
|
|
-} RGBQUAD;
|
|
|
|
-typedef struct tagBITMAPINFO {
|
|
|
|
- BITMAPINFOHEADER bmiHeader;
|
|
|
|
- RGBQUAD bmiColors[1];
|
|
|
|
-} BITMAPINFO, *PBITMAPINFO;
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-typedef struct gbPlatform {
|
|
|
|
- b32 is_initialized;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
- // NOTE(bill): Software rendering
|
|
|
|
- struct {
|
|
|
|
-#if defined(GB_SYSTEM_WINDOWS)
|
|
|
|
- BITMAPINFO win32_bmi;
|
|
|
|
-#endif
|
|
|
|
- void * memory;
|
|
|
|
- isize memory_size;
|
|
|
|
- i32 pitch;
|
|
|
|
- i32 bits_per_pixel;
|
|
|
|
- } sw_framebuffer;
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-typedef struct gbVideoMode {
|
|
|
|
- i32 width, height;
|
|
|
|
- i32 bits_per_pixel;
|
|
|
|
-} gbVideoMode;
|
|
|
|
-
|
|
|
|
-GB_DEF gbVideoMode gb_video_mode (i32 width, i32 height, i32 bits_per_pixel);
|
|
|
|
-GB_DEF b32 gb_video_mode_is_valid (gbVideoMode mode);
|
|
|
|
-GB_DEF gbVideoMode gb_video_mode_get_desktop (void);
|
|
|
|
-GB_DEF isize gb_video_mode_get_fullscreen_modes(gbVideoMode *modes, isize max_mode_count); // NOTE(bill): returns mode count
|
|
|
|
-GB_DEF GB_COMPARE_PROC(gb_video_mode_cmp); // NOTE(bill): Sort smallest to largest (Ascending)
|
|
|
|
-GB_DEF GB_COMPARE_PROC(gb_video_mode_dsc_cmp); // NOTE(bill): Sort largest to smallest (Descending)
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-// NOTE(bill): Software rendering
|
|
|
|
-GB_DEF b32 gb_platform_init_with_software (gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags);
|
|
|
|
-// NOTE(bill): OpenGL Rendering
|
|
|
|
-GB_DEF b32 gb_platform_init_with_opengl (gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags, i32 major, i32 minor, b32 core, b32 compatible);
|
|
|
|
-GB_DEF void gb_platform_update (gbPlatform *p);
|
|
|
|
-GB_DEF void gb_platform_display (gbPlatform *p);
|
|
|
|
-GB_DEF void gb_platform_destroy (gbPlatform *p);
|
|
|
|
-GB_DEF void gb_platform_show_cursor (gbPlatform *p, b32 show);
|
|
|
|
-GB_DEF void gb_platform_set_mouse_position (gbPlatform *p, i32 x, i32 y);
|
|
|
|
-GB_DEF void gb_platform_set_controller_vibration (gbPlatform *p, isize index, f32 left_motor, f32 right_motor);
|
|
|
|
-GB_DEF b32 gb_platform_has_clipboard_text (gbPlatform *p);
|
|
|
|
-GB_DEF void gb_platform_set_clipboard_text (gbPlatform *p, char const *str);
|
|
|
|
-GB_DEF char *gb_platform_get_clipboard_text (gbPlatform *p, gbAllocator a);
|
|
|
|
-GB_DEF void gb_platform_set_window_position (gbPlatform *p, i32 x, i32 y);
|
|
|
|
-GB_DEF void gb_platform_set_window_title (gbPlatform *p, char const *title, ...) GB_PRINTF_ARGS(2);
|
|
|
|
-GB_DEF void gb_platform_toggle_fullscreen (gbPlatform *p, b32 fullscreen_desktop);
|
|
|
|
-GB_DEF void gb_platform_toggle_borderless (gbPlatform *p);
|
|
|
|
-GB_DEF void gb_platform_make_opengl_context_current(gbPlatform *p);
|
|
|
|
-GB_DEF void gb_platform_show_window (gbPlatform *p);
|
|
|
|
-GB_DEF void gb_platform_hide_window (gbPlatform *p);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-#endif // GB_PLATFORM
|
|
|
|
-
|
|
|
|
#if defined(__cplusplus)
|
|
#if defined(__cplusplus)
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
@@ -9311,1787 +8825,6 @@ gb_inline isize gb_count_set_bits(u64 mask) {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-////////////////////////////////////////////////////////////////
|
|
|
|
-//
|
|
|
|
-// Platform
|
|
|
|
-//
|
|
|
|
-//
|
|
|
|
-
|
|
|
|
-#if defined(GB_PLATFORM)
|
|
|
|
-
|
|
|
|
-gb_inline void gb_key_state_update(gbKeyState *s, b32 is_down) {
|
|
|
|
- b32 was_down = (*s & gbKeyState_Down) != 0;
|
|
|
|
- is_down = is_down != 0; // NOTE(bill): Make sure it's a boolean
|
|
|
|
- GB_MASK_SET(*s, is_down, gbKeyState_Down);
|
|
|
|
- GB_MASK_SET(*s, !was_down && is_down, gbKeyState_Pressed);
|
|
|
|
- GB_MASK_SET(*s, was_down && !is_down, gbKeyState_Released);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#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;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-gb_internal gb_inline f32 gb__process_xinput_stick_value(i16 value, i16 dead_zone_threshold) {
|
|
|
|
- f32 result = 0;
|
|
|
|
-
|
|
|
|
- if (value < -dead_zone_threshold) {
|
|
|
|
- result = cast(f32) (value + dead_zone_threshold) / (32768.0f - dead_zone_threshold);
|
|
|
|
- } else if (value > dead_zone_threshold) {
|
|
|
|
- result = cast(f32) (value - dead_zone_threshold) / (32767.0f - dead_zone_threshold);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return result;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_internal void gb__platform_resize_dib_section(gbPlatform *p, i32 width, i32 height) {
|
|
|
|
- if ((p->renderer_type == gbRenderer_Software) &&
|
|
|
|
- !(p->window_width == width && p->window_height == height)) {
|
|
|
|
- BITMAPINFO bmi = {0};
|
|
|
|
-
|
|
|
|
- if (width == 0 || height == 0) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- p->window_width = width;
|
|
|
|
- p->window_height = height;
|
|
|
|
-
|
|
|
|
- // TODO(bill): Is this slow to get the desktop mode everytime?
|
|
|
|
- p->sw_framebuffer.bits_per_pixel = gb_video_mode_get_desktop().bits_per_pixel;
|
|
|
|
- p->sw_framebuffer.pitch = (p->sw_framebuffer.bits_per_pixel * width / 8);
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-gb_internal gbKeyType gb__win32_from_vk(unsigned int key) {
|
|
|
|
- // NOTE(bill): Letters and numbers are defined the same for VK_* and GB_*
|
|
|
|
- if (key >= 'A' && key < 'Z') return cast(gbKeyType)key;
|
|
|
|
- if (key >= '0' && key < '9') return cast(gbKeyType)key;
|
|
|
|
- switch (key) {
|
|
|
|
- case VK_ESCAPE: return gbKey_Escape;
|
|
|
|
-
|
|
|
|
- case VK_LCONTROL: return gbKey_Lcontrol;
|
|
|
|
- case VK_LSHIFT: return gbKey_Lshift;
|
|
|
|
- case VK_LMENU: return gbKey_Lalt;
|
|
|
|
- case VK_LWIN: return gbKey_Lsystem;
|
|
|
|
- case VK_RCONTROL: return gbKey_Rcontrol;
|
|
|
|
- case VK_RSHIFT: return gbKey_Rshift;
|
|
|
|
- case VK_RMENU: return gbKey_Ralt;
|
|
|
|
- case VK_RWIN: return gbKey_Rsystem;
|
|
|
|
- case VK_MENU: return gbKey_Menu;
|
|
|
|
-
|
|
|
|
- case VK_OEM_4: return gbKey_Lbracket;
|
|
|
|
- case VK_OEM_6: return gbKey_Rbracket;
|
|
|
|
- case VK_OEM_1: return gbKey_Semicolon;
|
|
|
|
- case VK_OEM_COMMA: return gbKey_Comma;
|
|
|
|
- case VK_OEM_PERIOD: return gbKey_Period;
|
|
|
|
- case VK_OEM_7: return gbKey_Quote;
|
|
|
|
- case VK_OEM_2: return gbKey_Slash;
|
|
|
|
- case VK_OEM_5: return gbKey_Backslash;
|
|
|
|
- case VK_OEM_3: return gbKey_Grave;
|
|
|
|
- case VK_OEM_PLUS: return gbKey_Equals;
|
|
|
|
- case VK_OEM_MINUS: return gbKey_Minus;
|
|
|
|
-
|
|
|
|
- case VK_SPACE: return gbKey_Space;
|
|
|
|
- case VK_RETURN: return gbKey_Return;
|
|
|
|
- case VK_BACK: return gbKey_Backspace;
|
|
|
|
- case VK_TAB: return gbKey_Tab;
|
|
|
|
-
|
|
|
|
- case VK_PRIOR: return gbKey_Pageup;
|
|
|
|
- case VK_NEXT: return gbKey_Pagedown;
|
|
|
|
- case VK_END: return gbKey_End;
|
|
|
|
- case VK_HOME: return gbKey_Home;
|
|
|
|
- case VK_INSERT: return gbKey_Insert;
|
|
|
|
- case VK_DELETE: return gbKey_Delete;
|
|
|
|
-
|
|
|
|
- case VK_ADD: return gbKey_Plus;
|
|
|
|
- case VK_SUBTRACT: return gbKey_Subtract;
|
|
|
|
- case VK_MULTIPLY: return gbKey_Multiply;
|
|
|
|
- case VK_DIVIDE: return gbKey_Divide;
|
|
|
|
-
|
|
|
|
- case VK_LEFT: return gbKey_Left;
|
|
|
|
- case VK_RIGHT: return gbKey_Right;
|
|
|
|
- case VK_UP: return gbKey_Up;
|
|
|
|
- case VK_DOWN: return gbKey_Down;
|
|
|
|
-
|
|
|
|
- case VK_NUMPAD0: return gbKey_Numpad0;
|
|
|
|
- case VK_NUMPAD1: return gbKey_Numpad1;
|
|
|
|
- case VK_NUMPAD2: return gbKey_Numpad2;
|
|
|
|
- case VK_NUMPAD3: return gbKey_Numpad3;
|
|
|
|
- case VK_NUMPAD4: return gbKey_Numpad4;
|
|
|
|
- case VK_NUMPAD5: return gbKey_Numpad5;
|
|
|
|
- case VK_NUMPAD6: return gbKey_Numpad6;
|
|
|
|
- case VK_NUMPAD7: return gbKey_Numpad7;
|
|
|
|
- case VK_NUMPAD8: return gbKey_Numpad8;
|
|
|
|
- case VK_NUMPAD9: return gbKey_Numpad9;
|
|
|
|
- case VK_SEPARATOR: return gbKey_NumpadEnter;
|
|
|
|
- case VK_DECIMAL: return gbKey_NumpadDot;
|
|
|
|
-
|
|
|
|
- case VK_F1: return gbKey_F1;
|
|
|
|
- case VK_F2: return gbKey_F2;
|
|
|
|
- case VK_F3: return gbKey_F3;
|
|
|
|
- case VK_F4: return gbKey_F4;
|
|
|
|
- case VK_F5: return gbKey_F5;
|
|
|
|
- case VK_F6: return gbKey_F6;
|
|
|
|
- case VK_F7: return gbKey_F7;
|
|
|
|
- case VK_F8: return gbKey_F8;
|
|
|
|
- case VK_F9: return gbKey_F9;
|
|
|
|
- case VK_F10: return gbKey_F10;
|
|
|
|
- case VK_F11: return gbKey_F11;
|
|
|
|
- case VK_F12: return gbKey_F12;
|
|
|
|
- case VK_F13: return gbKey_F13;
|
|
|
|
- case VK_F14: return gbKey_F14;
|
|
|
|
- case VK_F15: return gbKey_F15;
|
|
|
|
-
|
|
|
|
- case VK_PAUSE: return gbKey_Pause;
|
|
|
|
- }
|
|
|
|
- return gbKey_Unknown;
|
|
|
|
-}
|
|
|
|
-LRESULT CALLBACK gb__win32_window_callback(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|
|
|
- // NOTE(bill): Silly callbacks
|
|
|
|
- gbPlatform *platform = cast(gbPlatform *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
|
|
|
|
- b32 window_has_focus = (platform != NULL) && platform->window_has_focus;
|
|
|
|
-
|
|
|
|
- if (msg == WM_CREATE) { // NOTE(bill): Doesn't need the platform
|
|
|
|
- // NOTE(bill): https://msdn.microsoft.com/en-us/library/windows/desktop/ms645536(v=vs.85).aspx
|
|
|
|
- RAWINPUTDEVICE rid[2] = {0};
|
|
|
|
-
|
|
|
|
- // 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 (!GetRawInputData(cast(HRAWINPUT)lParam, RID_INPUT, &raw, &size, gb_size_of(RAWINPUTHEADER))) {
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- switch (raw.header.dwType) {
|
|
|
|
- case RIM_TYPEKEYBOARD: {
|
|
|
|
- // NOTE(bill): Many thanks to https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
|
|
|
|
- // for the
|
|
|
|
- RAWKEYBOARD *raw_kb = &raw.data.keyboard;
|
|
|
|
- unsigned int vk = raw_kb->VKey;
|
|
|
|
- unsigned int scan_code = raw_kb->MakeCode;
|
|
|
|
- unsigned int flags = raw_kb->Flags;
|
|
|
|
- // NOTE(bill): e0 and e1 are escape sequences used for certain special keys, such as PRINT and PAUSE/BREAK.
|
|
|
|
- // NOTE(bill): http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
|
|
|
|
- b32 is_e0 = (flags & RI_KEY_E0) != 0;
|
|
|
|
- b32 is_e1 = (flags & RI_KEY_E1) != 0;
|
|
|
|
- b32 is_up = (flags & RI_KEY_BREAK) != 0;
|
|
|
|
- b32 is_down = !is_up;
|
|
|
|
-
|
|
|
|
- // TODO(bill): Should I handle scan codes?
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
- case VK_RETURN: if (is_e0) vk = VK_SEPARATOR; break; // NOTE(bill): Numpad return
|
|
|
|
- case VK_DELETE: if (!is_e0) vk = VK_DECIMAL; break; // NOTE(bill): Numpad dot
|
|
|
|
- case VK_INSERT: if (!is_e0) vk = VK_NUMPAD0; break;
|
|
|
|
- case VK_HOME: if (!is_e0) vk = VK_NUMPAD7; break;
|
|
|
|
- case VK_END: if (!is_e0) vk = VK_NUMPAD1; break;
|
|
|
|
- case VK_PRIOR: if (!is_e0) vk = VK_NUMPAD9; break;
|
|
|
|
- case VK_NEXT: if (!is_e0) vk = VK_NUMPAD3; break;
|
|
|
|
-
|
|
|
|
- // NOTE(bill): The standard arrow keys will always have their e0 bit set, but the
|
|
|
|
- // corresponding keys on the NUMPAD will not.
|
|
|
|
- case VK_LEFT: if (!is_e0) vk = VK_NUMPAD4; break;
|
|
|
|
- case VK_RIGHT: if (!is_e0) vk = VK_NUMPAD6; break;
|
|
|
|
- case VK_UP: if (!is_e0) vk = VK_NUMPAD8; break;
|
|
|
|
- case VK_DOWN: if (!is_e0) vk = VK_NUMPAD2; break;
|
|
|
|
-
|
|
|
|
- // NUMPAD 5 doesn't have its e0 bit set
|
|
|
|
- case VK_CLEAR: if (!is_e0) vk = VK_NUMPAD5; break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // NOTE(bill): Set appropriate key state flags
|
|
|
|
- gb_key_state_update(&platform->keys[gb__win32_from_vk(vk)], is_down);
|
|
|
|
-
|
|
|
|
- } 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;
|
|
|
|
-
|
|
|
|
- if (flags & RI_MOUSE_WHEEL) {
|
|
|
|
- platform->mouse_wheel_delta = cast(i16)raw_mouse->usButtonData;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- platform->mouse_raw_dx = dx;
|
|
|
|
- platform->mouse_raw_dy = dy;
|
|
|
|
- } break;
|
|
|
|
- }
|
|
|
|
- } break;
|
|
|
|
-
|
|
|
|
- default: break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return DefWindowProcW(hWnd, msg, wParam, lParam);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-typedef void *wglCreateContextAttribsARB_Proc(void *hDC, void *hshareContext, int const *attribList);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-b32 gb__platform_init(gbPlatform *p, char const *window_title, gbVideoMode mode, gbRendererType type, u32 window_flags) {
|
|
|
|
- WNDCLASSEXW wc = {gb_size_of(WNDCLASSEXW)};
|
|
|
|
- DWORD ex_style = 0, style = 0;
|
|
|
|
- RECT wr;
|
|
|
|
- u16 title_buffer[256] = {0}; // TODO(bill): gb_local_persist this?
|
|
|
|
-
|
|
|
|
- wc.style = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC
|
|
|
|
- wc.lpfnWndProc = gb__win32_window_callback;
|
|
|
|
- wc.hbrBackground = cast(HBRUSH)GetStockObject(0/*WHITE_BRUSH*/);
|
|
|
|
- wc.lpszMenuName = NULL;
|
|
|
|
- wc.lpszClassName = L"gb-win32-wndclass"; // TODO(bill): Is this enough?
|
|
|
|
- wc.hInstance = GetModuleHandleW(NULL);
|
|
|
|
-
|
|
|
|
- if (RegisterClassExW(&wc) == 0) {
|
|
|
|
- MessageBoxW(NULL, L"Failed to register the window class", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((window_flags & gbWindow_Fullscreen) && !(window_flags & gbWindow_Borderless)) {
|
|
|
|
- DEVMODEW screen_settings = {gb_size_of(DEVMODEW)};
|
|
|
|
- screen_settings.dmPelsWidth = mode.width;
|
|
|
|
- screen_settings.dmPelsHeight = mode.height;
|
|
|
|
- screen_settings.dmBitsPerPel = mode.bits_per_pixel;
|
|
|
|
- screen_settings.dmFields = DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
|
|
|
|
- // style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
|
|
|
|
-
|
|
|
|
- style |= WS_VISIBLE;
|
|
|
|
-
|
|
|
|
- if (window_flags & gbWindow_Hidden) style &= ~WS_VISIBLE;
|
|
|
|
- if (window_flags & gbWindow_Resizable) style |= WS_THICKFRAME | WS_MAXIMIZEBOX;
|
|
|
|
- if (window_flags & gbWindow_Maximized) style |= WS_MAXIMIZE;
|
|
|
|
- if (window_flags & gbWindow_Minimized) style |= WS_MINIMIZE;
|
|
|
|
-
|
|
|
|
- // NOTE(bill): Completely ignore the given mode and just change it
|
|
|
|
- if (window_flags & gbWindow_FullscreenDesktop) {
|
|
|
|
- mode = gb_video_mode_get_desktop();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((window_flags & gbWindow_Fullscreen) || (window_flags & gbWindow_Borderless)) {
|
|
|
|
- style |= WS_POPUP;
|
|
|
|
- } else {
|
|
|
|
- style |= WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
- PIXELFORMATDESCRIPTOR pfd = {gb_size_of(PIXELFORMATDESCRIPTOR)};
|
|
|
|
- pfd.nVersion = 1;
|
|
|
|
- pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
|
|
- pfd.iPixelType = PFD_TYPE_RGBA;
|
|
|
|
- pfd.cColorBits = 32;
|
|
|
|
- pfd.cAlphaBits = 8;
|
|
|
|
- pfd.cDepthBits = 24;
|
|
|
|
- pfd.cStencilBits = 8;
|
|
|
|
- pfd.iLayerType = PFD_MAIN_PLANE;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
- if (!xinput_library) xinput_library = gb_dll_load("xinput9_1_0.dll");
|
|
|
|
- if (!xinput_library) xinput_library = gb_dll_load("xinput1_3.dll");
|
|
|
|
- if (!xinput_library) {
|
|
|
|
- // TODO(bill): Proper Diagnostic
|
|
|
|
- gb_printf_err("XInput could not be loaded. Controllers will not work!\n");
|
|
|
|
- } else {
|
|
|
|
- p->xinput.get_state = cast(gbXInputGetStateProc *)gb_dll_proc_address(xinput_library, "XInputGetState");
|
|
|
|
- p->xinput.set_state = cast(gbXInputSetStateProc *)gb_dll_proc_address(xinput_library, "XInputSetState");
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Init keys
|
|
|
|
- gb_zero_array(p->keys, gb_count_of(p->keys));
|
|
|
|
-
|
|
|
|
- p->is_initialized = true;
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_inline b32 gb_platform_init_with_software(gbPlatform *p, char const *window_title,
|
|
|
|
- i32 width, i32 height, u32 window_flags) {
|
|
|
|
- gbVideoMode mode;
|
|
|
|
- mode.width = width;
|
|
|
|
- mode.height = height;
|
|
|
|
- mode.bits_per_pixel = 32;
|
|
|
|
- return gb__platform_init(p, window_title, mode, gbRenderer_Software, window_flags);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_inline b32 gb_platform_init_with_opengl(gbPlatform *p, char const *window_title,
|
|
|
|
- i32 width, i32 height, u32 window_flags, i32 major, i32 minor, b32 core, b32 compatible) {
|
|
|
|
- gbVideoMode mode;
|
|
|
|
- mode.width = width;
|
|
|
|
- mode.height = height;
|
|
|
|
- mode.bits_per_pixel = 32;
|
|
|
|
- p->opengl.major = major;
|
|
|
|
- p->opengl.minor = minor;
|
|
|
|
- p->opengl.core = cast(b16)core;
|
|
|
|
- p->opengl.compatible = cast(b16)compatible;
|
|
|
|
- return gb__platform_init(p, window_title, mode, gbRenderer_Opengl, window_flags);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#ifndef _XINPUT_H_
|
|
|
|
-typedef struct _XINPUT_GAMEPAD {
|
|
|
|
- u16 wButtons;
|
|
|
|
- u8 bLeftTrigger;
|
|
|
|
- u8 bRightTrigger;
|
|
|
|
- u16 sThumbLX;
|
|
|
|
- u16 sThumbLY;
|
|
|
|
- u16 sThumbRX;
|
|
|
|
- u16 sThumbRY;
|
|
|
|
-} XINPUT_GAMEPAD;
|
|
|
|
-
|
|
|
|
-typedef struct _XINPUT_STATE {
|
|
|
|
- DWORD dwPacketNumber;
|
|
|
|
- XINPUT_GAMEPAD Gamepad;
|
|
|
|
-} XINPUT_STATE;
|
|
|
|
-
|
|
|
|
-typedef struct _XINPUT_VIBRATION {
|
|
|
|
- u16 wLeftMotorSpeed;
|
|
|
|
- u16 wRightMotorSpeed;
|
|
|
|
-} XINPUT_VIBRATION;
|
|
|
|
-
|
|
|
|
-#define XINPUT_GAMEPAD_DPAD_UP 0x00000001
|
|
|
|
-#define XINPUT_GAMEPAD_DPAD_DOWN 0x00000002
|
|
|
|
-#define XINPUT_GAMEPAD_DPAD_LEFT 0x00000004
|
|
|
|
-#define XINPUT_GAMEPAD_DPAD_RIGHT 0x00000008
|
|
|
|
-#define XINPUT_GAMEPAD_START 0x00000010
|
|
|
|
-#define XINPUT_GAMEPAD_BACK 0x00000020
|
|
|
|
-#define XINPUT_GAMEPAD_LEFT_THUMB 0x00000040
|
|
|
|
-#define XINPUT_GAMEPAD_RIGHT_THUMB 0x00000080
|
|
|
|
-#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
|
|
|
|
-#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
|
|
|
|
-#define XINPUT_GAMEPAD_A 0x1000
|
|
|
|
-#define XINPUT_GAMEPAD_B 0x2000
|
|
|
|
-#define XINPUT_GAMEPAD_X 0x4000
|
|
|
|
-#define XINPUT_GAMEPAD_Y 0x8000
|
|
|
|
-#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
|
|
|
|
-#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
|
|
|
|
-#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-#ifndef XUSER_MAX_COUNT
|
|
|
|
-#define XUSER_MAX_COUNT 4
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-void gb_platform_update(gbPlatform *p) {
|
|
|
|
- isize i;
|
|
|
|
-
|
|
|
|
- { // NOTE(bill): Set window state
|
|
|
|
- // TODO(bill): Should this be moved to gb__win32_window_callback ?
|
|
|
|
- RECT window_rect;
|
|
|
|
- i32 x, y, w, h;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-
|
|
|
|
- if ((p->window_width != w) || (p->window_height != h)) {
|
|
|
|
- if (p->renderer_type == gbRenderer_Software) {
|
|
|
|
- gb__platform_resize_dib_section(p, w, h);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
-
|
|
|
|
- p->window_has_focus = GetFocus() == cast(HWND)p->window_handle;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- { // NOTE(bill): Set mouse position
|
|
|
|
- POINT mouse_pos;
|
|
|
|
- DWORD win_button_id[gbMouseButton_Count] = {
|
|
|
|
- VK_LBUTTON,
|
|
|
|
- VK_MBUTTON,
|
|
|
|
- VK_RBUTTON,
|
|
|
|
- VK_XBUTTON1,
|
|
|
|
- VK_XBUTTON2,
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- // NOTE(bill): This needs to be GetAsyncKeyState as RAWMOUSE doesn't aways work for some odd reason
|
|
|
|
- // TODO(bill): Try and get RAWMOUSE to work for key presses
|
|
|
|
- for (i = 0; i < gbMouseButton_Count; i++) {
|
|
|
|
- gb_key_state_update(p->mouse_buttons+i, GetAsyncKeyState(win_button_id[i]) < 0);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // NOTE(bill): Set Key/Button states
|
|
|
|
- if (p->window_has_focus) {
|
|
|
|
- p->char_buffer_count = 0; // TODO(bill): Reset buffer count here or else where?
|
|
|
|
-
|
|
|
|
- // NOTE(bill): Need to update as the keys only get updates on events
|
|
|
|
- for (i = 0; i < gbKey_Count; i++) {
|
|
|
|
- b32 is_down = (p->keys[i] & gbKeyState_Down) != 0;
|
|
|
|
- gb_key_state_update(&p->keys[i], is_down);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (i = 0; i < max_controller_count; i++) {
|
|
|
|
- gbGameController *controller = &p->game_controllers[i];
|
|
|
|
- XINPUT_STATE controller_state = {0};
|
|
|
|
- if (p->xinput.get_state(cast(DWORD)i, &controller_state) != 0) {
|
|
|
|
- // NOTE(bill): The controller is not available
|
|
|
|
- controller->is_connected = false;
|
|
|
|
- } else {
|
|
|
|
- // NOTE(bill): This controller is plugged in
|
|
|
|
- // TODO(bill): See if ControllerState.dwPacketNumber increments too rapidly
|
|
|
|
- XINPUT_GAMEPAD *pad = &controller_state.Gamepad;
|
|
|
|
-
|
|
|
|
- controller->is_connected = true;
|
|
|
|
-
|
|
|
|
- // TODO(bill): This is a square deadzone, check XInput to verify that the deadzone is "round" and do round deadzone processing.
|
|
|
|
- controller->axes[gbControllerAxis_LeftX] = gb__process_xinput_stick_value(pad->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
|
|
|
|
- controller->axes[gbControllerAxis_LeftY] = gb__process_xinput_stick_value(pad->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
|
|
|
|
- controller->axes[gbControllerAxis_RightX] = gb__process_xinput_stick_value(pad->sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
|
|
|
|
- controller->axes[gbControllerAxis_RightY] = gb__process_xinput_stick_value(pad->sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #define GB__PROCESS_DIGITAL_BUTTON(button_type, xinput_button) \
|
|
|
|
- gb_key_state_update(&controller->buttons[button_type], (pad->wButtons & xinput_button) == xinput_button)
|
|
|
|
-
|
|
|
|
- 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
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- { // NOTE(bill): Process pending messages
|
|
|
|
- MSG message;
|
|
|
|
- for (;;) {
|
|
|
|
- BOOL is_okay = PeekMessageW(&message, 0, 0, 0, PM_REMOVE);
|
|
|
|
- if (!is_okay) break;
|
|
|
|
-
|
|
|
|
- switch (message.message) {
|
|
|
|
- case WM_QUIT:
|
|
|
|
- p->quit_requested = true;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- TranslateMessage(&message);
|
|
|
|
- DispatchMessageW(&message);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_display(gbPlatform *p) {
|
|
|
|
- if (p->renderer_type == gbRenderer_Opengl) {
|
|
|
|
- SwapBuffers(cast(HDC)p->win32_dc);
|
|
|
|
- } else if (p->renderer_type == gbRenderer_Software) {
|
|
|
|
- StretchDIBits(cast(HDC)p->win32_dc,
|
|
|
|
- 0, 0, p->window_width, p->window_height,
|
|
|
|
- 0, 0, p->window_width, p->window_height,
|
|
|
|
- p->sw_framebuffer.memory,
|
|
|
|
- &p->sw_framebuffer.win32_bmi,
|
|
|
|
- DIB_RGB_COLORS, SRCCOPY);
|
|
|
|
- } else {
|
|
|
|
- GB_PANIC("Invalid window rendering type");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-void gb_platform_destroy(gbPlatform *p) {
|
|
|
|
- if (p->renderer_type == gbRenderer_Opengl) {
|
|
|
|
- wglDeleteContext(cast(HGLRC)p->opengl.context);
|
|
|
|
- } else if (p->renderer_type == gbRenderer_Software) {
|
|
|
|
- gb_vm_free(gb_virtual_memory(p->sw_framebuffer.memory, p->sw_framebuffer.memory_size));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- DestroyWindow(cast(HWND)p->window_handle);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_show_cursor(gbPlatform *p, b32 show) {
|
|
|
|
- gb_unused(p);
|
|
|
|
- ShowCursor(show);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_set_mouse_position(gbPlatform *p, i32 x, i32 y) {
|
|
|
|
- POINT point;
|
|
|
|
- point.x = cast(LONG)x;
|
|
|
|
- point.y = cast(LONG)(p->window_height-1 - y);
|
|
|
|
- ClientToScreen(cast(HWND)p->window_handle, &point);
|
|
|
|
- SetCursorPos(point.x, point.y);
|
|
|
|
-
|
|
|
|
- p->mouse_x = point.x;
|
|
|
|
- p->mouse_y = p->window_height-1 - point.y;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-void gb_platform_set_controller_vibration(gbPlatform *p, isize index, f32 left_motor, f32 right_motor) {
|
|
|
|
- if (gb_is_between(index, 0, GB_MAX_GAME_CONTROLLER_COUNT-1)) {
|
|
|
|
- XINPUT_VIBRATION vibration = {0};
|
|
|
|
- left_motor = gb_clamp01(left_motor);
|
|
|
|
- right_motor = gb_clamp01(right_motor);
|
|
|
|
- vibration.wLeftMotorSpeed = cast(WORD)(65535 * left_motor);
|
|
|
|
- vibration.wRightMotorSpeed = cast(WORD)(65535 * right_motor);
|
|
|
|
-
|
|
|
|
- p->xinput.set_state(cast(DWORD)index, &vibration);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-void gb_platform_set_window_position(gbPlatform *p, i32 x, i32 y) {
|
|
|
|
- RECT rect;
|
|
|
|
- i32 width, height;
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_set_window_title(gbPlatform *p, char const *title, ...) {
|
|
|
|
- u16 buffer[256] = {0};
|
|
|
|
- char str[512] = {0};
|
|
|
|
- va_list va;
|
|
|
|
- va_start(va, title);
|
|
|
|
- gb_snprintf_va(str, gb_size_of(str), title, va);
|
|
|
|
- va_end(va);
|
|
|
|
-
|
|
|
|
- if (str[0] != '\0') {
|
|
|
|
- SetWindowTextW(cast(HWND)p->window_handle, cast(wchar_t const *)gb_utf8_to_ucs2(buffer, gb_size_of(buffer), str));
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_toggle_fullscreen(gbPlatform *p, b32 fullscreen_desktop) {
|
|
|
|
- // NOTE(bill): From the man himself, Raymond Chen! (Modified for my need.)
|
|
|
|
- HWND handle = cast(HWND)p->window_handle;
|
|
|
|
- DWORD style = cast(DWORD)GetWindowLongW(handle, GWL_STYLE);
|
|
|
|
- WINDOWPLACEMENT placement;
|
|
|
|
-
|
|
|
|
- if (style & WS_OVERLAPPEDWINDOW) {
|
|
|
|
- MONITORINFO monitor_info = {gb_size_of(monitor_info)};
|
|
|
|
- if (GetWindowPlacement(handle, &placement) &&
|
|
|
|
- GetMonitorInfoW(MonitorFromWindow(handle, 1), &monitor_info)) {
|
|
|
|
- style &= ~WS_OVERLAPPEDWINDOW;
|
|
|
|
- if (fullscreen_desktop) {
|
|
|
|
- style &= ~WS_CAPTION;
|
|
|
|
- style |= WS_POPUP;
|
|
|
|
- }
|
|
|
|
- SetWindowLongW(handle, GWL_STYLE, style);
|
|
|
|
- SetWindowPos(handle, HWND_TOP,
|
|
|
|
- monitor_info.rcMonitor.left, monitor_info.rcMonitor.top,
|
|
|
|
- monitor_info.rcMonitor.right - monitor_info.rcMonitor.left,
|
|
|
|
- monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top,
|
|
|
|
- SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
-
|
|
|
|
- p->window_flags &= ~gbWindow_Fullscreen;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_toggle_borderless(gbPlatform *p) {
|
|
|
|
- HWND handle = cast(HWND)p->window_handle;
|
|
|
|
- DWORD style = GetWindowLongW(handle, GWL_STYLE);
|
|
|
|
- b32 is_borderless = (style & WS_POPUP) != 0;
|
|
|
|
-
|
|
|
|
- GB_MASK_SET(style, is_borderless, WS_OVERLAPPEDWINDOW | WS_CAPTION);
|
|
|
|
- GB_MASK_SET(style, !is_borderless, WS_POPUP);
|
|
|
|
-
|
|
|
|
- SetWindowLongW(handle, GWL_STYLE, style);
|
|
|
|
-
|
|
|
|
- GB_MASK_SET(p->window_flags, !is_borderless, gbWindow_Borderless);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-gb_inline void gb_platform_make_opengl_context_current(gbPlatform *p) {
|
|
|
|
- if (p->renderer_type == gbRenderer_Opengl) {
|
|
|
|
- wglMakeCurrent(cast(HDC)p->win32_dc, cast(HGLRC)p->opengl.context);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_inline void gb_platform_show_window(gbPlatform *p) {
|
|
|
|
- ShowWindow(cast(HWND)p->window_handle, SW_SHOW);
|
|
|
|
- p->window_flags &= ~gbWindow_Hidden;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_inline void gb_platform_hide_window(gbPlatform *p) {
|
|
|
|
- ShowWindow(cast(HWND)p->window_handle, SW_HIDE);
|
|
|
|
- p->window_flags |= gbWindow_Hidden;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_inline gbVideoMode gb_video_mode_get_desktop(void) {
|
|
|
|
- DEVMODEW win32_mode = {gb_size_of(win32_mode)};
|
|
|
|
- EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &win32_mode);
|
|
|
|
- return gb_video_mode(win32_mode.dmPelsWidth, win32_mode.dmPelsHeight, win32_mode.dmBitsPerPel);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-isize gb_video_mode_get_fullscreen_modes(gbVideoMode *modes, isize max_mode_count) {
|
|
|
|
- DEVMODEW win32_mode = {gb_size_of(win32_mode)};
|
|
|
|
- i32 count;
|
|
|
|
- for (count = 0;
|
|
|
|
- count < max_mode_count && EnumDisplaySettingsW(NULL, count, &win32_mode);
|
|
|
|
- count++) {
|
|
|
|
- modes[count] = gb_video_mode(win32_mode.dmPelsWidth, win32_mode.dmPelsHeight, win32_mode.dmBitsPerPel);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- gb_sort_array(modes, count, gb_video_mode_dsc_cmp);
|
|
|
|
- return count;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-b32 gb_platform_has_clipboard_text(gbPlatform *p) {
|
|
|
|
- b32 result = false;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// TODO(bill): Handle UTF-8
|
|
|
|
-void gb_platform_set_clipboard_text(gbPlatform *p, char const *str) {
|
|
|
|
- if (OpenClipboard(cast(HWND)p->window_handle)) {
|
|
|
|
- isize i, len = gb_strlen(str)+1;
|
|
|
|
-
|
|
|
|
- HANDLE mem = cast(HANDLE)GlobalAlloc(0x0002/*GMEM_MOVEABLE*/, len);
|
|
|
|
- if (mem) {
|
|
|
|
- char *dst = cast(char *)GlobalLock(mem);
|
|
|
|
- if (dst) {
|
|
|
|
- for (i = 0; str[i]; i++) {
|
|
|
|
- // TODO(bill): Does this cause a buffer overflow?
|
|
|
|
- // NOTE(bill): Change \n to \r\n 'cause windows
|
|
|
|
- if (str[i] == '\n' && (i == 0 || str[i-1] != '\r')) {
|
|
|
|
- *dst++ = '\r';
|
|
|
|
- }
|
|
|
|
- *dst++ = str[i];
|
|
|
|
- }
|
|
|
|
- *dst = 0;
|
|
|
|
- }
|
|
|
|
- GlobalUnlock(mem);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- EmptyClipboard();
|
|
|
|
- if (!SetClipboardData(1/*CF_TEXT*/, mem)) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- CloseClipboard();
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// TODO(bill): Handle UTF-8
|
|
|
|
-char *gb_platform_get_clipboard_text(gbPlatform *p, gbAllocator a) {
|
|
|
|
- char *text = NULL;
|
|
|
|
-
|
|
|
|
- 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>
|
|
|
|
-
|
|
|
|
-#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
|
|
|
|
- #define NSIntegerEncoding "q"
|
|
|
|
- #define NSUIntegerEncoding "L"
|
|
|
|
-#else
|
|
|
|
- #define NSIntegerEncoding "i"
|
|
|
|
- #define NSUIntegerEncoding "I"
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-#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(__OBJC__) && __has_feature(objc_arc)
|
|
|
|
-#error TODO(bill): Cannot compile as objective-c code just yet!
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-// ABI is a bit different between platforms
|
|
|
|
-#ifdef __arm64__
|
|
|
|
-#define abi_objc_msgSend_stret objc_msgSend
|
|
|
|
-#else
|
|
|
|
-#define abi_objc_msgSend_stret objc_msgSend_stret
|
|
|
|
-#endif
|
|
|
|
-#ifdef __i386__
|
|
|
|
-#define abi_objc_msgSend_fpret objc_msgSend_fpret
|
|
|
|
-#else
|
|
|
|
-#define abi_objc_msgSend_fpret objc_msgSend
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-#define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend)
|
|
|
|
-#define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend)
|
|
|
|
-#define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend)
|
|
|
|
-#define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend)
|
|
|
|
-#define objc_msgSend_id_char_const ((id (*)(id, SEL, char const *))objc_msgSend)
|
|
|
|
-
|
|
|
|
-gb_internal NSUInteger gb__osx_application_should_terminate(id self, SEL _sel, id sender) {
|
|
|
|
- // NOTE(bill): Do nothing
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_internal void gb__osx_window_will_close(id self, SEL _sel, id notification) {
|
|
|
|
- NSUInteger value = true;
|
|
|
|
- object_setInstanceVariable(self, "closed", cast(void *)value);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_internal void gb__osx_window_did_become_key(id self, SEL _sel, id notification) {
|
|
|
|
- gbPlatform *p = NULL;
|
|
|
|
- object_getInstanceVariable(self, "gbPlatform", cast(void **)&p);
|
|
|
|
- if (p) {
|
|
|
|
- // TODO(bill):
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-b32 gb__platform_init(gbPlatform *p, char const *window_title, gbVideoMode mode, gbRendererType type, u32 window_flags) {
|
|
|
|
- if (p->is_initialized) {
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
- // Init Platform
|
|
|
|
- { // Initial OSX State
|
|
|
|
- Class appDelegateClass;
|
|
|
|
- b32 resultAddProtoc, resultAddMethod;
|
|
|
|
- id dgAlloc, dg, menubarAlloc, menubar;
|
|
|
|
- id appMenuItemAlloc, appMenuItem;
|
|
|
|
- id appMenuAlloc, appMenu;
|
|
|
|
-
|
|
|
|
- #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
|
|
|
|
-
|
|
|
|
- objc_msgSend_void_id(NSApp, sel_registerName("setDelegate:"), dg);
|
|
|
|
- objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
|
|
|
|
-
|
|
|
|
- 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
|
|
|
|
-
|
|
|
|
- objc_msgSend_void_id(window, sel_registerName("setDelegate:"), wdg);
|
|
|
|
-
|
|
|
|
- contentView = objc_msgSend_id(window, sel_registerName("contentView"));
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- NSPoint point = {20, 20};
|
|
|
|
- ((void (*)(id, SEL, NSPoint))objc_msgSend)(window, sel_registerName("cascadeTopLeftFromPoint:"), point);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// NOTE(bill): Software rendering
|
|
|
|
-b32 gb_platform_init_with_software(gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags) {
|
|
|
|
- GB_PANIC("TODO(bill): Software rendering in not yet implemented on OS X\n");
|
|
|
|
- return gb__platform_init(p, window_title, gb_video_mode(width, height, 32), gbRenderer_Software, window_flags);
|
|
|
|
-}
|
|
|
|
-// NOTE(bill): OpenGL Rendering
|
|
|
|
-b32 gb_platform_init_with_opengl(gbPlatform *p, char const *window_title, i32 width, i32 height, u32 window_flags,
|
|
|
|
- i32 major, i32 minor, b32 core, b32 compatible) {
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// NOTE(bill): Reverse engineering can be fun!!!
|
|
|
|
-gb_internal gbKeyType gb__osx_from_key_code(u16 key_code) {
|
|
|
|
- switch (key_code) {
|
|
|
|
- default: return gbKey_Unknown;
|
|
|
|
- // NOTE(bill): WHO THE FUCK DESIGNED THIS VIRTUAL KEY CODE SYSTEM?!
|
|
|
|
- // THEY ARE FUCKING IDIOTS!
|
|
|
|
- case 0x1d: return gbKey_0;
|
|
|
|
- case 0x12: return gbKey_1;
|
|
|
|
- case 0x13: return gbKey_2;
|
|
|
|
- case 0x14: return gbKey_3;
|
|
|
|
- case 0x15: return gbKey_4;
|
|
|
|
- case 0x17: return gbKey_5;
|
|
|
|
- case 0x16: return gbKey_6;
|
|
|
|
- case 0x1a: return gbKey_7;
|
|
|
|
- case 0x1c: return gbKey_8;
|
|
|
|
- case 0x19: return gbKey_9;
|
|
|
|
-
|
|
|
|
- case 0x00: return gbKey_A;
|
|
|
|
- case 0x0b: return gbKey_B;
|
|
|
|
- case 0x08: return gbKey_C;
|
|
|
|
- case 0x02: return gbKey_D;
|
|
|
|
- case 0x0e: return gbKey_E;
|
|
|
|
- case 0x03: return gbKey_F;
|
|
|
|
- case 0x05: return gbKey_G;
|
|
|
|
- case 0x04: return gbKey_H;
|
|
|
|
- case 0x22: return gbKey_I;
|
|
|
|
- case 0x26: return gbKey_J;
|
|
|
|
- case 0x28: return gbKey_K;
|
|
|
|
- case 0x25: return gbKey_L;
|
|
|
|
- case 0x2e: return gbKey_M;
|
|
|
|
- case 0x2d: return gbKey_N;
|
|
|
|
- case 0x1f: return gbKey_O;
|
|
|
|
- case 0x23: return gbKey_P;
|
|
|
|
- case 0x0c: return gbKey_Q;
|
|
|
|
- case 0x0f: return gbKey_R;
|
|
|
|
- case 0x01: return gbKey_S;
|
|
|
|
- case 0x11: return gbKey_T;
|
|
|
|
- case 0x20: return gbKey_U;
|
|
|
|
- case 0x09: return gbKey_V;
|
|
|
|
- case 0x0d: return gbKey_W;
|
|
|
|
- case 0x07: return gbKey_X;
|
|
|
|
- case 0x10: return gbKey_Y;
|
|
|
|
- case 0x06: return gbKey_Z;
|
|
|
|
-
|
|
|
|
- case 0x21: return gbKey_Lbracket;
|
|
|
|
- case 0x1e: return gbKey_Rbracket;
|
|
|
|
- case 0x29: return gbKey_Semicolon;
|
|
|
|
- case 0x2b: return gbKey_Comma;
|
|
|
|
- case 0x2f: return gbKey_Period;
|
|
|
|
- case 0x27: return gbKey_Quote;
|
|
|
|
- case 0x2c: return gbKey_Slash;
|
|
|
|
- case 0x2a: return gbKey_Backslash;
|
|
|
|
- case 0x32: return gbKey_Grave;
|
|
|
|
- case 0x18: return gbKey_Equals;
|
|
|
|
- case 0x1b: return gbKey_Minus;
|
|
|
|
- case 0x31: return gbKey_Space;
|
|
|
|
-
|
|
|
|
- case 0x35: return gbKey_Escape; // Escape
|
|
|
|
- case 0x3b: return gbKey_Lcontrol; // Left Control
|
|
|
|
- case 0x38: return gbKey_Lshift; // Left Shift
|
|
|
|
- case 0x3a: return gbKey_Lalt; // Left Alt
|
|
|
|
- case 0x37: return gbKey_Lsystem; // Left OS specific: window (Windows and Linux), apple/cmd (MacOS X), ...
|
|
|
|
- case 0x3e: return gbKey_Rcontrol; // Right Control
|
|
|
|
- case 0x3c: return gbKey_Rshift; // Right Shift
|
|
|
|
- case 0x3d: return gbKey_Ralt; // Right Alt
|
|
|
|
- // case 0x37: return gbKey_Rsystem; // Right OS specific: window (Windows and Linux), apple/cmd (MacOS X), ...
|
|
|
|
- case 0x6e: return gbKey_Menu; // Menu
|
|
|
|
- case 0x24: return gbKey_Return; // Return
|
|
|
|
- case 0x33: return gbKey_Backspace; // Backspace
|
|
|
|
- case 0x30: return gbKey_Tab; // Tabulation
|
|
|
|
- case 0x74: return gbKey_Pageup; // Page up
|
|
|
|
- case 0x79: return gbKey_Pagedown; // Page down
|
|
|
|
- case 0x77: return gbKey_End; // End
|
|
|
|
- case 0x73: return gbKey_Home; // Home
|
|
|
|
- case 0x72: return gbKey_Insert; // Insert
|
|
|
|
- case 0x75: return gbKey_Delete; // Delete
|
|
|
|
- case 0x45: return gbKey_Plus; // +
|
|
|
|
- case 0x4e: return gbKey_Subtract; // -
|
|
|
|
- case 0x43: return gbKey_Multiply; // *
|
|
|
|
- case 0x4b: return gbKey_Divide; // /
|
|
|
|
- case 0x7b: return gbKey_Left; // Left arrow
|
|
|
|
- case 0x7c: return gbKey_Right; // Right arrow
|
|
|
|
- case 0x7e: return gbKey_Up; // Up arrow
|
|
|
|
- case 0x7d: return gbKey_Down; // Down arrow
|
|
|
|
- case 0x52: return gbKey_Numpad0; // Numpad 0
|
|
|
|
- case 0x53: return gbKey_Numpad1; // Numpad 1
|
|
|
|
- case 0x54: return gbKey_Numpad2; // Numpad 2
|
|
|
|
- case 0x55: return gbKey_Numpad3; // Numpad 3
|
|
|
|
- case 0x56: return gbKey_Numpad4; // Numpad 4
|
|
|
|
- case 0x57: return gbKey_Numpad5; // Numpad 5
|
|
|
|
- case 0x58: return gbKey_Numpad6; // Numpad 6
|
|
|
|
- case 0x59: return gbKey_Numpad7; // Numpad 7
|
|
|
|
- case 0x5b: return gbKey_Numpad8; // Numpad 8
|
|
|
|
- case 0x5c: return gbKey_Numpad9; // Numpad 9
|
|
|
|
- case 0x41: return gbKey_NumpadDot; // Numpad .
|
|
|
|
- case 0x4c: return gbKey_NumpadEnter; // Numpad Enter
|
|
|
|
- case 0x7a: return gbKey_F1; // F1
|
|
|
|
- case 0x78: return gbKey_F2; // F2
|
|
|
|
- case 0x63: return gbKey_F3; // F3
|
|
|
|
- case 0x76: return gbKey_F4; // F4
|
|
|
|
- case 0x60: return gbKey_F5; // F5
|
|
|
|
- case 0x61: return gbKey_F6; // F6
|
|
|
|
- case 0x62: return gbKey_F7; // F7
|
|
|
|
- case 0x64: return gbKey_F8; // F8
|
|
|
|
- case 0x65: return gbKey_F9; // F8
|
|
|
|
- case 0x6d: return gbKey_F10; // F10
|
|
|
|
- case 0x67: return gbKey_F11; // F11
|
|
|
|
- case 0x6f: return gbKey_F12; // F12
|
|
|
|
- case 0x69: return gbKey_F13; // F13
|
|
|
|
- case 0x6b: return gbKey_F14; // F14
|
|
|
|
- case 0x71: return gbKey_F15; // F15
|
|
|
|
- // case : return gbKey_Pause; // Pause // NOTE(bill): Not possible on OS X
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_internal void gb__osx_on_cocoa_event(gbPlatform *p, id event, id window) {
|
|
|
|
- if (!event) {
|
|
|
|
- return;
|
|
|
|
- } else if (objc_msgSend_id(window, sel_registerName("delegate"))) {
|
|
|
|
- NSUInteger event_type = ((NSUInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("type"));
|
|
|
|
- switch (event_type) {
|
|
|
|
- case 1: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Left], true); break; // NSLeftMouseDown
|
|
|
|
- case 2: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Left], false); break; // NSLeftMouseUp
|
|
|
|
- case 3: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Right], true); break; // NSRightMouseDown
|
|
|
|
- case 4: gb_key_state_update(&p->mouse_buttons[gbMouseButton_Right], false); break; // NSRightMouseUp
|
|
|
|
- case 25: { // NSOtherMouseDown
|
|
|
|
- // TODO(bill): Test thoroughly
|
|
|
|
- NSInteger number = ((NSInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"));
|
|
|
|
- if (number == 2) gb_key_state_update(&p->mouse_buttons[gbMouseButton_Middle], true);
|
|
|
|
- if (number == 3) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X1], true);
|
|
|
|
- if (number == 4) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X2], true);
|
|
|
|
- } break;
|
|
|
|
- case 26: { // NSOtherMouseUp
|
|
|
|
- NSInteger number = ((NSInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"));
|
|
|
|
- if (number == 2) gb_key_state_update(&p->mouse_buttons[gbMouseButton_Middle], false);
|
|
|
|
- if (number == 3) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X1], false);
|
|
|
|
- if (number == 4) gb_key_state_update(&p->mouse_buttons[gbMouseButton_X2], false);
|
|
|
|
-
|
|
|
|
- } break;
|
|
|
|
-
|
|
|
|
- // TODO(bill): Scroll wheel
|
|
|
|
- case 22: { // NSScrollWheel
|
|
|
|
- CGFloat dx = ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("scrollingDeltaX"));
|
|
|
|
- CGFloat dy = ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("scrollingDeltaY"));
|
|
|
|
- BOOL precision_scrolling = ((BOOL (*)(id, SEL))objc_msgSend)(event, sel_registerName("hasPreciseScrollingDeltas"));
|
|
|
|
- if (precision_scrolling) {
|
|
|
|
- dx *= 0.1f;
|
|
|
|
- dy *= 0.1f;
|
|
|
|
- }
|
|
|
|
- // TODO(bill): Handle sideways
|
|
|
|
- p->mouse_wheel_delta = dy;
|
|
|
|
- // p->mouse_wheel_dy = dy;
|
|
|
|
- // gb_printf("%f %f\n", dx, dy);
|
|
|
|
- } break;
|
|
|
|
-
|
|
|
|
- case 12: { // NSFlagsChanged
|
|
|
|
- #if 0
|
|
|
|
- // TODO(bill): Reverse engineer this properly
|
|
|
|
- NSUInteger modifiers = ((NSUInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags"));
|
|
|
|
- u32 upper_mask = (modifiers & 0xffff0000ul) >> 16;
|
|
|
|
- b32 shift = (upper_mask & 0x02) != 0;
|
|
|
|
- b32 control = (upper_mask & 0x04) != 0;
|
|
|
|
- b32 alt = (upper_mask & 0x08) != 0;
|
|
|
|
- b32 command = (upper_mask & 0x10) != 0;
|
|
|
|
- #endif
|
|
|
|
-
|
|
|
|
- // gb_printf("%u\n", keys.mask);
|
|
|
|
- // gb_printf("%x\n", cast(u32)modifiers);
|
|
|
|
- } break;
|
|
|
|
-
|
|
|
|
- case 10: { // NSKeyDown
|
|
|
|
- u16 key_code;
|
|
|
|
-
|
|
|
|
- id input_text = objc_msgSend_id(event, sel_registerName("characters"));
|
|
|
|
- char const *input_text_utf8 = ((char const *(*)(id, SEL))objc_msgSend)(input_text, sel_registerName("UTF8String"));
|
|
|
|
- p->char_buffer_count = gb_strnlen(input_text_utf8, gb_size_of(p->char_buffer));
|
|
|
|
- gb_memcopy(p->char_buffer, input_text_utf8, p->char_buffer_count);
|
|
|
|
-
|
|
|
|
- key_code = ((unsigned short (*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
|
|
|
|
- gb_key_state_update(&p->keys[gb__osx_from_key_code(key_code)], true);
|
|
|
|
- } break;
|
|
|
|
-
|
|
|
|
- case 11: { // NSKeyUp
|
|
|
|
- u16 key_code = ((unsigned short (*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
|
|
|
|
- gb_key_state_update(&p->keys[gb__osx_from_key_code(key_code)], false);
|
|
|
|
- } break;
|
|
|
|
-
|
|
|
|
- default: break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), event);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-void gb_platform_update(gbPlatform *p) {
|
|
|
|
- id window, key_window, content_view;
|
|
|
|
- NSRect original_frame;
|
|
|
|
-
|
|
|
|
- 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?
|
|
|
|
-
|
|
|
|
- // NOTE(bill): Need to update as the keys only get updates on events
|
|
|
|
- for (i = 0; i < gbKey_Count; i++) {
|
|
|
|
- b32 is_down = (p->keys[i] & gbKeyState_Down) != 0;
|
|
|
|
- gb_key_state_update(&p->keys[i], is_down);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (i = 0; i < gbMouseButton_Count; i++) {
|
|
|
|
- b32 is_down = (p->mouse_buttons[i] & gbKeyState_Down) != 0;
|
|
|
|
- gb_key_state_update(&p->mouse_buttons[i], is_down);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- { // 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];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- { // Check if window is closed
|
|
|
|
- id wdg = objc_msgSend_id(window, sel_registerName("delegate"));
|
|
|
|
- if (!wdg) {
|
|
|
|
- p->window_is_closed = false;
|
|
|
|
- } else {
|
|
|
|
- NSUInteger value = 0;
|
|
|
|
- object_getInstanceVariable(wdg, "closed", cast(void **)&value);
|
|
|
|
- p->window_is_closed = (value != 0);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- content_view = objc_msgSend_id(window, sel_registerName("contentView"));
|
|
|
|
- original_frame = ((NSRect (*)(id, SEL))abi_objc_msgSend_stret)(content_view, sel_registerName("frame"));
|
|
|
|
-
|
|
|
|
- { // 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
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // TODO(bill): Is this in the correct place?
|
|
|
|
- objc_msgSend_void(NSApp, sel_registerName("updateWindows"));
|
|
|
|
- if (p->renderer_type == gbRenderer_Opengl) {
|
|
|
|
- objc_msgSend_void(cast(id)p->opengl.context, sel_registerName("update"));
|
|
|
|
- gb_platform_make_opengl_context_current(p);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_display(gbPlatform *p) {
|
|
|
|
- // TODO(bill): Do more
|
|
|
|
- if (p->renderer_type == gbRenderer_Opengl) {
|
|
|
|
- gb_platform_make_opengl_context_current(p);
|
|
|
|
- objc_msgSend_void(cast(id)p->opengl.context, sel_registerName("flushBuffer"));
|
|
|
|
- } else if (p->renderer_type == gbRenderer_Software) {
|
|
|
|
- // TODO(bill):
|
|
|
|
- } else {
|
|
|
|
- GB_PANIC("Invalid window rendering type");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_destroy(gbPlatform *p) {
|
|
|
|
- gb_platform_make_opengl_context_current(p);
|
|
|
|
-
|
|
|
|
- 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
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_show_cursor(gbPlatform *p, b32 show) {
|
|
|
|
- if (show ) {
|
|
|
|
- // objc_msgSend_void(class_registerName("NSCursor"), sel_registerName("unhide"));
|
|
|
|
- } else {
|
|
|
|
- // objc_msgSend_void(class_registerName("NSCursor"), sel_registerName("hide"));
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_set_mouse_position(gbPlatform *p, i32 x, i32 y) {
|
|
|
|
- // TODO(bill):
|
|
|
|
- CGPoint pos = {cast(CGFloat)x, cast(CGFloat)y};
|
|
|
|
- pos.x += p->window_x;
|
|
|
|
- pos.y += p->window_y;
|
|
|
|
- CGWarpMouseCursorPosition(pos);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_set_controller_vibration(gbPlatform *p, isize index, f32 left_motor, f32 right_motor) {
|
|
|
|
- // TODO(bill):
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-b32 gb_platform_has_clipboard_text(gbPlatform *p) {
|
|
|
|
- // TODO(bill):
|
|
|
|
- return false;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_set_clipboard_text(gbPlatform *p, char const *str) {
|
|
|
|
- // TODO(bill):
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-char *gb_platform_get_clipboard_text(gbPlatform *p, gbAllocator a) {
|
|
|
|
- // TODO(bill):
|
|
|
|
- return NULL;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_set_window_position(gbPlatform *p, i32 x, i32 y) {
|
|
|
|
- // TODO(bill):
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_set_window_title(gbPlatform *p, char const *title, ...) {
|
|
|
|
- id title_string;
|
|
|
|
- char buf[256] = {0};
|
|
|
|
- va_list va;
|
|
|
|
- va_start(va, title);
|
|
|
|
- gb_snprintf_va(buf, gb_count_of(buf), title, va);
|
|
|
|
- va_end(va);
|
|
|
|
-
|
|
|
|
- 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);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_toggle_fullscreen(gbPlatform *p, b32 fullscreen_desktop) {
|
|
|
|
- // TODO(bill):
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_toggle_borderless(gbPlatform *p) {
|
|
|
|
- // TODO(bill):
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_make_opengl_context_current(gbPlatform *p) {
|
|
|
|
- objc_msgSend_void(cast(id)p->opengl.context, sel_registerName("makeCurrentContext"));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_show_window(gbPlatform *p) {
|
|
|
|
- // TODO(bill):
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void gb_platform_hide_window(gbPlatform *p) {
|
|
|
|
- // TODO(bill):
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-i32 gb__osx_mode_bits_per_pixel(CGDisplayModeRef mode) {
|
|
|
|
- i32 bits_per_pixel = 0;
|
|
|
|
- CFStringRef pixel_encoding = CGDisplayModeCopyPixelEncoding(mode);
|
|
|
|
- if(CFStringCompare(pixel_encoding, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
|
|
|
|
- bits_per_pixel = 32;
|
|
|
|
- } else if(CFStringCompare(pixel_encoding, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
|
|
|
|
- bits_per_pixel = 16;
|
|
|
|
- } else if(CFStringCompare(pixel_encoding, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
|
|
|
|
- bits_per_pixel = 8;
|
|
|
|
- }
|
|
|
|
- CFRelease(pixel_encoding);
|
|
|
|
-
|
|
|
|
- return bits_per_pixel;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-i32 gb__osx_display_bits_per_pixel(CGDirectDisplayID display) {
|
|
|
|
- CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display);
|
|
|
|
- i32 bits_per_pixel = gb__osx_mode_bits_per_pixel(mode);
|
|
|
|
- CGDisplayModeRelease(mode);
|
|
|
|
- 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));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-isize gb_video_mode_get_fullscreen_modes(gbVideoMode *modes, isize max_mode_count) {
|
|
|
|
- CFArrayRef cg_modes = CGDisplayCopyAllDisplayModes(CGMainDisplayID(), NULL);
|
|
|
|
- CFIndex i, count;
|
|
|
|
- if (cg_modes == NULL) {
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- count = gb_min(CFArrayGetCount(cg_modes), max_mode_count);
|
|
|
|
- for (i = 0; i < count; i++) {
|
|
|
|
- CGDisplayModeRef cg_mode = cast(CGDisplayModeRef)CFArrayGetValueAtIndex(cg_modes, i);
|
|
|
|
- modes[i] = gb_video_mode(CGDisplayModeGetWidth(cg_mode),
|
|
|
|
- CGDisplayModeGetHeight(cg_mode),
|
|
|
|
- gb__osx_mode_bits_per_pixel(cg_mode));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- CFRelease(cg_modes);
|
|
|
|
-
|
|
|
|
- gb_sort_array(modes, count, gb_video_mode_dsc_cmp);
|
|
|
|
- return cast(isize)count;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-// TODO(bill): OSX Platform Layer
|
|
|
|
-// NOTE(bill): Use this as a guide so there is no need for Obj-C https://github.com/jimon/osx_app_in_plain_c
|
|
|
|
-
|
|
|
|
-gb_inline gbVideoMode gb_video_mode(i32 width, i32 height, i32 bits_per_pixel) {
|
|
|
|
- gbVideoMode m;
|
|
|
|
- m.width = width;
|
|
|
|
- m.height = height;
|
|
|
|
- m.bits_per_pixel = bits_per_pixel;
|
|
|
|
- return m;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-gb_inline b32 gb_video_mode_is_valid(gbVideoMode mode) {
|
|
|
|
- gb_local_persist gbVideoMode modes[256] = {0};
|
|
|
|
- gb_local_persist isize mode_count = 0;
|
|
|
|
- gb_local_persist b32 is_set = false;
|
|
|
|
- isize i;
|
|
|
|
-
|
|
|
|
- if (!is_set) {
|
|
|
|
- mode_count = gb_video_mode_get_fullscreen_modes(modes, gb_count_of(modes));
|
|
|
|
- is_set = true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (i = 0; i < mode_count; i++) {
|
|
|
|
- gb_printf("%d %d\n", modes[i].width, modes[i].height);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return gb_binary_search_array(modes, mode_count, &mode, gb_video_mode_cmp) >= 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-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)
|
|
#if defined(GB_COMPILER_MSVC)
|
|
#pragma warning(pop)
|
|
#pragma warning(pop)
|
|
#endif
|
|
#endif
|