Advertisement
Kitomas

work for 2025-04-25 (3/5)

Apr 25th, 2025
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 41.03 KB | None | 0 0
  1. /******************************************************************************/
  2. /******************************************************************************/
  3. //"gdi_maze_game_2025-04-25\src\win32\fileio.cpp":
  4. #include <windows.h>
  5.  
  6. #include <public_stuff.hpp>
  7.  
  8.  
  9.  
  10.  
  11.  
  12. BinaryData::BinaryData(const char* file_path)
  13.   : size( fileio_size(file_path) )
  14. {
  15.   data_void = nullptr;
  16.   if(size == FILEIO_SIZE_ERR) return;
  17.  
  18.   size_t _size;
  19.   data_void = fileio_read(file_path, &_size);
  20.  
  21.   if(_size != size){ // Just in case
  22.     mem_free(&data_void);
  23.   }
  24.  
  25. }
  26.  
  27.  
  28.  
  29.  
  30.  
  31. BinaryData::BinaryData(const void* src, size_t _size)
  32.   : size( _size )
  33. {
  34.   data_void = mem_alloc(size+1);
  35.   if(data_void == nullptr) return; // Failed to allocate memory for data
  36.  
  37.   // Adding +1 to the Alloc size makes it so that I can always
  38.   // make sure that the file's data is null-terminated, like so:
  39.   data_char[size] = 0;
  40.  
  41.   if(src != nullptr) mem_copy(data_char, src, size);
  42.  
  43. }
  44.  
  45.  
  46.  
  47.  
  48.  
  49. BinaryData::~BinaryData(){
  50.   mem_free(&data_void);
  51.  
  52. }
  53.  
  54.  
  55.  
  56.  
  57.  
  58. /******************************************************************************/
  59.  
  60.  
  61.  
  62.  
  63.  
  64. void* fileio_read(const char* file_path, size_t* dataSize_p){
  65.   size_t size = fileio_size(file_path);
  66.   if(size == FILEIO_SIZE_ERR) return nullptr;
  67.  
  68.  
  69.  
  70.   // Open the file
  71.   HANDLE fileHandle = CreateFileA(file_path, GENERIC_READ, FILE_SHARE_READ,
  72.                                   nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
  73.  
  74.   if(fileHandle == INVALID_HANDLE_VALUE) return nullptr; // Failed to open file
  75.  
  76.  
  77.  
  78.   // Allocate memory for file data
  79.   void* data = mem_alloc(size+1);
  80.  
  81.   if(data == nullptr){  CloseHandle(fileHandle);  return nullptr;  }
  82.  
  83.   ((char*)data)[size] = 0;
  84.  
  85.  
  86.  
  87.   DWORD bytesRead;
  88.   BOOL success = ReadFile(fileHandle, data, (DWORD)size, &bytesRead, nullptr);
  89.   CloseHandle(fileHandle);
  90.  
  91.   if(!success  ||  bytesRead < size)
  92.   {
  93.     mem_free(&data);
  94.     return nullptr;
  95.   }
  96.  
  97.  
  98.  
  99.   if(dataSize_p != nullptr) *dataSize_p = size;
  100.  
  101.   return data;
  102.  
  103. }
  104.  
  105.  
  106.  
  107.  
  108.  
  109. bool fileio_write(const char* file_path, const void* data,
  110.                   size_t data_size, bool append)
  111. {
  112.   if(file_path == nullptr  ||  data == nullptr) return false;
  113.  
  114.   // Writing files >=4GiB is currently unsupported!
  115.   // (If you really wanted to, I guess you could write files larger than 4 gigs
  116.   // with an overwrite and a series of appends, so that's always an option?)
  117.   if(data_size >= 0xFFFFFFFF) return false;
  118.  
  119.  
  120.  
  121.   DWORD accessMode, creationDisposition;
  122.  
  123.   if(append){ // Create or append to existing
  124.     accessMode          = FILE_APPEND_DATA;
  125.     creationDisposition = OPEN_ALWAYS;
  126.  
  127.   } else { // Create or overwrite existing
  128.     accessMode          = GENERIC_WRITE;
  129.     creationDisposition = CREATE_ALWAYS;
  130.  
  131.   }
  132.  
  133.  
  134.  
  135.   HANDLE fileHandle = CreateFileA(file_path, accessMode, 0, nullptr,
  136.                                   creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
  137.  
  138.   if(fileHandle == INVALID_HANDLE_VALUE) return false; // Failed to open file
  139.  
  140.  
  141.  
  142.   // If data_size *is* 0, then just create/open the file without writing to it
  143.  
  144.   if(data_size > 0){
  145.     DWORD bytesWritten;
  146.     BOOL success = WriteFile(fileHandle, data, (DWORD)data_size, &bytesWritten, nullptr);
  147.     CloseHandle(fileHandle);
  148.     if(!success) return false; // Failed to write to file
  149.     if(bytesWritten < data_size) return false; // Bytes read was less than data_size
  150.  
  151.   } else {
  152.     CloseHandle(fileHandle);
  153.  
  154.   }
  155.  
  156.  
  157.  
  158.   return true;
  159.  
  160. }
  161.  
  162.  
  163.  
  164.  
  165.  
  166. /******************************************************************************/
  167.  
  168.  
  169.  
  170.  
  171.  
  172. size_t fileio_size(const char* file_path){
  173.   if(file_path == nullptr) return FILEIO_SIZE_ERR;
  174.  
  175.   HANDLE fileHandle = CreateFileA(file_path, 0, FILE_SHARE_READ, nullptr,
  176.                                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,nullptr);
  177.  
  178.  
  179.  
  180.   if(fileHandle == INVALID_HANDLE_VALUE){
  181.     if(GetLastError() == ERROR_FILE_NOT_FOUND) return FILEIO_SIZE_ERR; // File not found
  182.     else                                       return FILEIO_SIZE_ERR; // Failed to open file
  183.   }
  184.  
  185.  
  186.  
  187.   LARGE_INTEGER fileSize;
  188.   BOOL success = GetFileSizeEx(fileHandle, &fileSize);
  189.   CloseHandle(fileHandle); // Close the file whether or not the query succeeds
  190.   if(!success) return FILEIO_SIZE_ERR;
  191.  
  192.   return (size_t)fileSize.QuadPart;
  193.  
  194. }
  195.  
  196.  
  197.  
  198.  
  199.  
  200. #define IS_READONLY(_attributes) \
  201.   ( (_attributes)&FILE_ATTRIBUTE_READONLY )
  202.  
  203. #define IS_DIRECTORY(_attributes) \
  204.   ( (_attributes)&FILE_ATTRIBUTE_DIRECTORY )
  205.  
  206. #define IS_READONLY_FILE(_attributes) \
  207.   ( IS_READONLY(_attributes) && !IS_DIRECTORY(_attributes) )
  208.  
  209. bool fileio_isreadonly(const char* file_path){
  210.   if(file_path == nullptr) return false; // file_path is not a real string
  211.  
  212.   DWORD fileAttributes = GetFileAttributesA(file_path);
  213.  
  214.   if(fileAttributes == INVALID_FILE_ATTRIBUTES){
  215.     if(GetLastError() != ERROR_FILE_NOT_FOUND) return false; // Failed to get file attributes
  216.     else                                       return false; // File doesn't exist
  217.  
  218.   } else if(IS_READONLY_FILE(fileAttributes)){
  219.     return true; // File is read-only
  220.  
  221.   } else {
  222.     return false; // File is not read-only
  223.  
  224.   }
  225.  
  226. }
  227.  
  228.  
  229.  
  230.  
  231.  
  232. #define IS_NORMAL_FILE(_attributes) \
  233.   ( !( IS_READONLY(_attributes) || IS_DIRECTORY(_attributes) ) )
  234.  
  235. bool fileio_exists(const char* file_path){
  236.   if(file_path == nullptr) return false; // file_path is not a real string
  237.  
  238.   DWORD fileAttributes = GetFileAttributesA(file_path);
  239.  
  240.   if(fileAttributes == INVALID_FILE_ATTRIBUTES){
  241.     if(GetLastError() != ERROR_FILE_NOT_FOUND) return false; // Failed to get file attributes
  242.     else                                       return false;
  243.  
  244.   } else if(IS_NORMAL_FILE(fileAttributes)){
  245.     return true;
  246.  
  247.   } else {
  248.     return false; // Path exists, but is not associated with a normal file
  249.  
  250.   }
  251.  
  252. }
  253.  
  254.  
  255.  
  256.  
  257.  
  258. bool fileio_delete(const char* file_path){
  259.   if(!fileio_exists(file_path)) return false; // File does not exist
  260.   if(!DeleteFileA(file_path)  ) return false; // Failed to delete file
  261.  
  262.   return true;
  263.  
  264. }
  265. /******************************************************************************/
  266. /******************************************************************************/
  267. //"gdi_maze_game_2025-04-25\src\win32\functions.cpp":
  268. #include <win32/audio.hpp>
  269. #include <win32/video.hpp>
  270.  
  271.  
  272.  
  273.  
  274.  
  275. _Winmm_func_t _Winmm_func;
  276.  
  277. char _Winmm_names_a[] = // prefix = "time"
  278.   "GetDevCaps\0"
  279.   "BeginPeriod\0"
  280.   "\0";
  281.  
  282. char _Winmm_names_b[] = // prefix = "waveOut"
  283. //"GetNumDevs\0"
  284.   "GetDevCapsA\0"
  285. //"GetVolume\0"
  286.   "SetVolume\0"
  287. //"GetErrorTextA\0"
  288.   "Open\0"
  289. //"Close\0"
  290.   "PrepareHeader\0"
  291. //"UnprepareHeader\0"
  292.   "Write\0"
  293.   "Pause\0"
  294. //"Restart\0"
  295. //"Reset\0"
  296. //"BreakLoop\0"
  297. //"GetPosition\0"
  298. //"GetPitch\0"
  299. //"SetPitch\0"
  300. //"GetPlaybackRate\0"
  301. //"SetPlaybackRate\0"
  302. //"GetID\0"
  303. //"Message\0"
  304.   "\0";
  305.  
  306.  
  307.  
  308.  
  309.  
  310. _User32_func_t _User32_func;
  311.  
  312. char _User32_names_a[] = // prefix = ""
  313.   "RegisterClassA\0"
  314.   "CreateWindowExA\0"
  315.   "DefWindowProcA\0"
  316.   "InvalidateRect\0"
  317.   "UpdateWindow\0"
  318.   "BeginPaint\0"
  319.   "EndPaint\0"
  320.   "PeekMessageA\0"
  321.   "DispatchMessageA\0"
  322.   "DestroyWindow\0"
  323. //"ReleaseDC\0"
  324. //"GetDC\0"
  325.   "PostQuitMessage\0"
  326.   "MessageBoxA\0"
  327.   "TranslateMessage\0"
  328.   "GetWindowLongA\0"
  329.   "AdjustWindowRectEx\0"
  330.   "LoadCursorA\0"
  331.   "MapVirtualKeyA\0"
  332. //"GetCursorPos\0"
  333.   "ScreenToClient\0"
  334.   "ShowCursor\0"
  335.   "ClipCursor\0"
  336.   "GetClientRect\0"
  337.   "ClientToScreen\0"
  338.   "SetCapture\0"
  339.   "ReleaseCapture\0"
  340.   "SetCursorPos\0"
  341.   "RegisterRawInputDevices\0"
  342.   "GetRawInputData\0"
  343.   "\0";
  344.  
  345.  
  346.  
  347. _Gdi32_func_t _Gdi32_func;
  348.  
  349. char _Gdi32_names_a[] = // prefix = ""
  350.   "CreateCompatibleDC\0"
  351.   "CreateDIBSection\0"
  352.   "SelectObject\0"
  353.   "DeleteObject\0"
  354.   "BitBlt\0"
  355.   "DeleteDC\0"
  356.   "StretchBlt\0"
  357.   "CreateCompatibleBitmap\0"
  358.   "SetStretchBltMode\0"
  359.   "SetDIBColorTable\0"
  360.   "CreateSolidBrush\0"
  361.   "\0";
  362. /******************************************************************************/
  363. /******************************************************************************/
  364. //"gdi_maze_game_2025-04-25\src\win32\input.cpp":
  365. #include <win32/input.hpp>
  366.  
  367. #include <public_stuff.hpp>
  368.  
  369.  
  370.  
  371. CRITICAL_SECTION events_lock;
  372. Event            events_queue[65536];
  373. u16              events_next = 0;
  374. u16              events_end  = 0;
  375.  
  376. Event_Key_Mod key_mods;
  377. bool          key_states[256] = {0};
  378.  
  379. Point2d mouse_position = {0};
  380. bool    mouse_was_moved_before = false;
  381.  
  382. bool cursor_trapped = false;
  383. bool cursor_hidden = false;
  384.  
  385.  
  386.  
  387.  
  388.  
  389. extern HWND win;
  390.  
  391. int InputInit(){
  392.   // This function has no failure condition
  393.   InitializeCriticalSectionAndSpinCount(&events_lock, 2048);
  394.  
  395.   return 0;
  396.  
  397. }
  398.  
  399.  
  400.  
  401. void InputQuit(){}
  402.  
  403.  
  404.  
  405.  
  406.  
  407. // Returns false if queue is full
  408. bool AddToEventQueue(Event& event){
  409.   EnterCriticalSection(&events_lock);
  410.  
  411.   bool success = false;
  412.  
  413.   if((events_end+1) != events_next){
  414.     events_queue[events_end++] = event;
  415.     success = true;
  416.  
  417.   }
  418.  
  419.   LeaveCriticalSection(&events_lock);
  420.  
  421.   return success;
  422.  
  423. }
  424.  
  425.  
  426.  
  427. // Returns a EVENT_NULL event if queue is empty
  428. Event RemoveFromEventQueue(){
  429.   EnterCriticalSection(&events_lock);
  430.  
  431.   Event event;
  432.   event.type = EVENT_NULL;
  433.  
  434.   if(events_next != events_end)
  435.     event = events_queue[events_next++];
  436.  
  437.   LeaveCriticalSection(&events_lock);
  438.  
  439.   return event;
  440.  
  441. }
  442.  
  443.  
  444.  
  445.  
  446.  
  447. // Calling this exclusively in the main thread is recommended
  448. bool pollEvent(Event* event_p){
  449.   // Take one event off the current event queue and set it to *event_p
  450.   EnterCriticalSection(&events_lock);
  451.  
  452.   Event event = RemoveFromEventQueue();
  453.  
  454.   LeaveCriticalSection(&events_lock);
  455.  
  456.  
  457.  
  458.   // If previous event queue is now empty, process any pending window messages,
  459.   // while adding any events generated by WindowProc to the event queue
  460.   if(events_next == events_end){
  461.     MSG message;
  462.  
  463.     while(PeekMessageA(&message, nullptr, 0, 0, PM_REMOVE)){
  464.       TranslateMessage(&message);
  465.       DispatchMessageA(&message);
  466.  
  467.     }
  468.  
  469.   }
  470.  
  471.  
  472.  
  473.   if(event_p != nullptr) *event_p = event;
  474.  
  475.   return event.type != EVENT_NULL;
  476.  
  477. }
  478.  
  479.  
  480.  
  481.  
  482.  
  483. bool is_cursor_trapped(){
  484.   return cursor_trapped; // Yep, that's all it does
  485.  
  486. }
  487. /******************************************************************************/
  488. /******************************************************************************/
  489. //"gdi_maze_game_2025-04-25\src\win32\main.cpp":
  490. #include <win32/audio.hpp>
  491. #include <win32/video.hpp>
  492. #include <win32/input.hpp>
  493.  
  494. #include <public_stuff.hpp>
  495.  
  496.  
  497.  
  498.  
  499.  
  500. static HMODULE _Ole32_dll;
  501. static HMODULE _Winmm_dll;
  502. static HMODULE _User32_dll;
  503. static HMODULE _Gdi32_dll;
  504.  
  505.  
  506.  
  507.  
  508.  
  509. int user_main(int argc, char** argv);
  510.  
  511.  
  512.  
  513.  
  514.  
  515. // Doing "*((FARPROC*)&_name)" to work around not
  516. // being able to directly cast an lvalue.
  517. // (This is only really useful for isolated function pointers.)
  518. #define LOAD_FUNC(_pre, _post, _name, _dll) \
  519.   *((FARPROC*)&_name) = loadFunction(_pre, _post, _dll)
  520.  
  521.  
  522.  
  523. char _functionNameBuffer[256];
  524.  
  525. FARPROC loadFunction(const char* prefix, const char* postfix, HMODULE dll){
  526.   strnCpy(_functionNameBuffer, prefix, 255);
  527.   strCat(_functionNameBuffer, postfix);
  528.  
  529.   _functionNameBuffer[255] = 0; // Just in case
  530.  
  531.   return GetProcAddress(dll, _functionNameBuffer);
  532.  
  533. }
  534.  
  535.  
  536.  
  537. // Postfixes string should look like: "nameA\0nameB\0nameC\0\0"
  538. bool loadFunctions(const char* prefix, const char* postfixes, HMODULE dll,
  539.                    FARPROC* funcs, int from)
  540. {
  541.   while(*postfixes != '\0'){
  542.     if(!( funcs[from++] = loadFunction(prefix, postfixes, dll) ))
  543.       return false;
  544.  
  545.     // +1 to get past null-terminator
  546.     postfixes += strnLen(postfixes, 256)+1;
  547.  
  548.   }
  549.  
  550.   return true;
  551.  
  552. }
  553.  
  554.  
  555.  
  556.  
  557.  
  558. //
  559.  
  560.  
  561.  
  562.  
  563.  
  564. #define MAIN_ASSERT(_success, _code, _msg) \
  565.   if(!(_success)){ \
  566.     returnCode = (_code); \
  567.     _printf("ERROR: %s\n", _msg); \
  568.     goto _main_return; \
  569.   }
  570.  
  571. int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst,
  572.                    LPSTR     lpszArg,   int       nCmdShow)
  573. {
  574.   int err, returnCode = 0;
  575.  
  576.   // g++ doesn't seem to allow gotos that might maybe possibly step
  577.   // over declarations, so the exit is near the top as a workaround
  578.   if(0){ _main_return:
  579.     // Seemingly due to WinMM being janky and old, in some environments the
  580.     // program hangs indefinitely unless I explicitly exit the process like
  581.     // this for some reason!
  582.     ExitProcess((UINT)returnCode);
  583.     return returnCode;
  584.   }
  585.  
  586.   #ifdef _DEBUG
  587.     f64 timeStart = timeGetSeconds();
  588.   #endif /* _DEBUG */
  589.  
  590.  
  591.  
  592.  
  593.  
  594.   _Ole32_dll = LoadLibrary("Ole32.dll");
  595.   MAIN_ASSERT(_Ole32_dll, -1, "failed to load \"Ole32.dll\"");
  596.  
  597.   _Winmm_dll = LoadLibrary("Winmm.dll");
  598.   MAIN_ASSERT(_Winmm_dll, -2, "failed to load \"Winmm.dll\"");
  599.  
  600.   _User32_dll = LoadLibrary("User32.dll");
  601.   MAIN_ASSERT(_User32_dll, -3, "failed to load \"User32.dll\"");
  602.  
  603.   _Gdi32_dll = LoadLibrary("Gdi32.dll");
  604.   MAIN_ASSERT(_Gdi32_dll, -4, "failed to load \"Gdi32.dll\"");
  605.  
  606.  
  607.  
  608.  
  609.  
  610.   MAIN_ASSERT(loadFunctions("time", _Winmm_names_a, _Winmm_dll,
  611.                             _Winmm_func.ptrs, 0), -11,
  612.                             "failed to load \"time\" functions");
  613.  
  614.   MAIN_ASSERT(loadFunctions("waveOut", _Winmm_names_b, _Winmm_dll,
  615.                             _Winmm_func.ptrs, 2), -12,
  616.                             "failed to load \"waveOut\" functions");
  617.  
  618.  
  619.  
  620.   #define LOAD_USER32_FUNC(_prefix, _namegroup, _from, _errcode, _msg)         \
  621.     MAIN_ASSERT(loadFunctions(_prefix, _User32_names_##_namegroup, _User32_dll,\
  622.                               _User32_func.ptrs, (_from)), (_errcode), _msg)
  623.  
  624.   LOAD_USER32_FUNC("", a, 0, -13, "failed to load functions from User32");
  625.  
  626.  
  627.  
  628.   #define LOAD_GDI32_FUNC(_prefix, _namegroup, _from, _errcode, _msg)        \
  629.     MAIN_ASSERT(loadFunctions(_prefix, _Gdi32_names_##_namegroup, _Gdi32_dll,\
  630.                               _Gdi32_func.ptrs, (_from)), (_errcode), _msg)
  631.  
  632.   LOAD_GDI32_FUNC("", a, 0, -14, "failed to load functions from Gdi32");
  633.  
  634.  
  635.  
  636.  
  637.  
  638.   // Request 1ms minimum delay, to make timeSleep(16) reliably take 15 to 17 ms
  639.   MAIN_ASSERT(!timeBeginPeriod(1), -50, "failed to set timer resolution");
  640.  
  641.  
  642.  
  643.  
  644.  
  645.   err = WaveOutInit(); // Also starts audio automatically
  646.   MAIN_ASSERT(err == 0, err-100, "failed to initialize waveOut device");
  647.  
  648.   err = WindowInit(hThisInst);
  649.   MAIN_ASSERT(err == 0, err-200, "failed to initialize window & pixel buffer");
  650.  
  651.   err = InputInit();
  652.   MAIN_ASSERT(err == 0, err-300, "failed to initialize event system");
  653.  
  654.  
  655.  
  656.   #if defined(FONT_PICO8_USED) && defined(UNPACK_FONT_USED)
  657.     unpack_font(font_pico8, font_pico8_packed);
  658.   #endif
  659.  
  660.   #if defined(STDLIB_USED)
  661.     srand(timeGetPerfCounter()&0xFFFFFFFF);
  662.   #endif
  663.  
  664.   _printf("Initialized in: %.4f seconds!\n", timeGetSeconds()-timeStart);
  665.  
  666.   returnCode = user_main(0, nullptr); // TBD: provide main's arguments
  667.  
  668.  
  669.  
  670.   InputQuit();
  671.  
  672.   WindowQuit();
  673.  
  674.   WaveOutQuit();
  675.  
  676.  
  677.  
  678.   if(mem_getNumAllocations() > 0){
  679.     _printf("# OF ALLOCATIONS (THIS NUMBER SHOULD BE 0!): %llu\n",
  680.             mem_getNumAllocations());
  681.   }
  682.  
  683.  
  684.  
  685.   // Make sure no unused parameter warnings occur for these
  686.   (void)hThisInst;
  687.   (void)hPrevInst;
  688.   (void)lpszArg;
  689.   (void)nCmdShow;
  690.  
  691.   goto _main_return;
  692.  
  693. }
  694. /******************************************************************************/
  695. /******************************************************************************/
  696. //"gdi_maze_game_2025-04-25\src\win32\memory.cpp":
  697. #include <public_stuff.hpp>
  698.  
  699.  
  700. //turns something into a void**
  701.  //(this makes some code here easier for me to read)
  702. #define VPP(_ptr_p) ((void**)(_ptr_p))
  703.  
  704.  
  705. size_t numAllocations = 0;
  706.  
  707.  
  708.  
  709.  
  710.  
  711. void* mem_alloc(size_t size){
  712.   void* newHeapMemory = malloc(size);
  713.   if(newHeapMemory != nullptr) ++numAllocations;
  714.   return newHeapMemory;
  715.  
  716. }
  717.  
  718.  
  719.  
  720.  
  721.  
  722. void mem_free(void* ptr_p){
  723.   if(VPP(ptr_p) != nullptr  &&  *VPP(ptr_p) != nullptr){
  724.     --numAllocations;
  725.     free(*VPP(ptr_p));
  726.     *VPP(ptr_p) = nullptr;
  727.  
  728.   }
  729.  
  730. }
  731.  
  732.  
  733.  
  734.  
  735. void* mem_realloc(void* ptr_p, size_t newSize){
  736.   void* ptr_new = nullptr;
  737.  
  738.   if(VPP(ptr_p) != nullptr){
  739.     ptr_new = realloc(*VPP(ptr_p), newSize);
  740.  
  741.     if(ptr_new != nullptr){
  742.       if(*VPP(ptr_p) == nullptr) ++numAllocations;
  743.       *VPP(ptr_p) = ptr_new;
  744.  
  745.     }
  746.  
  747.   }
  748.  
  749.   return ptr_new;
  750.  
  751. }
  752.  
  753.  
  754.  
  755.  
  756.  
  757. size_t mem_getNumAllocations(){ return numAllocations; }
  758.  
  759. size_t mem_setNumAllocations(size_t value){ return numAllocations = value; }
  760.  
  761. size_t mem_addNumAllocations(s32 amount){
  762.   size_t originalState = numAllocations;
  763.  
  764.   numAllocations += amount;
  765.  
  766.   if(amount < 0  &&  numAllocations > originalState)
  767.     numAllocations = 0; // Integer underflow protection
  768.  
  769.   return numAllocations;
  770.  
  771. }
  772.  
  773.  
  774.  
  775.  
  776.  
  777. // Currently just a wrapper, but now I can make my own implementation
  778. // whenever I want, without replacing every call to memset with it
  779. void* mem_set(void* ptr, s32 value, size_t size){
  780.   if(ptr == nullptr) return nullptr; //now it's safe to pass nullptr :D
  781.   return memSet(ptr, value, size);
  782.  
  783. }
  784.  
  785.  
  786.  
  787.  
  788.  
  789. void* mem_copy(void* destination, const void* source, size_t size){
  790.   if(!destination || !source) return destination;
  791.   return memCpy(destination, source, size);
  792.  
  793. }
  794. /******************************************************************************/
  795. /******************************************************************************/
  796. //"gdi_maze_game_2025-04-25\src\win32\misc.cpp":
  797. #include <windows.h>
  798.  
  799. #include <public_stuff.hpp>
  800.  
  801.  
  802.  
  803.  
  804.  
  805. extern HWND win;
  806.  
  807. u32 showMessageBox(const char* text, const char* title,
  808.                    u32 type, u32 defaultButton)
  809. {
  810.   if(!text ) text  = "";
  811.   if(!title) title = "";
  812.  
  813.   defaultButton = (defaultButton&3)<<8; //0x000 -> 0x300
  814.  
  815.   return MessageBoxA(win, text, title, type|defaultButton);
  816.  
  817. }
  818.  
  819.  
  820.  
  821.  
  822.  
  823. u64 timeGetPerfCounter(){
  824.   u64 result;
  825.   QueryPerformanceCounter((LARGE_INTEGER*)&result);
  826.   return result;
  827.  
  828. }
  829.  
  830.  
  831.  
  832. static u64 perfFreq = 0;
  833.  
  834. u64 timeGetPerfFreq(){
  835.   if(!perfFreq)
  836.     QueryPerformanceFrequency((LARGE_INTEGER*)&perfFreq);
  837.  
  838.   return perfFreq;
  839.  
  840. }
  841.  
  842.  
  843.  
  844. f64 timeGetSeconds(){
  845.   return (f64)timeGetPerfCounter()/timeGetPerfFreq();
  846.  
  847. }
  848.  
  849.  
  850.  
  851. void timeSleep(u32 milliseconds){
  852.   Sleep((DWORD)milliseconds);
  853.  
  854. }
  855. /******************************************************************************/
  856. /******************************************************************************/
  857. //"gdi_maze_game_2025-04-25\src\win32\std.cpp":
  858. #include <public_stuff.hpp>
  859.  
  860. #ifndef STDLIB_USED
  861.  
  862.  
  863.  
  864.  
  865.  
  866. void* memSet(void* dst, int val, size_t len){
  867.   if(!len) return dst;
  868.  
  869.   u8* dst8 = (u8*)dst;
  870.   u8  val8 = (u8 )val;
  871.  
  872.  
  873.   size_t remainder = len%sizeof(u32);
  874.   len -= remainder;
  875.  
  876.   while((remainder--) > 0)
  877.     *(dst8++) = val8;
  878.  
  879.  
  880.   u32* dst32 = (u32*)dst8;
  881.   u32  val32 = val8;
  882.  
  883.   val32 |= val32<< 8;
  884.   val32 |= val32<<16;
  885.  
  886.   len /= sizeof(u32);
  887.  
  888.   while((len--) > 0)
  889.     *(dst32++) = val32;
  890.  
  891.  
  892.   return dst;
  893.  
  894. }
  895.  
  896.  
  897.  
  898. void* memCpy(void* dst, const void* src, size_t len){
  899.   if(!len) return dst;
  900.  
  901.   u8* dst8 = (u8*)dst;
  902.   u8* src8 = (u8*)src;
  903.  
  904.   while(len--) *(dst8++) = *(src8++);
  905.  
  906.   return dst;
  907.  
  908. }
  909.  
  910.  
  911.  
  912.  
  913.  
  914. //  (len_max does not include null-terminator!)
  915. //  (If !len_max, call is analogous to str(not n)len)
  916. size_t strnLen(const char* str, size_t len_max){
  917.   size_t len = 0;
  918.  
  919.   if(!len_max){
  920.     for(; str[len]; ++len);
  921.  
  922.   } else {
  923.     for(; str[len] && len<len_max; ++len);
  924.  
  925.   }
  926.  
  927.   return len;
  928.  
  929. }
  930.  
  931.  
  932.  
  933. char* strnCpy(char* str_dst, const char* str_src, size_t len_max){
  934.   char* _str_dst = str_dst; // Copy original state of str_dst
  935.  
  936.   if(!len_max){
  937.     while((*str_dst++ = *str_src++));
  938.  
  939.   } else {
  940.     size_t i = 0;
  941.     while(i++ != len_max && (*str_dst++ = *str_src++));
  942.  
  943.   }
  944.  
  945.  
  946.   *str_dst = 0; // Null-terminator
  947.  
  948.   return _str_dst;
  949.  
  950. }
  951.  
  952.  
  953.  
  954. char* strCat(char* dst, const char* src){
  955.   char* dst_start = dst;
  956.  
  957.   while(*dst) ++dst;
  958.   while((*dst++ = *src++));
  959.  
  960.   return dst_start;
  961.  
  962. }
  963.  
  964.  
  965.  
  966. // (len_max does not include null-terminator!)
  967. // (If !len_max, call is analogous to str(not n)cmp)
  968. s32 strnCmp(const char* str_a, const char* str_b, size_t len_max){
  969.   if(!len_max){
  970.     while(*str_a && (*str_a == *str_b))
  971.       ++str_a, ++str_b;
  972.  
  973.   } else {
  974.     --len_max;
  975.     while(*str_a && (*str_a == *str_b) && len_max)
  976.       ++str_a, ++str_b, --len_max;
  977.  
  978.   }
  979.  
  980.   return (*(const u8*)str_a) - (*(const u8*)str_b);
  981.  
  982. }
  983.  
  984.  
  985.  
  986.  
  987.  
  988. #define sinf_bhaskara_fmod(_x, _y) (   (_x) - (  (s64)((_x)/(_y)) * (_y)  )   )
  989.  
  990. // Custom sinf implementation; a little under 2% error iirc
  991. // (You could switch floats with doubles here, though the approximation's
  992. // lack of precision prevents that switch from being all that useful.)
  993. f32 sinF(f32 x){
  994.   // Keep x within the domain of >=0  ->  <pi,
  995.   // while preserving relevant info
  996.   bool negative = x<0.0f;
  997.   if(x < 0.0f) x = -x; //x = fabsf(x);
  998.   x = sinf_bhaskara_fmod(x, M_2PIf); //x %= 2pi
  999.   // 'If original value of x%(2pi) is between _ -> _, returned value will be _':
  1000.    //>-2pi -> <=-pi,  >=0
  1001.    //> -pi -> <=  0,  <=0
  1002.    //>=  0 -> <  pi,  >=0
  1003.    //>= pi -> < 2pi,  <=0
  1004.   negative ^= x>=M_PIf;
  1005.   if(x >= M_PIf) x -= M_PIf; //x %= pi
  1006.  
  1007.   // Uses Bhaskara I's sine approximation formula
  1008.   f32 result = 16.0f*x * (M_PIf-x);
  1009.   result /= 5.0f*M_PIf*M_PIf - result*0.25f;
  1010.   return (negative) ? -result : result;
  1011.  
  1012. }
  1013.  
  1014.  
  1015.  
  1016.  
  1017.  
  1018. #endif /* STDLIB_USED */
  1019. /******************************************************************************/
  1020. /******************************************************************************/
  1021. //"gdi_maze_game_2025-04-25\src\win32\video.cpp":
  1022. #include "_WindowProc.hpp"
  1023.  
  1024.  
  1025.  
  1026.  
  1027.  
  1028. // Header + 256 colors (AKA 8-bpp)
  1029. static u8         _canvas_info[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256];
  1030. static BITMAPINFO* canvas_info = (BITMAPINFO*)_canvas_info; // Lol
  1031. static HBITMAP     canvas;
  1032. static HDC         canvas_dc;
  1033.  
  1034. Color8*  pixels         = nullptr;
  1035. Color24* pixels_palette = (Color24*)canvas_info->bmiColors;
  1036.  
  1037.  
  1038.  
  1039. static const char win_class_name[] = "Normal Resizable Window";
  1040. static WNDCLASS   win_class = {0};
  1041. HWND              win;
  1042. bool              win_closed = false;
  1043. Point2d           win_size = {CANVAS_W*WINDOW_RESMUL, CANVAS_H*WINDOW_RESMUL};
  1044.  
  1045.  
  1046.  
  1047.  
  1048.  
  1049. // Canvas is 8-bpp at (by default) 256x144
  1050. static inline bool InitCanvas(){
  1051.   canvas_info->bmiHeader.biSize = sizeof(canvas_info->bmiHeader);
  1052.  
  1053.   canvas_info->bmiHeader.biWidth  = CANVAS_W;
  1054.   canvas_info->bmiHeader.biHeight = CANVAS_H;
  1055.  
  1056.   canvas_info->bmiHeader.biPlanes      = 1;
  1057.   canvas_info->bmiHeader.biBitCount    = 8; // 8-bpp
  1058.   canvas_info->bmiHeader.biCompression = BI_RGB;
  1059.   canvas_info->bmiHeader.biSizeImage   = 0; // Valid for uncompressed bitmaps
  1060.  
  1061.   canvas_info->bmiHeader.biXPelsPerMeter = 3780; // Approximately 96dpi
  1062.   canvas_info->bmiHeader.biYPelsPerMeter = 3780;
  1063.  
  1064.   canvas_info->bmiHeader.biClrUsed      = 0; // Maximum # of colors used (256)
  1065.   canvas_info->bmiHeader.biClrImportant = 0; // All colors are important
  1066.  
  1067.  
  1068.  
  1069.   // Generate 256-color palette
  1070.   for(int i=0; i<256; ++i){
  1071.     Color8 color = i; // Sets the ".v" member to i
  1072.  
  1073.     #define conv_b(_c) ((_c)*85)
  1074.     #define conv_rg(_c) ((u8)( (255.0f*((f32)(_c)/7)) + 0.5f ))
  1075.     pixels_palette[i].b = conv_b(color.b);
  1076.     pixels_palette[i].g = conv_rg(color.g);
  1077.     pixels_palette[i].r = conv_rg(color.r);
  1078.     pixels_palette[i]._ = 0;
  1079.  
  1080.   }
  1081.  
  1082.   // Generate grayscale portion
  1083.   // (This does overwrite some colors, but it's worth it, trust :)
  1084.   for(int i=0; i<256; i+=16){
  1085.     pixels_palette[i].b = i;
  1086.     pixels_palette[i].g = i;
  1087.     pixels_palette[i].r = i;
  1088.  
  1089.   }
  1090.  
  1091.  
  1092.  
  1093.   canvas_dc = CreateCompatibleDC(nullptr);
  1094.   if(!canvas_dc) return false;
  1095.  
  1096.  
  1097.  
  1098.   canvas = CreateDIBSection(nullptr, canvas_info, DIB_RGB_COLORS,
  1099.                             (void**)&pixels, nullptr, 0);
  1100.   if(!canvas) return false;
  1101.  
  1102.  
  1103.  
  1104.   HGDIOBJ result = SelectObject(canvas_dc, canvas);
  1105.  
  1106.   return result != nullptr  &&  result != HGDI_ERROR;
  1107.  
  1108. }
  1109.  
  1110.  
  1111.  
  1112.  
  1113.  
  1114. LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); // Forward declaration
  1115.  
  1116. int WindowInit(HINSTANCE hThisInst){
  1117.   if(!InitCanvas()) return -1;
  1118.  
  1119.   win_class.style         = CS_DBLCLKS; // Allow double clicks
  1120.   win_class.lpfnWndProc   = WindowProc;
  1121.   win_class.hInstance     = hThisInst;
  1122.   win_class.hCursor       = LoadCursorA(nullptr, IDC_ARROW);
  1123.   win_class.lpszClassName = win_class_name;
  1124.   RegisterClassA(&win_class);
  1125.  
  1126.   win = CreateWindowExA(WS_EX_LEFT, win_class_name, WINDOW_NAME,
  1127.                         WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  1128.                         CW_USEDEFAULT, CW_USEDEFAULT, // X, Y
  1129.                         win_size.x, win_size.y, // W, H
  1130.                         nullptr, nullptr, hThisInst, nullptr);
  1131.  
  1132.   if(!win) return -2;
  1133.  
  1134.   return 0;
  1135.  
  1136. }
  1137.  
  1138.  
  1139.  
  1140.  
  1141.  
  1142. void WindowQuit(){} // TBD (actually it may not be necessary at all, but w/e)
  1143.  
  1144.  
  1145.  
  1146.  
  1147.  
  1148. void canvas_present(bool immediate){
  1149.   // Marks the canvas to be redrawn.
  1150.   // If this is not called, Win32 will assume no redraw
  1151.   // is being requested at all!
  1152.   InvalidateRect(win, nullptr, false);
  1153.  
  1154.   // Win32 technically will redraw it automatically when it needs to (assuming
  1155.   // InvalidateRect was called), but doing this will send a paint message
  1156.   // immediately.
  1157.   //
  1158.   // This is useful if you end up updating the canvas slightly
  1159.   // after it would've updated normally, potentially making it
  1160.   // look like a frame was skipped (because it was :)
  1161.   if(immediate) UpdateWindow(win);
  1162.  
  1163. }
  1164.  
  1165.  
  1166.  
  1167. // Yep, this is just a single memset that sets every byte to fill_color
  1168. void canvas_clear(Color8 fill_color){
  1169.   memSet(&pixels[0].v, fill_color.v, CANVAS_W*CANVAS_H);
  1170.  
  1171. }
  1172.  
  1173.  
  1174.  
  1175. void close_window(){
  1176.   if(win_closed) return;
  1177.  
  1178.   DeleteObject(canvas);
  1179.   DeleteDC(canvas_dc);
  1180.   DestroyWindow(win);
  1181.   // These might not be necessary, due to the way uninitialization is set up
  1182.   //UnregisterClassA
  1183.   //DeleteCriticalSection
  1184.  
  1185.   win_closed = true;
  1186.  
  1187. }
  1188.  
  1189.  
  1190.  
  1191. #ifndef   INT_MAX
  1192. #define   INT_MAX 2147483647
  1193. #endif /* INT_MAX */
  1194.  
  1195. bool update_palette(u32 first_color, u32 num_colors,
  1196.                     const Color24* new_palette)
  1197. {
  1198.   // Return early if any of the parameters are out of range
  1199.   if(new_palette == nullptr) return false;
  1200.   if(first_color >  INT_MAX) return false;
  1201.   if(num_colors  >  INT_MAX) return false;
  1202.  
  1203.  
  1204.   first_color = MIN(first_color, 255);
  1205.   num_colors  = MIN(num_colors , 256);
  1206.  
  1207.   if((first_color+num_colors) > 256)
  1208.     num_colors = 256-first_color;
  1209.  
  1210.  
  1211.   bool success = SetDIBColorTable(canvas_dc, first_color, num_colors,
  1212.                                   (const RGBQUAD*)new_palette) != 0;
  1213.  
  1214.   return success;
  1215.  
  1216. }
  1217.  
  1218.  
  1219.  
  1220. Point2d get_win_size(){
  1221.   return win_size;
  1222.  
  1223. }
  1224.  
  1225.  
  1226.  
  1227.  
  1228.  
  1229. extern CRITICAL_SECTION events_lock;
  1230. extern Event            events_queue[65536];
  1231. extern u16              events_next;
  1232. extern u16              events_end;
  1233.  
  1234. extern Event_Key_Mod key_mods;
  1235. extern bool          key_states[256];
  1236.  
  1237. extern Point2d mouse_position;
  1238. extern bool    mouse_was_moved_before;
  1239.  
  1240. extern bool cursor_trapped;
  1241. extern bool cursor_hidden;
  1242.  
  1243. static union {
  1244.   RECT v;
  1245.   struct { POINT topLeft, bottomRight; };
  1246. } clipRect;
  1247.  
  1248. #include <hidusage.h>
  1249.  
  1250. static RAWINPUTDEVICE rid = {
  1251.   .usUsagePage = HID_USAGE_PAGE_GENERIC,  // Generic desktop controls
  1252.   .usUsage     = HID_USAGE_GENERIC_MOUSE, // Mouse
  1253. //.dwFlags     = RIDEV_NOLEGACY,
  1254.   .hwndTarget  = nullptr,
  1255. };
  1256.  
  1257. MOUSE_ButtonStates mbutton_states; // Persistent mouse state
  1258.  
  1259.  
  1260.  
  1261.  
  1262.  
  1263. static void update_cursor_clip(){
  1264.   LPRECT clipRect_p = nullptr;
  1265.  
  1266.   if(cursor_trapped){
  1267.     clipRect_p = &clipRect.v;
  1268.     GetClientRect(win, clipRect_p);
  1269.     ClientToScreen(win, &clipRect.topLeft);
  1270.     ClientToScreen(win, &clipRect.bottomRight);
  1271.  
  1272.   }
  1273.  
  1274.   ClipCursor(clipRect_p);
  1275.  
  1276. }
  1277.  
  1278.  
  1279.  
  1280.  
  1281.  
  1282. static void trap_cursor(bool enable, bool condition){
  1283.   if(!condition) return;
  1284.  
  1285.   if(enable != cursor_hidden){
  1286.     cursor_hidden = enable;
  1287.     ShowCursor(!enable); // Enabling makes it hidden, hence the '!'
  1288.   }
  1289.  
  1290.   if(enable != cursor_trapped){
  1291.     cursor_trapped = enable;
  1292.     update_cursor_clip();
  1293.  
  1294.     if(enable) SetCapture(win);
  1295.     else       ReleaseCapture();
  1296.  
  1297.     if(enable  &&  rid.hwndTarget == nullptr){
  1298.       rid.dwFlags    = RIDEV_NOLEGACY;
  1299.       rid.hwndTarget = win;
  1300.       if(!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))){
  1301.         _printf("ERROR: failed to register mouse as a raw input device! "
  1302.                 "(error code = 0x%08X)\n", (u32)GetLastError());
  1303.         rid.hwndTarget = nullptr;
  1304.       }
  1305.     }
  1306.     else if(!enable  &&  rid.hwndTarget != nullptr){
  1307.       HWND hwndOld = rid.hwndTarget;
  1308.       rid.dwFlags    = RIDEV_REMOVE;
  1309.       rid.hwndTarget = nullptr;
  1310.       if(!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))){
  1311.         _printf("ERROR: failed to unregister mouse as a raw input device! "
  1312.                 "(error code = 0x%08X)\n", (u32)GetLastError());
  1313.         rid.hwndTarget = hwndOld;
  1314.       }
  1315.     }
  1316.  
  1317.     Event evt;
  1318.     memSet(&evt, 0, sizeof(Event));
  1319.     evt.type = (enable) ? EVENT_CURSOR_TRAPPED : EVENT_CURSOR_RELEASED;
  1320.     AddToEventQueue(evt);
  1321.  
  1322.   }
  1323.  
  1324. }
  1325.  
  1326.  
  1327.  
  1328.  
  1329.  
  1330. static void add_event(Event& evt){
  1331.   // I pray AddToEventQueue never fails ever
  1332.   if(!AddToEventQueue(evt)){ _printf("ERROR: EVENT QUEUE IS FULL!!!\n"); }
  1333.  
  1334. }
  1335.  
  1336.  
  1337.  
  1338.  
  1339.  
  1340. // flag_which must be one of the "DOWN" flags (Ex.: RI_MOUSE_BUTTON_1_DOWN)
  1341. // (Also, timestamp is assumed to have already been set inside evt!)
  1342. static void send_raw_mbutton_press(Event& evt, u16 flags,
  1343.                                    u16 flag_which, u8 button_which)
  1344. {
  1345.   if(flags&flag_which){ // Mouse down
  1346.     evt.type=EVENT_MOUSE_DOWN;
  1347.     evt.mouse.pressed = true;
  1348.  
  1349.     u8 button_old = evt.mouse.button;
  1350.     evt.mouse.button = button_which;
  1351.  
  1352.     add_event(evt);
  1353.     evt.mouse.button = button_old|button_which;
  1354.     mbutton_states.value |= button_which;
  1355.  
  1356.   }
  1357.  
  1358.   if(flags&(flag_which<<1)){ // Mouse up
  1359.     evt.type=EVENT_MOUSE_UP;
  1360.     evt.mouse.pressed = false;
  1361.  
  1362.     u8 button_old = evt.mouse.button;
  1363.     evt.mouse.button = button_which;
  1364.  
  1365.     add_event(evt);
  1366.     evt.mouse.button = button_old&(~button_which);
  1367.     mbutton_states.value &= ~button_which;
  1368.  
  1369.   }
  1370.  
  1371. }
  1372.  
  1373.  
  1374.  
  1375.  
  1376.  
  1377. LRESULT CALLBACK WindowProc(HWND winHandle, UINT message,
  1378.                             WPARAM wParam, LPARAM lParam)
  1379. {
  1380.   LRESULT returnResult = 0;
  1381.  
  1382.   Event evt; // Will be populated by any call to HANDLE_EVENT_<?>()...
  1383.   memSet(&evt, 0, sizeof(Event)); //(this is done BEFORE setting anything)
  1384.   evt.type = EVENT_COMMON; //...otherwise it will stay common
  1385.  
  1386.   bool switchBool  = false; // Multipurpose
  1387.   bool switchBool2 = true;  // ^^(switchBool2 intentionally initialized to true)
  1388.   u32  switchFlags = 0;     // ^^
  1389.  
  1390.   bool addEvent = true;
  1391.  
  1392.  
  1393.  
  1394.   switch(message){
  1395.     case WM_DESTROY:
  1396.       PostQuitMessage(0);
  1397.     case WM_QUIT:
  1398.     {
  1399.       evt.type = EVENT_QUIT;
  1400.       win_closed = true;
  1401.       trap_cursor(false, true);
  1402.     } break;
  1403.  
  1404.  
  1405.  
  1406.     case WM_GETMINMAXINFO: // For enforcing a minimum window size
  1407.     {
  1408.       u32 winStyleCurrent   = GetWindowLongA(winHandle, GWL_STYLE  );
  1409.       u32 winStyleExCurrent = GetWindowLongA(winHandle, GWL_EXSTYLE);
  1410.  
  1411.       Point2d winSizeAdjusted = CalculateWindowSize(CANVAS_W, CANVAS_H,
  1412.                                                     winStyleCurrent,
  1413.                                                     winStyleExCurrent);
  1414.  
  1415.       // Set the MINMAXINFO struct provided by lParam
  1416.       MINMAXINFO* mmi = (MINMAXINFO*)lParam;
  1417.       mmi->ptMinTrackSize.x = winSizeAdjusted.x;
  1418.       mmi->ptMinTrackSize.y = winSizeAdjusted.y;
  1419.  
  1420.     } break;
  1421.  
  1422.  
  1423.  
  1424.     case WM_SIZE: // Also redraw canvas if window is being resized
  1425.       win_size.x = LOWORD(lParam);
  1426.       win_size.y = HIWORD(lParam);
  1427.       update_cursor_clip();
  1428.     case WM_PAINT:
  1429.     {
  1430.       // Prepare window for painting
  1431.       PAINTSTRUCT paint;
  1432.       HDC winDevCtx = BeginPaint(winHandle, &paint); // Window device context
  1433.       Rect2d rectW = ConvertToKitRect(paint.rcPaint); // Window's rect
  1434.  
  1435.       // Copy canvas bitmap to window
  1436.       SetStretchBltMode(winDevCtx, COLORONCOLOR); // Nearest-neighbor
  1437.       StretchBlt(winDevCtx, rectW.x,rectW.y,  rectW.w,  rectW.h,
  1438.                  canvas_dc,       0,      0, CANVAS_W, CANVAS_H, SRCCOPY);
  1439.  
  1440.       if(message == WM_SIZE)
  1441.         canvas_present(true);
  1442.  
  1443.       EndPaint(winHandle, &paint);
  1444.  
  1445.     } break;
  1446.  
  1447.  
  1448.  
  1449.     case WM_KILLFOCUS:
  1450.     {
  1451.       // If a key is released outside the client area, its key up message
  1452.       // is never sent, so here the key states are reset when unfocusing...
  1453.       // ...but first, send a key up event for every currently pressed key
  1454.  
  1455.       // Call QPC only once, since the events technically happen simultaneously
  1456.       QueryPerformanceCounter((LARGE_INTEGER*)&evt.key.timestamp);
  1457.  
  1458.       KEY_Params params = 0;
  1459.       params.currUnpressed = 1;
  1460.  
  1461.       for(u32 chr=0; chr<256; ++chr){
  1462.         // Send a KEY_UP only if the state was previously true
  1463.         if(key_states[chr] == true){
  1464.           params.scanCode = MapVirtualKeyA(chr, MAPVK_VK_TO_VSC);
  1465.           HANDLE_KEY_CHARUPDOWN(evt, false, chr, params, 0);
  1466.           add_event(evt);
  1467.         }
  1468.       }
  1469.  
  1470.       memSet(key_states, 0, sizeof(bool)*256); //NOW the states can be reset
  1471.  
  1472.       // Reset all key modifiers (except for toggles) too
  1473.       key_mods.all &= 0xff00; // Non-toggles are stored in low byte
  1474.  
  1475.       mbutton_states.value = 0; // Reset mouse states as well
  1476.  
  1477.       trap_cursor(false, true);
  1478.  
  1479.     } break;
  1480.  
  1481.  
  1482.  
  1483.     //EVENT_KEY_CHAR, EVENT_KEY_UP, EVENT_KEY_DOWN
  1484.     case WM_CHAR:
  1485.     case WM_SYSKEYUP:
  1486.     case WM_SYSKEYDOWN:
  1487.     case WM_KEYUP:
  1488.     case WM_KEYDOWN:
  1489.     case WM_SYSCHAR:
  1490.     {
  1491.       // Marshal relevant stuff to pass to event handler
  1492.       bool    charEvent = message==WM_CHAR;
  1493.       u8 virtualKeyCode = (u8) wParam;
  1494.       KEY_Params params = (u32)lParam;
  1495.  
  1496.       // Set repeat flag
  1497.       params.repeatCount = (!params.currUnpressed)    == true &&
  1498.                            key_states[virtualKeyCode] == true;
  1499.  
  1500.       if(!charEvent){
  1501.         // (Uncomment the last part of the line below if issues occur
  1502.         //  related to missed key press events or something!)
  1503.         bool keydown = message==WM_KEYDOWN;//|| message==WM_SYSKEYDOWN;
  1504.         bool pressed = !params.currUnpressed;
  1505.         key_states[virtualKeyCode] = pressed;
  1506.  
  1507.         // Update any relevant key modifiers
  1508.         switch(virtualKeyCode){
  1509.           case VKEY_SHIFT    : { key_mods.lshift = key_mods.rshift = pressed; } break;
  1510.           case VKEY_CONTROL  : { key_mods.lctrl  = key_mods.rctrl  = pressed; } break;
  1511.           case VKEY_ALT      : { key_mods.lalt   = key_mods.ralt   = pressed; } break;
  1512.           case VKEY_LWIN     : { key_mods.lgui   = pressed; } break;
  1513.           case VKEY_RWIN     : { key_mods.rgui   = pressed; } break;
  1514.           case VKEY_NUMLOCK  : if(keydown){ key_mods.numlock^=1; } break;
  1515.           case VKEY_CAPSLOCK : if(keydown){ key_mods.capslock^=1; } break;
  1516.           case VKEY_SCROLLOCK: if(keydown){ key_mods.scrollock^=1; } break;
  1517.           default:;
  1518.         }
  1519.  
  1520.         if(     virtualKeyCode==VKEY_CTRL ) mbutton_states.ctrl  = pressed;
  1521.         else if(virtualKeyCode==VKEY_SHIFT) mbutton_states.shift = pressed;
  1522.  
  1523.         bool repeat = params.repeatCount!=0;
  1524.         trap_cursor(!cursor_trapped,
  1525.                     virtualKeyCode==VKEY_ESCAPE && keydown && !repeat);
  1526.  
  1527.       }
  1528.  
  1529.       HANDLE_KEY_CHARUPDOWN(evt, charEvent, virtualKeyCode,
  1530.                                     params,   key_mods.all);
  1531.  
  1532.     } break;
  1533.  
  1534.  
  1535.  
  1536.     //EVENT_MOUSE_MOVED
  1537.     case WM_MOUSEMOVE:
  1538.     {
  1539.       // Get button states
  1540.       MOUSE_ButtonStates buttonStates;
  1541.       buttonStates.left   = (wParam&MK_LBUTTON ) != 0;
  1542.       buttonStates.middle = (wParam&MK_MBUTTON ) != 0;
  1543.       buttonStates.right  = (wParam&MK_RBUTTON ) != 0;
  1544.       buttonStates.x1     = (wParam&MK_XBUTTON1) != 0;
  1545.       buttonStates.x2     = (wParam&MK_XBUTTON2) != 0;
  1546.       buttonStates.ctrl   = (wParam&MK_CONTROL ) != 0;
  1547.       buttonStates.shift  = (wParam&MK_SHIFT   ) != 0;
  1548.  
  1549.       // Get new mouse position
  1550.       Point2d mousePositionNew;
  1551.       mousePositionNew.x =                GET_X_LPARAM(lParam);
  1552.       mousePositionNew.y = win_size.y-1 - GET_Y_LPARAM(lParam);
  1553.  
  1554.       // If this is the first instance of WINEVENT_MOUSE_MOVE,
  1555.       // there is no previous mouse position, so the delta should be 0 then
  1556.       if(!mouse_was_moved_before){
  1557.         mouse_position = mousePositionNew;
  1558.         mouse_was_moved_before = true;
  1559.       }
  1560.  
  1561.       HANDLE_MOUSE_MOVED(evt, buttonStates.value,
  1562.                          mouse_position, mousePositionNew);
  1563.  
  1564.       mouse_position = mousePositionNew; // Set current position to new one
  1565.  
  1566.     } break;
  1567.  
  1568.  
  1569.  
  1570.     case WM_MOUSELEAVE:
  1571.     {
  1572.       // Indicates that the mouse will have yet to be moved inside client area
  1573.       mouse_was_moved_before = false;
  1574.     } break;
  1575.  
  1576.  
  1577.  
  1578.     //EVENT_MOUSE_HWHEEL, EVENT_MOUSE_VWHEEL
  1579.     case WM_MOUSEHWHEEL:
  1580.     case WM_MOUSEWHEEL:
  1581.     {
  1582.       bool verticalScroll = message==WM_MOUSEWHEEL;
  1583.  
  1584.       s16 scrollAmount = (s16)HIWORD(wParam);
  1585.  
  1586.       mbutton_states.left   = (wParam&MK_LBUTTON ) != 0;
  1587.       mbutton_states.middle = (wParam&MK_MBUTTON ) != 0;
  1588.       mbutton_states.right  = (wParam&MK_RBUTTON ) != 0;
  1589.       mbutton_states.x1     = (wParam&MK_XBUTTON1) != 0;
  1590.       mbutton_states.x2     = (wParam&MK_XBUTTON2) != 0;
  1591.  
  1592.       Point2d scrollMousePosition;
  1593.       scrollMousePosition.x = GET_X_LPARAM(lParam);
  1594.       scrollMousePosition.y = GET_Y_LPARAM(lParam);
  1595.  
  1596.       // The coordinates are, by default, relative to the screen
  1597.       // and not the window (for some reason)
  1598.       ScreenToClient(winHandle, (POINT*)&scrollMousePosition);
  1599.  
  1600.       HANDLE_MOUSE_HVWHEEL(evt, verticalScroll, scrollAmount,
  1601.                            mbutton_states.value, scrollMousePosition);
  1602.  
  1603.     } break;
  1604.  
  1605.  
  1606.  
  1607.     //EVENT_MOUSE_UP, EVENT_MOUSE_DOWN
  1608.      //switchBool  = evt.mouse.dblClick
  1609.      //switchBool2 = evt.mouse.pressed
  1610.      //switchFlags = evt.mouse.button
  1611.     case WM_LBUTTONDBLCLK: switchBool  = true;  goto _notLButtonUp;
  1612.     case WM_LBUTTONUP    : switchBool2 = false; _notLButtonUp:
  1613.     case WM_LBUTTONDOWN  : switchFlags |= MBUTTON_LEFT; goto _handleMouseClick;
  1614.  
  1615.     case WM_MBUTTONDBLCLK: switchBool  = true;  goto _notMButtonUp;
  1616.     case WM_MBUTTONUP    : switchBool2 = false; _notMButtonUp:
  1617.     case WM_MBUTTONDOWN  : switchFlags |=MBUTTON_MIDDLE; goto _handleMouseClick;
  1618.  
  1619.     case WM_RBUTTONDBLCLK: switchBool  = true;  goto _notRButtonUp;
  1620.     case WM_RBUTTONUP    : switchBool2 = false; _notRButtonUp:
  1621.     case WM_RBUTTONDOWN  : switchFlags |= MBUTTON_RIGHT; goto _handleMouseClick;
  1622.  
  1623.     case WM_XBUTTONDBLCLK: switchBool  = true;  goto _notXButtonUp;
  1624.     case WM_XBUTTONUP    : switchBool2 = false; _notXButtonUp:
  1625.     case WM_XBUTTONDOWN  : if(wParam & MK_XBUTTON1) switchFlags |= MBUTTON_X1;
  1626.                            else                     switchFlags |= MBUTTON_X2;
  1627.     {
  1628.       _handleMouseClick:
  1629.  
  1630.       Point2d clickPosition;
  1631.       clickPosition.x = GET_X_LPARAM(lParam);
  1632.       clickPosition.y = GET_Y_LPARAM(lParam);
  1633.  
  1634.       bool pressed      = switchBool2;
  1635.       bool doubleClick  = switchBool;
  1636.  
  1637.            if(switchFlags&MBUTTON_LEFT  ) mbutton_states.left   = pressed;
  1638.       else if(switchFlags&MBUTTON_MIDDLE) mbutton_states.middle = pressed;
  1639.       else if(switchFlags&MBUTTON_RIGHT ) mbutton_states.right  = pressed;
  1640.       else if(switchFlags&MBUTTON_X1    ) mbutton_states.x1     = pressed;
  1641.       else if(switchFlags&MBUTTON_X2    ) mbutton_states.x2     = pressed;
  1642.  
  1643.       HANDLE_MOUSE_UPDOWN(evt, clickPosition,
  1644.                           mbutton_states.value, pressed, doubleClick);
  1645.  
  1646.     } break;
  1647.  
  1648.  
  1649.  
  1650.     case WM_INPUT:
  1651.     {
  1652.       // First call to query for the data's size
  1653.       UINT dataSize = 0;
  1654.       GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr,
  1655.                       &dataSize, sizeof(RAWINPUTHEADER));
  1656.       RAWINPUT* data = (RAWINPUT*)alloca(dataSize);
  1657.  
  1658.       // Second call to get the actual data
  1659.       if(GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data,
  1660.                          &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
  1661.       {
  1662.         const RAWMOUSE& mouse = data->data.mouse;
  1663.  
  1664.         if(data->header.dwType != RIM_TYPEMOUSE  ||
  1665.            mouse.usFlags != MOUSE_MOVE_RELATIVE)
  1666.         {
  1667.           addEvent = false; break;
  1668.         }
  1669.  
  1670.         // Handle the actual event stuff
  1671.         {
  1672.           s32 _deltaWheel = mouse.usButtonData;
  1673.           if(_deltaWheel >= 32768) _deltaWheel -= 65536; // Unsigned-to-signed
  1674.  
  1675.           const u16 flags      = mouse.usButtonFlags;
  1676.           const f32 deltaWheel = (f32)_deltaWheel/WHEEL_DELTA;
  1677.           const f32 deltaX     =  mouse.lLastX;
  1678.           const f32 deltaY     = -mouse.lLastY; // GDI's bitmaps are *bottom-up*
  1679.  
  1680.           HANDLE_MOUSE_RAWINPUT(evt, flags, deltaWheel, deltaX, deltaY);
  1681.         }
  1682.  
  1683.       } else {
  1684.         _printf("ERROR: failed to get raw mouse input data! "
  1685.                 "(error code = 0x%08X)\n", (u32)GetLastError());
  1686.  
  1687.       }
  1688.  
  1689.       // Any relevant raw input events will already have been sent by this point
  1690.       addEvent = false;
  1691.  
  1692.     } break;
  1693.  
  1694.  
  1695.  
  1696.     default: returnResult = DefWindowProcA(winHandle, message, wParam, lParam);
  1697.  
  1698.   }
  1699.  
  1700.  
  1701.  
  1702.   if(evt.type != EVENT_COMMON  &&  evt.common.timestamp == 0  &&  addEvent)
  1703.   {
  1704.     // Idk how expensive QPC is, so I'll only call it if the event is valid
  1705.     QueryPerformanceCounter((LARGE_INTEGER*)&evt.common.timestamp);
  1706.     add_event(evt);
  1707.   }
  1708.  
  1709.  
  1710.  
  1711.   return returnResult;
  1712.  
  1713. }
  1714.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement