Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\fileio.cpp":
- #include <windows.h>
- #include <public_stuff.hpp>
- BinaryData::BinaryData(const char* file_path)
- : size( fileio_size(file_path) )
- {
- data_void = nullptr;
- if(size == FILEIO_SIZE_ERR) return;
- size_t _size;
- data_void = fileio_read(file_path, &_size);
- if(_size != size){ // Just in case
- mem_free(&data_void);
- }
- }
- BinaryData::BinaryData(const void* src, size_t _size)
- : size( _size )
- {
- data_void = mem_alloc(size+1);
- if(data_void == nullptr) return; // Failed to allocate memory for data
- // Adding +1 to the Alloc size makes it so that I can always
- // make sure that the file's data is null-terminated, like so:
- data_char[size] = 0;
- if(src != nullptr) mem_copy(data_char, src, size);
- }
- BinaryData::~BinaryData(){
- mem_free(&data_void);
- }
- /******************************************************************************/
- void* fileio_read(const char* file_path, size_t* dataSize_p){
- size_t size = fileio_size(file_path);
- if(size == FILEIO_SIZE_ERR) return nullptr;
- // Open the file
- HANDLE fileHandle = CreateFileA(file_path, GENERIC_READ, FILE_SHARE_READ,
- nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
- if(fileHandle == INVALID_HANDLE_VALUE) return nullptr; // Failed to open file
- // Allocate memory for file data
- void* data = mem_alloc(size+1);
- if(data == nullptr){ CloseHandle(fileHandle); return nullptr; }
- ((char*)data)[size] = 0;
- DWORD bytesRead;
- BOOL success = ReadFile(fileHandle, data, (DWORD)size, &bytesRead, nullptr);
- CloseHandle(fileHandle);
- if(!success || bytesRead < size)
- {
- mem_free(&data);
- return nullptr;
- }
- if(dataSize_p != nullptr) *dataSize_p = size;
- return data;
- }
- bool fileio_write(const char* file_path, const void* data,
- size_t data_size, bool append)
- {
- if(file_path == nullptr || data == nullptr) return false;
- // Writing files >=4GiB is currently unsupported!
- // (If you really wanted to, I guess you could write files larger than 4 gigs
- // with an overwrite and a series of appends, so that's always an option?)
- if(data_size >= 0xFFFFFFFF) return false;
- DWORD accessMode, creationDisposition;
- if(append){ // Create or append to existing
- accessMode = FILE_APPEND_DATA;
- creationDisposition = OPEN_ALWAYS;
- } else { // Create or overwrite existing
- accessMode = GENERIC_WRITE;
- creationDisposition = CREATE_ALWAYS;
- }
- HANDLE fileHandle = CreateFileA(file_path, accessMode, 0, nullptr,
- creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
- if(fileHandle == INVALID_HANDLE_VALUE) return false; // Failed to open file
- // If data_size *is* 0, then just create/open the file without writing to it
- if(data_size > 0){
- DWORD bytesWritten;
- BOOL success = WriteFile(fileHandle, data, (DWORD)data_size, &bytesWritten, nullptr);
- CloseHandle(fileHandle);
- if(!success) return false; // Failed to write to file
- if(bytesWritten < data_size) return false; // Bytes read was less than data_size
- } else {
- CloseHandle(fileHandle);
- }
- return true;
- }
- /******************************************************************************/
- size_t fileio_size(const char* file_path){
- if(file_path == nullptr) return FILEIO_SIZE_ERR;
- HANDLE fileHandle = CreateFileA(file_path, 0, FILE_SHARE_READ, nullptr,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,nullptr);
- if(fileHandle == INVALID_HANDLE_VALUE){
- if(GetLastError() == ERROR_FILE_NOT_FOUND) return FILEIO_SIZE_ERR; // File not found
- else return FILEIO_SIZE_ERR; // Failed to open file
- }
- LARGE_INTEGER fileSize;
- BOOL success = GetFileSizeEx(fileHandle, &fileSize);
- CloseHandle(fileHandle); // Close the file whether or not the query succeeds
- if(!success) return FILEIO_SIZE_ERR;
- return (size_t)fileSize.QuadPart;
- }
- #define IS_READONLY(_attributes) \
- ( (_attributes)&FILE_ATTRIBUTE_READONLY )
- #define IS_DIRECTORY(_attributes) \
- ( (_attributes)&FILE_ATTRIBUTE_DIRECTORY )
- #define IS_READONLY_FILE(_attributes) \
- ( IS_READONLY(_attributes) && !IS_DIRECTORY(_attributes) )
- bool fileio_isreadonly(const char* file_path){
- if(file_path == nullptr) return false; // file_path is not a real string
- DWORD fileAttributes = GetFileAttributesA(file_path);
- if(fileAttributes == INVALID_FILE_ATTRIBUTES){
- if(GetLastError() != ERROR_FILE_NOT_FOUND) return false; // Failed to get file attributes
- else return false; // File doesn't exist
- } else if(IS_READONLY_FILE(fileAttributes)){
- return true; // File is read-only
- } else {
- return false; // File is not read-only
- }
- }
- #define IS_NORMAL_FILE(_attributes) \
- ( !( IS_READONLY(_attributes) || IS_DIRECTORY(_attributes) ) )
- bool fileio_exists(const char* file_path){
- if(file_path == nullptr) return false; // file_path is not a real string
- DWORD fileAttributes = GetFileAttributesA(file_path);
- if(fileAttributes == INVALID_FILE_ATTRIBUTES){
- if(GetLastError() != ERROR_FILE_NOT_FOUND) return false; // Failed to get file attributes
- else return false;
- } else if(IS_NORMAL_FILE(fileAttributes)){
- return true;
- } else {
- return false; // Path exists, but is not associated with a normal file
- }
- }
- bool fileio_delete(const char* file_path){
- if(!fileio_exists(file_path)) return false; // File does not exist
- if(!DeleteFileA(file_path) ) return false; // Failed to delete file
- return true;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\functions.cpp":
- #include <win32/audio.hpp>
- #include <win32/video.hpp>
- _Winmm_func_t _Winmm_func;
- char _Winmm_names_a[] = // prefix = "time"
- "GetDevCaps\0"
- "BeginPeriod\0"
- "\0";
- char _Winmm_names_b[] = // prefix = "waveOut"
- //"GetNumDevs\0"
- "GetDevCapsA\0"
- //"GetVolume\0"
- "SetVolume\0"
- //"GetErrorTextA\0"
- "Open\0"
- //"Close\0"
- "PrepareHeader\0"
- //"UnprepareHeader\0"
- "Write\0"
- "Pause\0"
- //"Restart\0"
- //"Reset\0"
- //"BreakLoop\0"
- //"GetPosition\0"
- //"GetPitch\0"
- //"SetPitch\0"
- //"GetPlaybackRate\0"
- //"SetPlaybackRate\0"
- //"GetID\0"
- //"Message\0"
- "\0";
- _User32_func_t _User32_func;
- char _User32_names_a[] = // prefix = ""
- "RegisterClassA\0"
- "CreateWindowExA\0"
- "DefWindowProcA\0"
- "InvalidateRect\0"
- "UpdateWindow\0"
- "BeginPaint\0"
- "EndPaint\0"
- "PeekMessageA\0"
- "DispatchMessageA\0"
- "DestroyWindow\0"
- //"ReleaseDC\0"
- //"GetDC\0"
- "PostQuitMessage\0"
- "MessageBoxA\0"
- "TranslateMessage\0"
- "GetWindowLongA\0"
- "AdjustWindowRectEx\0"
- "LoadCursorA\0"
- "MapVirtualKeyA\0"
- //"GetCursorPos\0"
- "ScreenToClient\0"
- "ShowCursor\0"
- "ClipCursor\0"
- "GetClientRect\0"
- "ClientToScreen\0"
- "SetCapture\0"
- "ReleaseCapture\0"
- "SetCursorPos\0"
- "RegisterRawInputDevices\0"
- "GetRawInputData\0"
- "\0";
- _Gdi32_func_t _Gdi32_func;
- char _Gdi32_names_a[] = // prefix = ""
- "CreateCompatibleDC\0"
- "CreateDIBSection\0"
- "SelectObject\0"
- "DeleteObject\0"
- "BitBlt\0"
- "DeleteDC\0"
- "StretchBlt\0"
- "CreateCompatibleBitmap\0"
- "SetStretchBltMode\0"
- "SetDIBColorTable\0"
- "CreateSolidBrush\0"
- "\0";
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\input.cpp":
- #include <win32/input.hpp>
- #include <public_stuff.hpp>
- CRITICAL_SECTION events_lock;
- Event events_queue[65536];
- u16 events_next = 0;
- u16 events_end = 0;
- Event_Key_Mod key_mods;
- bool key_states[256] = {0};
- Point2d mouse_position = {0};
- bool mouse_was_moved_before = false;
- bool cursor_trapped = false;
- bool cursor_hidden = false;
- extern HWND win;
- int InputInit(){
- // This function has no failure condition
- InitializeCriticalSectionAndSpinCount(&events_lock, 2048);
- return 0;
- }
- void InputQuit(){}
- // Returns false if queue is full
- bool AddToEventQueue(Event& event){
- EnterCriticalSection(&events_lock);
- bool success = false;
- if((events_end+1) != events_next){
- events_queue[events_end++] = event;
- success = true;
- }
- LeaveCriticalSection(&events_lock);
- return success;
- }
- // Returns a EVENT_NULL event if queue is empty
- Event RemoveFromEventQueue(){
- EnterCriticalSection(&events_lock);
- Event event;
- event.type = EVENT_NULL;
- if(events_next != events_end)
- event = events_queue[events_next++];
- LeaveCriticalSection(&events_lock);
- return event;
- }
- // Calling this exclusively in the main thread is recommended
- bool pollEvent(Event* event_p){
- // Take one event off the current event queue and set it to *event_p
- EnterCriticalSection(&events_lock);
- Event event = RemoveFromEventQueue();
- LeaveCriticalSection(&events_lock);
- // If previous event queue is now empty, process any pending window messages,
- // while adding any events generated by WindowProc to the event queue
- if(events_next == events_end){
- MSG message;
- while(PeekMessageA(&message, nullptr, 0, 0, PM_REMOVE)){
- TranslateMessage(&message);
- DispatchMessageA(&message);
- }
- }
- if(event_p != nullptr) *event_p = event;
- return event.type != EVENT_NULL;
- }
- bool is_cursor_trapped(){
- return cursor_trapped; // Yep, that's all it does
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\main.cpp":
- #include <win32/audio.hpp>
- #include <win32/video.hpp>
- #include <win32/input.hpp>
- #include <public_stuff.hpp>
- static HMODULE _Ole32_dll;
- static HMODULE _Winmm_dll;
- static HMODULE _User32_dll;
- static HMODULE _Gdi32_dll;
- int user_main(int argc, char** argv);
- // Doing "*((FARPROC*)&_name)" to work around not
- // being able to directly cast an lvalue.
- // (This is only really useful for isolated function pointers.)
- #define LOAD_FUNC(_pre, _post, _name, _dll) \
- *((FARPROC*)&_name) = loadFunction(_pre, _post, _dll)
- char _functionNameBuffer[256];
- FARPROC loadFunction(const char* prefix, const char* postfix, HMODULE dll){
- strnCpy(_functionNameBuffer, prefix, 255);
- strCat(_functionNameBuffer, postfix);
- _functionNameBuffer[255] = 0; // Just in case
- return GetProcAddress(dll, _functionNameBuffer);
- }
- // Postfixes string should look like: "nameA\0nameB\0nameC\0\0"
- bool loadFunctions(const char* prefix, const char* postfixes, HMODULE dll,
- FARPROC* funcs, int from)
- {
- while(*postfixes != '\0'){
- if(!( funcs[from++] = loadFunction(prefix, postfixes, dll) ))
- return false;
- // +1 to get past null-terminator
- postfixes += strnLen(postfixes, 256)+1;
- }
- return true;
- }
- //
- #define MAIN_ASSERT(_success, _code, _msg) \
- if(!(_success)){ \
- returnCode = (_code); \
- _printf("ERROR: %s\n", _msg); \
- goto _main_return; \
- }
- int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst,
- LPSTR lpszArg, int nCmdShow)
- {
- int err, returnCode = 0;
- // g++ doesn't seem to allow gotos that might maybe possibly step
- // over declarations, so the exit is near the top as a workaround
- if(0){ _main_return:
- // Seemingly due to WinMM being janky and old, in some environments the
- // program hangs indefinitely unless I explicitly exit the process like
- // this for some reason!
- ExitProcess((UINT)returnCode);
- return returnCode;
- }
- #ifdef _DEBUG
- f64 timeStart = timeGetSeconds();
- #endif /* _DEBUG */
- _Ole32_dll = LoadLibrary("Ole32.dll");
- MAIN_ASSERT(_Ole32_dll, -1, "failed to load \"Ole32.dll\"");
- _Winmm_dll = LoadLibrary("Winmm.dll");
- MAIN_ASSERT(_Winmm_dll, -2, "failed to load \"Winmm.dll\"");
- _User32_dll = LoadLibrary("User32.dll");
- MAIN_ASSERT(_User32_dll, -3, "failed to load \"User32.dll\"");
- _Gdi32_dll = LoadLibrary("Gdi32.dll");
- MAIN_ASSERT(_Gdi32_dll, -4, "failed to load \"Gdi32.dll\"");
- MAIN_ASSERT(loadFunctions("time", _Winmm_names_a, _Winmm_dll,
- _Winmm_func.ptrs, 0), -11,
- "failed to load \"time\" functions");
- MAIN_ASSERT(loadFunctions("waveOut", _Winmm_names_b, _Winmm_dll,
- _Winmm_func.ptrs, 2), -12,
- "failed to load \"waveOut\" functions");
- #define LOAD_USER32_FUNC(_prefix, _namegroup, _from, _errcode, _msg) \
- MAIN_ASSERT(loadFunctions(_prefix, _User32_names_##_namegroup, _User32_dll,\
- _User32_func.ptrs, (_from)), (_errcode), _msg)
- LOAD_USER32_FUNC("", a, 0, -13, "failed to load functions from User32");
- #define LOAD_GDI32_FUNC(_prefix, _namegroup, _from, _errcode, _msg) \
- MAIN_ASSERT(loadFunctions(_prefix, _Gdi32_names_##_namegroup, _Gdi32_dll,\
- _Gdi32_func.ptrs, (_from)), (_errcode), _msg)
- LOAD_GDI32_FUNC("", a, 0, -14, "failed to load functions from Gdi32");
- // Request 1ms minimum delay, to make timeSleep(16) reliably take 15 to 17 ms
- MAIN_ASSERT(!timeBeginPeriod(1), -50, "failed to set timer resolution");
- err = WaveOutInit(); // Also starts audio automatically
- MAIN_ASSERT(err == 0, err-100, "failed to initialize waveOut device");
- err = WindowInit(hThisInst);
- MAIN_ASSERT(err == 0, err-200, "failed to initialize window & pixel buffer");
- err = InputInit();
- MAIN_ASSERT(err == 0, err-300, "failed to initialize event system");
- #if defined(FONT_PICO8_USED) && defined(UNPACK_FONT_USED)
- unpack_font(font_pico8, font_pico8_packed);
- #endif
- #if defined(STDLIB_USED)
- srand(timeGetPerfCounter()&0xFFFFFFFF);
- #endif
- _printf("Initialized in: %.4f seconds!\n", timeGetSeconds()-timeStart);
- returnCode = user_main(0, nullptr); // TBD: provide main's arguments
- InputQuit();
- WindowQuit();
- WaveOutQuit();
- if(mem_getNumAllocations() > 0){
- _printf("# OF ALLOCATIONS (THIS NUMBER SHOULD BE 0!): %llu\n",
- mem_getNumAllocations());
- }
- // Make sure no unused parameter warnings occur for these
- (void)hThisInst;
- (void)hPrevInst;
- (void)lpszArg;
- (void)nCmdShow;
- goto _main_return;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\memory.cpp":
- #include <public_stuff.hpp>
- //turns something into a void**
- //(this makes some code here easier for me to read)
- #define VPP(_ptr_p) ((void**)(_ptr_p))
- size_t numAllocations = 0;
- void* mem_alloc(size_t size){
- void* newHeapMemory = malloc(size);
- if(newHeapMemory != nullptr) ++numAllocations;
- return newHeapMemory;
- }
- void mem_free(void* ptr_p){
- if(VPP(ptr_p) != nullptr && *VPP(ptr_p) != nullptr){
- --numAllocations;
- free(*VPP(ptr_p));
- *VPP(ptr_p) = nullptr;
- }
- }
- void* mem_realloc(void* ptr_p, size_t newSize){
- void* ptr_new = nullptr;
- if(VPP(ptr_p) != nullptr){
- ptr_new = realloc(*VPP(ptr_p), newSize);
- if(ptr_new != nullptr){
- if(*VPP(ptr_p) == nullptr) ++numAllocations;
- *VPP(ptr_p) = ptr_new;
- }
- }
- return ptr_new;
- }
- size_t mem_getNumAllocations(){ return numAllocations; }
- size_t mem_setNumAllocations(size_t value){ return numAllocations = value; }
- size_t mem_addNumAllocations(s32 amount){
- size_t originalState = numAllocations;
- numAllocations += amount;
- if(amount < 0 && numAllocations > originalState)
- numAllocations = 0; // Integer underflow protection
- return numAllocations;
- }
- // Currently just a wrapper, but now I can make my own implementation
- // whenever I want, without replacing every call to memset with it
- void* mem_set(void* ptr, s32 value, size_t size){
- if(ptr == nullptr) return nullptr; //now it's safe to pass nullptr :D
- return memSet(ptr, value, size);
- }
- void* mem_copy(void* destination, const void* source, size_t size){
- if(!destination || !source) return destination;
- return memCpy(destination, source, size);
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\misc.cpp":
- #include <windows.h>
- #include <public_stuff.hpp>
- extern HWND win;
- u32 showMessageBox(const char* text, const char* title,
- u32 type, u32 defaultButton)
- {
- if(!text ) text = "";
- if(!title) title = "";
- defaultButton = (defaultButton&3)<<8; //0x000 -> 0x300
- return MessageBoxA(win, text, title, type|defaultButton);
- }
- u64 timeGetPerfCounter(){
- u64 result;
- QueryPerformanceCounter((LARGE_INTEGER*)&result);
- return result;
- }
- static u64 perfFreq = 0;
- u64 timeGetPerfFreq(){
- if(!perfFreq)
- QueryPerformanceFrequency((LARGE_INTEGER*)&perfFreq);
- return perfFreq;
- }
- f64 timeGetSeconds(){
- return (f64)timeGetPerfCounter()/timeGetPerfFreq();
- }
- void timeSleep(u32 milliseconds){
- Sleep((DWORD)milliseconds);
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\std.cpp":
- #include <public_stuff.hpp>
- #ifndef STDLIB_USED
- void* memSet(void* dst, int val, size_t len){
- if(!len) return dst;
- u8* dst8 = (u8*)dst;
- u8 val8 = (u8 )val;
- size_t remainder = len%sizeof(u32);
- len -= remainder;
- while((remainder--) > 0)
- *(dst8++) = val8;
- u32* dst32 = (u32*)dst8;
- u32 val32 = val8;
- val32 |= val32<< 8;
- val32 |= val32<<16;
- len /= sizeof(u32);
- while((len--) > 0)
- *(dst32++) = val32;
- return dst;
- }
- void* memCpy(void* dst, const void* src, size_t len){
- if(!len) return dst;
- u8* dst8 = (u8*)dst;
- u8* src8 = (u8*)src;
- while(len--) *(dst8++) = *(src8++);
- return dst;
- }
- // (len_max does not include null-terminator!)
- // (If !len_max, call is analogous to str(not n)len)
- size_t strnLen(const char* str, size_t len_max){
- size_t len = 0;
- if(!len_max){
- for(; str[len]; ++len);
- } else {
- for(; str[len] && len<len_max; ++len);
- }
- return len;
- }
- char* strnCpy(char* str_dst, const char* str_src, size_t len_max){
- char* _str_dst = str_dst; // Copy original state of str_dst
- if(!len_max){
- while((*str_dst++ = *str_src++));
- } else {
- size_t i = 0;
- while(i++ != len_max && (*str_dst++ = *str_src++));
- }
- *str_dst = 0; // Null-terminator
- return _str_dst;
- }
- char* strCat(char* dst, const char* src){
- char* dst_start = dst;
- while(*dst) ++dst;
- while((*dst++ = *src++));
- return dst_start;
- }
- // (len_max does not include null-terminator!)
- // (If !len_max, call is analogous to str(not n)cmp)
- s32 strnCmp(const char* str_a, const char* str_b, size_t len_max){
- if(!len_max){
- while(*str_a && (*str_a == *str_b))
- ++str_a, ++str_b;
- } else {
- --len_max;
- while(*str_a && (*str_a == *str_b) && len_max)
- ++str_a, ++str_b, --len_max;
- }
- return (*(const u8*)str_a) - (*(const u8*)str_b);
- }
- #define sinf_bhaskara_fmod(_x, _y) ( (_x) - ( (s64)((_x)/(_y)) * (_y) ) )
- // Custom sinf implementation; a little under 2% error iirc
- // (You could switch floats with doubles here, though the approximation's
- // lack of precision prevents that switch from being all that useful.)
- f32 sinF(f32 x){
- // Keep x within the domain of >=0 -> <pi,
- // while preserving relevant info
- bool negative = x<0.0f;
- if(x < 0.0f) x = -x; //x = fabsf(x);
- x = sinf_bhaskara_fmod(x, M_2PIf); //x %= 2pi
- // 'If original value of x%(2pi) is between _ -> _, returned value will be _':
- //>-2pi -> <=-pi, >=0
- //> -pi -> <= 0, <=0
- //>= 0 -> < pi, >=0
- //>= pi -> < 2pi, <=0
- negative ^= x>=M_PIf;
- if(x >= M_PIf) x -= M_PIf; //x %= pi
- // Uses Bhaskara I's sine approximation formula
- f32 result = 16.0f*x * (M_PIf-x);
- result /= 5.0f*M_PIf*M_PIf - result*0.25f;
- return (negative) ? -result : result;
- }
- #endif /* STDLIB_USED */
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\win32\video.cpp":
- #include "_WindowProc.hpp"
- // Header + 256 colors (AKA 8-bpp)
- static u8 _canvas_info[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256];
- static BITMAPINFO* canvas_info = (BITMAPINFO*)_canvas_info; // Lol
- static HBITMAP canvas;
- static HDC canvas_dc;
- Color8* pixels = nullptr;
- Color24* pixels_palette = (Color24*)canvas_info->bmiColors;
- static const char win_class_name[] = "Normal Resizable Window";
- static WNDCLASS win_class = {0};
- HWND win;
- bool win_closed = false;
- Point2d win_size = {CANVAS_W*WINDOW_RESMUL, CANVAS_H*WINDOW_RESMUL};
- // Canvas is 8-bpp at (by default) 256x144
- static inline bool InitCanvas(){
- canvas_info->bmiHeader.biSize = sizeof(canvas_info->bmiHeader);
- canvas_info->bmiHeader.biWidth = CANVAS_W;
- canvas_info->bmiHeader.biHeight = CANVAS_H;
- canvas_info->bmiHeader.biPlanes = 1;
- canvas_info->bmiHeader.biBitCount = 8; // 8-bpp
- canvas_info->bmiHeader.biCompression = BI_RGB;
- canvas_info->bmiHeader.biSizeImage = 0; // Valid for uncompressed bitmaps
- canvas_info->bmiHeader.biXPelsPerMeter = 3780; // Approximately 96dpi
- canvas_info->bmiHeader.biYPelsPerMeter = 3780;
- canvas_info->bmiHeader.biClrUsed = 0; // Maximum # of colors used (256)
- canvas_info->bmiHeader.biClrImportant = 0; // All colors are important
- // Generate 256-color palette
- for(int i=0; i<256; ++i){
- Color8 color = i; // Sets the ".v" member to i
- #define conv_b(_c) ((_c)*85)
- #define conv_rg(_c) ((u8)( (255.0f*((f32)(_c)/7)) + 0.5f ))
- pixels_palette[i].b = conv_b(color.b);
- pixels_palette[i].g = conv_rg(color.g);
- pixels_palette[i].r = conv_rg(color.r);
- pixels_palette[i]._ = 0;
- }
- // Generate grayscale portion
- // (This does overwrite some colors, but it's worth it, trust :)
- for(int i=0; i<256; i+=16){
- pixels_palette[i].b = i;
- pixels_palette[i].g = i;
- pixels_palette[i].r = i;
- }
- canvas_dc = CreateCompatibleDC(nullptr);
- if(!canvas_dc) return false;
- canvas = CreateDIBSection(nullptr, canvas_info, DIB_RGB_COLORS,
- (void**)&pixels, nullptr, 0);
- if(!canvas) return false;
- HGDIOBJ result = SelectObject(canvas_dc, canvas);
- return result != nullptr && result != HGDI_ERROR;
- }
- LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); // Forward declaration
- int WindowInit(HINSTANCE hThisInst){
- if(!InitCanvas()) return -1;
- win_class.style = CS_DBLCLKS; // Allow double clicks
- win_class.lpfnWndProc = WindowProc;
- win_class.hInstance = hThisInst;
- win_class.hCursor = LoadCursorA(nullptr, IDC_ARROW);
- win_class.lpszClassName = win_class_name;
- RegisterClassA(&win_class);
- win = CreateWindowExA(WS_EX_LEFT, win_class_name, WINDOW_NAME,
- WS_OVERLAPPEDWINDOW | WS_VISIBLE,
- CW_USEDEFAULT, CW_USEDEFAULT, // X, Y
- win_size.x, win_size.y, // W, H
- nullptr, nullptr, hThisInst, nullptr);
- if(!win) return -2;
- return 0;
- }
- void WindowQuit(){} // TBD (actually it may not be necessary at all, but w/e)
- void canvas_present(bool immediate){
- // Marks the canvas to be redrawn.
- // If this is not called, Win32 will assume no redraw
- // is being requested at all!
- InvalidateRect(win, nullptr, false);
- // Win32 technically will redraw it automatically when it needs to (assuming
- // InvalidateRect was called), but doing this will send a paint message
- // immediately.
- //
- // This is useful if you end up updating the canvas slightly
- // after it would've updated normally, potentially making it
- // look like a frame was skipped (because it was :)
- if(immediate) UpdateWindow(win);
- }
- // Yep, this is just a single memset that sets every byte to fill_color
- void canvas_clear(Color8 fill_color){
- memSet(&pixels[0].v, fill_color.v, CANVAS_W*CANVAS_H);
- }
- void close_window(){
- if(win_closed) return;
- DeleteObject(canvas);
- DeleteDC(canvas_dc);
- DestroyWindow(win);
- // These might not be necessary, due to the way uninitialization is set up
- //UnregisterClassA
- //DeleteCriticalSection
- win_closed = true;
- }
- #ifndef INT_MAX
- #define INT_MAX 2147483647
- #endif /* INT_MAX */
- bool update_palette(u32 first_color, u32 num_colors,
- const Color24* new_palette)
- {
- // Return early if any of the parameters are out of range
- if(new_palette == nullptr) return false;
- if(first_color > INT_MAX) return false;
- if(num_colors > INT_MAX) return false;
- first_color = MIN(first_color, 255);
- num_colors = MIN(num_colors , 256);
- if((first_color+num_colors) > 256)
- num_colors = 256-first_color;
- bool success = SetDIBColorTable(canvas_dc, first_color, num_colors,
- (const RGBQUAD*)new_palette) != 0;
- return success;
- }
- Point2d get_win_size(){
- return win_size;
- }
- extern CRITICAL_SECTION events_lock;
- extern Event events_queue[65536];
- extern u16 events_next;
- extern u16 events_end;
- extern Event_Key_Mod key_mods;
- extern bool key_states[256];
- extern Point2d mouse_position;
- extern bool mouse_was_moved_before;
- extern bool cursor_trapped;
- extern bool cursor_hidden;
- static union {
- RECT v;
- struct { POINT topLeft, bottomRight; };
- } clipRect;
- #include <hidusage.h>
- static RAWINPUTDEVICE rid = {
- .usUsagePage = HID_USAGE_PAGE_GENERIC, // Generic desktop controls
- .usUsage = HID_USAGE_GENERIC_MOUSE, // Mouse
- //.dwFlags = RIDEV_NOLEGACY,
- .hwndTarget = nullptr,
- };
- MOUSE_ButtonStates mbutton_states; // Persistent mouse state
- static void update_cursor_clip(){
- LPRECT clipRect_p = nullptr;
- if(cursor_trapped){
- clipRect_p = &clipRect.v;
- GetClientRect(win, clipRect_p);
- ClientToScreen(win, &clipRect.topLeft);
- ClientToScreen(win, &clipRect.bottomRight);
- }
- ClipCursor(clipRect_p);
- }
- static void trap_cursor(bool enable, bool condition){
- if(!condition) return;
- if(enable != cursor_hidden){
- cursor_hidden = enable;
- ShowCursor(!enable); // Enabling makes it hidden, hence the '!'
- }
- if(enable != cursor_trapped){
- cursor_trapped = enable;
- update_cursor_clip();
- if(enable) SetCapture(win);
- else ReleaseCapture();
- if(enable && rid.hwndTarget == nullptr){
- rid.dwFlags = RIDEV_NOLEGACY;
- rid.hwndTarget = win;
- if(!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))){
- _printf("ERROR: failed to register mouse as a raw input device! "
- "(error code = 0x%08X)\n", (u32)GetLastError());
- rid.hwndTarget = nullptr;
- }
- }
- else if(!enable && rid.hwndTarget != nullptr){
- HWND hwndOld = rid.hwndTarget;
- rid.dwFlags = RIDEV_REMOVE;
- rid.hwndTarget = nullptr;
- if(!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))){
- _printf("ERROR: failed to unregister mouse as a raw input device! "
- "(error code = 0x%08X)\n", (u32)GetLastError());
- rid.hwndTarget = hwndOld;
- }
- }
- Event evt;
- memSet(&evt, 0, sizeof(Event));
- evt.type = (enable) ? EVENT_CURSOR_TRAPPED : EVENT_CURSOR_RELEASED;
- AddToEventQueue(evt);
- }
- }
- static void add_event(Event& evt){
- // I pray AddToEventQueue never fails ever
- if(!AddToEventQueue(evt)){ _printf("ERROR: EVENT QUEUE IS FULL!!!\n"); }
- }
- // flag_which must be one of the "DOWN" flags (Ex.: RI_MOUSE_BUTTON_1_DOWN)
- // (Also, timestamp is assumed to have already been set inside evt!)
- static void send_raw_mbutton_press(Event& evt, u16 flags,
- u16 flag_which, u8 button_which)
- {
- if(flags&flag_which){ // Mouse down
- evt.type=EVENT_MOUSE_DOWN;
- evt.mouse.pressed = true;
- u8 button_old = evt.mouse.button;
- evt.mouse.button = button_which;
- add_event(evt);
- evt.mouse.button = button_old|button_which;
- mbutton_states.value |= button_which;
- }
- if(flags&(flag_which<<1)){ // Mouse up
- evt.type=EVENT_MOUSE_UP;
- evt.mouse.pressed = false;
- u8 button_old = evt.mouse.button;
- evt.mouse.button = button_which;
- add_event(evt);
- evt.mouse.button = button_old&(~button_which);
- mbutton_states.value &= ~button_which;
- }
- }
- LRESULT CALLBACK WindowProc(HWND winHandle, UINT message,
- WPARAM wParam, LPARAM lParam)
- {
- LRESULT returnResult = 0;
- Event evt; // Will be populated by any call to HANDLE_EVENT_<?>()...
- memSet(&evt, 0, sizeof(Event)); //(this is done BEFORE setting anything)
- evt.type = EVENT_COMMON; //...otherwise it will stay common
- bool switchBool = false; // Multipurpose
- bool switchBool2 = true; // ^^(switchBool2 intentionally initialized to true)
- u32 switchFlags = 0; // ^^
- bool addEvent = true;
- switch(message){
- case WM_DESTROY:
- PostQuitMessage(0);
- case WM_QUIT:
- {
- evt.type = EVENT_QUIT;
- win_closed = true;
- trap_cursor(false, true);
- } break;
- case WM_GETMINMAXINFO: // For enforcing a minimum window size
- {
- u32 winStyleCurrent = GetWindowLongA(winHandle, GWL_STYLE );
- u32 winStyleExCurrent = GetWindowLongA(winHandle, GWL_EXSTYLE);
- Point2d winSizeAdjusted = CalculateWindowSize(CANVAS_W, CANVAS_H,
- winStyleCurrent,
- winStyleExCurrent);
- // Set the MINMAXINFO struct provided by lParam
- MINMAXINFO* mmi = (MINMAXINFO*)lParam;
- mmi->ptMinTrackSize.x = winSizeAdjusted.x;
- mmi->ptMinTrackSize.y = winSizeAdjusted.y;
- } break;
- case WM_SIZE: // Also redraw canvas if window is being resized
- win_size.x = LOWORD(lParam);
- win_size.y = HIWORD(lParam);
- update_cursor_clip();
- case WM_PAINT:
- {
- // Prepare window for painting
- PAINTSTRUCT paint;
- HDC winDevCtx = BeginPaint(winHandle, &paint); // Window device context
- Rect2d rectW = ConvertToKitRect(paint.rcPaint); // Window's rect
- // Copy canvas bitmap to window
- SetStretchBltMode(winDevCtx, COLORONCOLOR); // Nearest-neighbor
- StretchBlt(winDevCtx, rectW.x,rectW.y, rectW.w, rectW.h,
- canvas_dc, 0, 0, CANVAS_W, CANVAS_H, SRCCOPY);
- if(message == WM_SIZE)
- canvas_present(true);
- EndPaint(winHandle, &paint);
- } break;
- case WM_KILLFOCUS:
- {
- // If a key is released outside the client area, its key up message
- // is never sent, so here the key states are reset when unfocusing...
- // ...but first, send a key up event for every currently pressed key
- // Call QPC only once, since the events technically happen simultaneously
- QueryPerformanceCounter((LARGE_INTEGER*)&evt.key.timestamp);
- KEY_Params params = 0;
- params.currUnpressed = 1;
- for(u32 chr=0; chr<256; ++chr){
- // Send a KEY_UP only if the state was previously true
- if(key_states[chr] == true){
- params.scanCode = MapVirtualKeyA(chr, MAPVK_VK_TO_VSC);
- HANDLE_KEY_CHARUPDOWN(evt, false, chr, params, 0);
- add_event(evt);
- }
- }
- memSet(key_states, 0, sizeof(bool)*256); //NOW the states can be reset
- // Reset all key modifiers (except for toggles) too
- key_mods.all &= 0xff00; // Non-toggles are stored in low byte
- mbutton_states.value = 0; // Reset mouse states as well
- trap_cursor(false, true);
- } break;
- //EVENT_KEY_CHAR, EVENT_KEY_UP, EVENT_KEY_DOWN
- case WM_CHAR:
- case WM_SYSKEYUP:
- case WM_SYSKEYDOWN:
- case WM_KEYUP:
- case WM_KEYDOWN:
- case WM_SYSCHAR:
- {
- // Marshal relevant stuff to pass to event handler
- bool charEvent = message==WM_CHAR;
- u8 virtualKeyCode = (u8) wParam;
- KEY_Params params = (u32)lParam;
- // Set repeat flag
- params.repeatCount = (!params.currUnpressed) == true &&
- key_states[virtualKeyCode] == true;
- if(!charEvent){
- // (Uncomment the last part of the line below if issues occur
- // related to missed key press events or something!)
- bool keydown = message==WM_KEYDOWN;//|| message==WM_SYSKEYDOWN;
- bool pressed = !params.currUnpressed;
- key_states[virtualKeyCode] = pressed;
- // Update any relevant key modifiers
- switch(virtualKeyCode){
- case VKEY_SHIFT : { key_mods.lshift = key_mods.rshift = pressed; } break;
- case VKEY_CONTROL : { key_mods.lctrl = key_mods.rctrl = pressed; } break;
- case VKEY_ALT : { key_mods.lalt = key_mods.ralt = pressed; } break;
- case VKEY_LWIN : { key_mods.lgui = pressed; } break;
- case VKEY_RWIN : { key_mods.rgui = pressed; } break;
- case VKEY_NUMLOCK : if(keydown){ key_mods.numlock^=1; } break;
- case VKEY_CAPSLOCK : if(keydown){ key_mods.capslock^=1; } break;
- case VKEY_SCROLLOCK: if(keydown){ key_mods.scrollock^=1; } break;
- default:;
- }
- if( virtualKeyCode==VKEY_CTRL ) mbutton_states.ctrl = pressed;
- else if(virtualKeyCode==VKEY_SHIFT) mbutton_states.shift = pressed;
- bool repeat = params.repeatCount!=0;
- trap_cursor(!cursor_trapped,
- virtualKeyCode==VKEY_ESCAPE && keydown && !repeat);
- }
- HANDLE_KEY_CHARUPDOWN(evt, charEvent, virtualKeyCode,
- params, key_mods.all);
- } break;
- //EVENT_MOUSE_MOVED
- case WM_MOUSEMOVE:
- {
- // Get button states
- MOUSE_ButtonStates buttonStates;
- buttonStates.left = (wParam&MK_LBUTTON ) != 0;
- buttonStates.middle = (wParam&MK_MBUTTON ) != 0;
- buttonStates.right = (wParam&MK_RBUTTON ) != 0;
- buttonStates.x1 = (wParam&MK_XBUTTON1) != 0;
- buttonStates.x2 = (wParam&MK_XBUTTON2) != 0;
- buttonStates.ctrl = (wParam&MK_CONTROL ) != 0;
- buttonStates.shift = (wParam&MK_SHIFT ) != 0;
- // Get new mouse position
- Point2d mousePositionNew;
- mousePositionNew.x = GET_X_LPARAM(lParam);
- mousePositionNew.y = win_size.y-1 - GET_Y_LPARAM(lParam);
- // If this is the first instance of WINEVENT_MOUSE_MOVE,
- // there is no previous mouse position, so the delta should be 0 then
- if(!mouse_was_moved_before){
- mouse_position = mousePositionNew;
- mouse_was_moved_before = true;
- }
- HANDLE_MOUSE_MOVED(evt, buttonStates.value,
- mouse_position, mousePositionNew);
- mouse_position = mousePositionNew; // Set current position to new one
- } break;
- case WM_MOUSELEAVE:
- {
- // Indicates that the mouse will have yet to be moved inside client area
- mouse_was_moved_before = false;
- } break;
- //EVENT_MOUSE_HWHEEL, EVENT_MOUSE_VWHEEL
- case WM_MOUSEHWHEEL:
- case WM_MOUSEWHEEL:
- {
- bool verticalScroll = message==WM_MOUSEWHEEL;
- s16 scrollAmount = (s16)HIWORD(wParam);
- mbutton_states.left = (wParam&MK_LBUTTON ) != 0;
- mbutton_states.middle = (wParam&MK_MBUTTON ) != 0;
- mbutton_states.right = (wParam&MK_RBUTTON ) != 0;
- mbutton_states.x1 = (wParam&MK_XBUTTON1) != 0;
- mbutton_states.x2 = (wParam&MK_XBUTTON2) != 0;
- Point2d scrollMousePosition;
- scrollMousePosition.x = GET_X_LPARAM(lParam);
- scrollMousePosition.y = GET_Y_LPARAM(lParam);
- // The coordinates are, by default, relative to the screen
- // and not the window (for some reason)
- ScreenToClient(winHandle, (POINT*)&scrollMousePosition);
- HANDLE_MOUSE_HVWHEEL(evt, verticalScroll, scrollAmount,
- mbutton_states.value, scrollMousePosition);
- } break;
- //EVENT_MOUSE_UP, EVENT_MOUSE_DOWN
- //switchBool = evt.mouse.dblClick
- //switchBool2 = evt.mouse.pressed
- //switchFlags = evt.mouse.button
- case WM_LBUTTONDBLCLK: switchBool = true; goto _notLButtonUp;
- case WM_LBUTTONUP : switchBool2 = false; _notLButtonUp:
- case WM_LBUTTONDOWN : switchFlags |= MBUTTON_LEFT; goto _handleMouseClick;
- case WM_MBUTTONDBLCLK: switchBool = true; goto _notMButtonUp;
- case WM_MBUTTONUP : switchBool2 = false; _notMButtonUp:
- case WM_MBUTTONDOWN : switchFlags |=MBUTTON_MIDDLE; goto _handleMouseClick;
- case WM_RBUTTONDBLCLK: switchBool = true; goto _notRButtonUp;
- case WM_RBUTTONUP : switchBool2 = false; _notRButtonUp:
- case WM_RBUTTONDOWN : switchFlags |= MBUTTON_RIGHT; goto _handleMouseClick;
- case WM_XBUTTONDBLCLK: switchBool = true; goto _notXButtonUp;
- case WM_XBUTTONUP : switchBool2 = false; _notXButtonUp:
- case WM_XBUTTONDOWN : if(wParam & MK_XBUTTON1) switchFlags |= MBUTTON_X1;
- else switchFlags |= MBUTTON_X2;
- {
- _handleMouseClick:
- Point2d clickPosition;
- clickPosition.x = GET_X_LPARAM(lParam);
- clickPosition.y = GET_Y_LPARAM(lParam);
- bool pressed = switchBool2;
- bool doubleClick = switchBool;
- if(switchFlags&MBUTTON_LEFT ) mbutton_states.left = pressed;
- else if(switchFlags&MBUTTON_MIDDLE) mbutton_states.middle = pressed;
- else if(switchFlags&MBUTTON_RIGHT ) mbutton_states.right = pressed;
- else if(switchFlags&MBUTTON_X1 ) mbutton_states.x1 = pressed;
- else if(switchFlags&MBUTTON_X2 ) mbutton_states.x2 = pressed;
- HANDLE_MOUSE_UPDOWN(evt, clickPosition,
- mbutton_states.value, pressed, doubleClick);
- } break;
- case WM_INPUT:
- {
- // First call to query for the data's size
- UINT dataSize = 0;
- GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr,
- &dataSize, sizeof(RAWINPUTHEADER));
- RAWINPUT* data = (RAWINPUT*)alloca(dataSize);
- // Second call to get the actual data
- if(GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data,
- &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
- {
- const RAWMOUSE& mouse = data->data.mouse;
- if(data->header.dwType != RIM_TYPEMOUSE ||
- mouse.usFlags != MOUSE_MOVE_RELATIVE)
- {
- addEvent = false; break;
- }
- // Handle the actual event stuff
- {
- s32 _deltaWheel = mouse.usButtonData;
- if(_deltaWheel >= 32768) _deltaWheel -= 65536; // Unsigned-to-signed
- const u16 flags = mouse.usButtonFlags;
- const f32 deltaWheel = (f32)_deltaWheel/WHEEL_DELTA;
- const f32 deltaX = mouse.lLastX;
- const f32 deltaY = -mouse.lLastY; // GDI's bitmaps are *bottom-up*
- HANDLE_MOUSE_RAWINPUT(evt, flags, deltaWheel, deltaX, deltaY);
- }
- } else {
- _printf("ERROR: failed to get raw mouse input data! "
- "(error code = 0x%08X)\n", (u32)GetLastError());
- }
- // Any relevant raw input events will already have been sent by this point
- addEvent = false;
- } break;
- default: returnResult = DefWindowProcA(winHandle, message, wParam, lParam);
- }
- if(evt.type != EVENT_COMMON && evt.common.timestamp == 0 && addEvent)
- {
- // Idk how expensive QPC is, so I'll only call it if the event is valid
- QueryPerformanceCounter((LARGE_INTEGER*)&evt.common.timestamp);
- add_event(evt);
- }
- return returnResult;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement