Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\src\user_audio.cpp":
- #include <public_stuff.hpp>
- /*
- #define volume 0.05f
- #define u8_smp(_smp) ( (s16)( ((_smp)-128)<<8 ) ) //u8 to s16 sample
- void audio_callback(s16* smp, u32 len){
- static s64 t = 0;
- for(u32 i=0; i<len; ++i){
- //bytebeat shamelessly stolen from dollchan (go check out their bytebeat editor, seriously)
- smp[i] = u8_smp( (((t/10|0)^(t/10|0)-1280)%11*t/2&127)+(((t/640|0)^(t/640|0)-2)%13*t/2&127) );
- smp[i] *= volume;
- ++t;
- }
- }
- */
- #include <math.h>
- // This plays a 440Hz sine wave
- void user_audio(StereoF32* stream, u32 len, u32 sampleRate, f64 timeStamp)
- {
- static f64 t = 0;
- static f64 modulate = 0.0f;
- for(u32 i=0; i<len; ++i){
- f32 cycle = ((f32)t/sampleRate) * M_2PIf;
- f64 increment = 1.0 + (f64)sinF(modulate)*0.3;
- t += increment;
- modulate = fmod(modulate+0.0001, M_2PIf);
- f32 smp = sinF(cycle*440);
- smp *= 0.10f / increment;
- stream[i].l = smp;
- stream[i].r = smp;
- }
- t = fmod(t, sampleRate);
- /*
- // The static keyword here just makes the variable persistent, in that
- // its state is preserved between each call to user_audio
- static u64 t = 0; // u[nsigned] 64-bit integer
- for(u32 i=0; i<len; ++i){ //For every sample to be filled in the audio buffer
- // t is incremented every sample, and is divided by the sample rate to
- // basically make a percentage of how far it is through a second.
- // This is then multiplied by 2pi, which effectively makes it oscillate
- // once every second if you fed cycle into sin directly
- f32 cycle = ((f32)(t++)/sampleRate) * M_2PIf;
- // cycle is multiplied by 440 so that it oscillates 440 times a second
- // instead of just once.
- // (Also, sinF is just sin but it uses 32-bit f[loats] instead of 64-bit,
- // and unlike the actual sinf, this is a custom implementation)
- f32 smp = sinF(cycle*440); // This ofc returns a number from -1.0 to 1.0
- // 20% volume
- smp *= 0.20f;
- // Since the audio stream is stereo, the sample is applied to
- // both 'ears' so-to-speak.
- stream[i].l = smp;
- stream[i].r = smp;
- }
- // Unnecessary, since a u64 is so big, but this makes it so that
- // even if it was a smaller integer size, it will never overflow.
- // (The % operator does a division, but gets the remainder instead)
- t %= sampleRate;
- */
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\src\user_main.cpp":
- #include <math.h>
- #include <public_stuff.hpp>
- // Creates an ARGB color in the form of a u32, not Color24
- #ifndef ABGR_U32
- #define ABGR_U32(_r,_g,_b,_a) (\
- ((u32)(_a)<<24) | ((u32)(_b)<<16) |\
- ((u32)(_g)<< 8) | ((u32)(_r) ) )
- #endif /* ABGR_U32 */
- u32 hsv2rgb(f32 h, f32 s, f32 v){
- f32 r, g, b;
- // Normalize values
- h /= 360;
- s /= 100;
- v /= 100;
- // The rest is magic, idk
- s32 i = floor(h*6);
- f32 f = h * 6 - i;
- f32 p = v * (1.0f - s);
- f32 q = v * (1.0f - f * s);
- f32 t = v * (1.0f - (1.0f-f) * s);
- switch (i % 6) {
- case 0: r = v, g = t, b = p; break;
- case 1: r = q, g = v, b = p; break;
- case 2: r = p, g = v, b = t; break;
- case 3: r = p, g = q, b = v; break;
- case 4: r = t, g = p, b = v; break;
- case 5: r = v, g = p, b = q; break;
- }
- // It should be impossible for r,g,b to be uninitialized
- // due to how the switch is set up, so the warning can be ignored.
- #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
- #pragma GCC diagnostic push
- return ABGR_U32(r*255, g*255, b*255, 255);
- #pragma GCC diagnostic pop
- }
- static inline u8 sinF_unit_u8(f32 x){ return (0.5f + sinF(x)*0.5f) * 255; }
- int user_main(int argc, char** argv){
- for(u32 i=0; i<256; ++i)
- pixels_palette[i].v = hsv2rgb(((f32)i/256)*360.0f, 100.0f, 100.0f);
- // pixels_palette[i].v = ABGR_U32(i, i, i, 255);
- update_palette();
- u64 f = 0; // Frame counter
- while(!win_closed){
- Event evt;
- while(pollEvent(&evt));
- if(win_closed) break;
- for(int y=0; y<CANVAS_H; ++y)
- for(int x=0; x<CANVAS_W; ++x)
- {
- f32 ux = (f32)x / (CANVAS_W-1);
- f32 uy = (f32)y / (CANVAS_H-1);
- f32 uf = (f32)f / 60;
- f32 xmod = cosF( (uy+uf/3) * M_2PIf );
- u8 value = sinF_unit_u8( (xmod*ux + uy + uf) * M_2PIf );
- PixelCoords which_pixel = {(u8)x, (u8)y};
- pixels[ which_pixel.v ].v = value;
- }
- canvas_present(); // Draws the canvas to the window
- timeSleep(16); // Waits for about 16ms to approximate 60fps (1000/16 = 62.5)
- ++f; // Increment frames
- }
- return 0;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\src\win32\audio.cpp":
- #include <win32/audio.hpp>
- #include <public_stuff.hpp>
- void user_audio(StereoF32* stream, u32 len, u32 sampleRate, f64 timeStamp);
- #define _chunk_count 3
- #define fade_delta_len 0.010f //for 10ms
- static StereoF32 samples_in[4096];
- static StereoS16 _chunk_data[_chunk_count][4096];
- static WAVEHDR _chunk_header[_chunk_count];
- static int _chunk_which;
- static HWAVEOUT wave_out;
- static bool fade_in = true;
- static f32 fade_volume = 0.0f;
- u32 audio_samples = 0;
- u32 sample_rate = 0;
- static inline void call_user_audio(int which){
- // Zero out user buffer, before invoking user function
- memSet(samples_in, 0, audio_samples*sizeof(StereoF32));
- user_audio(samples_in, audio_samples, sample_rate, timeGetSeconds());
- // Convert stereo f32 (-1.0f to 1.0f) to stereo s16 (-32768 to 32767)
- StereoS16* samples_out = _chunk_data[which];
- // 'By how much does the fade volume change every frame?'
- const f32 fade_delta = 1.0f / (fade_delta_len*sample_rate);
- if(fade_in && fade_volume>=1.0f){ // Fully faded in
- for(u32 i=0; i<audio_samples; ++i){
- samples_out[i].l = (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
- samples_out[i].r = (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
- }
- } else if(fade_in){ // Currently fading in
- for(u32 i=0; i<audio_samples; ++i){
- f32 smp_l = (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
- f32 smp_r = (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
- smp_l *= fade_volume;
- smp_r *= fade_volume;
- fade_volume = MIN(fade_volume+fade_delta, 1.0f);
- samples_out[i].l = smp_l;
- samples_out[i].r = smp_r;
- }
- } else { // if(!fade_in)
- for(u32 i=0; i<audio_samples; ++i){
- f32 smp_l = (s16)( CLAMP(samples_in[i].l, -1.0f, 1.0f) * 32767 );
- f32 smp_r = (s16)( CLAMP(samples_in[i].r, -1.0f, 1.0f) * 32767 );
- smp_l *= fade_volume;
- smp_r *= fade_volume;
- fade_volume = MAX(fade_volume-fade_delta, 0.0f);
- samples_out[i].l = smp_l;
- samples_out[i].r = smp_r;
- }
- }
- }
- void CALLBACK WaveOutProc(HWAVEOUT hwo, UINT msg, DWORD_PTR inst,
- DWORD_PTR param1, DWORD_PTR param2)
- {
- if(msg == WOM_DONE){
- call_user_audio(_chunk_which);
- waveOutWrite(hwo, &_chunk_header[_chunk_which], sizeof(WAVEHDR));
- _chunk_which += 1;
- _chunk_which %= _chunk_count;
- }
- }
- int WaveOutInit(){
- MMRESULT err;
- WAVEOUTCAPS devcaps;
- if((err = waveOutGetDevCapsA(WAVE_MAPPER, &devcaps, sizeof(WAVEOUTCAPS))))
- return -1;
- if(devcaps.dwFormats & WAVE_FORMAT_4S16) // 44100Hz, Stereo, s16
- {
- audio_samples = 2048;
- sample_rate = 44100;
- }
- else if(devcaps.dwFormats & WAVE_FORMAT_96S16) // 96000Hz, Stereo, s16
- {
- audio_samples = 4096;
- sample_rate = 96000;
- }
- else if(devcaps.dwFormats & WAVE_FORMAT_2S16) // 22050Hz, Stereo, s16
- {
- audio_samples = 1024;
- sample_rate = 22050;
- }
- else if(devcaps.dwFormats & WAVE_FORMAT_1S16) // 11025Hz, Stereo, s16
- {
- audio_samples = 512;
- sample_rate = 11025;
- }
- else // No valid 16-bit stereo sample rate is supported!
- {
- return -2;
- }
- _chunk_which = 0;
- WAVEFORMATEX fmt;
- fmt.wFormatTag = WAVE_FORMAT_PCM;
- fmt.nChannels = 2;
- fmt.nSamplesPerSec = sample_rate;
- fmt.wBitsPerSample = 16;
- fmt.cbSize = 0;
- fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
- fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
- if(waveOutOpen(&wave_out, WAVE_MAPPER, &fmt,
- (DWORD_PTR)WaveOutProc, 0, CALLBACK_FUNCTION))
- {
- return -3;
- }
- #define WO_VOLUME(_left, _right) ( ((_right)<<16) | (_left) )
- err = waveOutSetVolume(wave_out, WO_VOLUME(0xffff, 0xffff));
- // Even if the function isn't supported, hopefully it'll just default to 100%
- if(err && err != MMSYSERR_NOTSUPPORTED) return -4;
- // Set header info and prime the audio buffers
- for(u32 i=0; i<_chunk_count; ++i){
- _chunk_header[i].lpData = (CHAR*)_chunk_data[i];
- _chunk_header[i].dwBufferLength = audio_samples*sizeof(s16)*fmt.nChannels;
- _chunk_header[i].dwBytesRecorded = 0;
- _chunk_header[i].dwUser = 0;
- _chunk_header[i].dwFlags = 0;
- _chunk_header[i].dwLoops = 0;
- _chunk_header[i].lpNext = nullptr;
- _chunk_header[i].reserved = 0;
- if(waveOutPrepareHeader(wave_out, &_chunk_header[i], sizeof(WAVEHDR)))
- return -5;
- memSet(_chunk_data[i], 0, _chunk_header[i].dwBufferLength);
- if(waveOutWrite(wave_out, &_chunk_header[i], sizeof(WAVEHDR)))
- return -6;
- }
- return 0;
- }
- void WaveOutQuit(){
- // Fade audio out smoothly before pausing
- fade_in = false;
- while(fade_volume > 0.0f) Sleep(10);
- // Also make sure all buffers have played
- #define chunk_time ((f64)(_chunk_count*audio_samples)/sample_rate)
- Sleep((DWORD)(chunk_time*1000));
- waveOutPause(wave_out);
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\src\win32\functions.cpp":
- #include <win32/audio.hpp>
- #include <win32/video.hpp>
- #include <win32/misc.hpp>
- _Ole32_func_t _Ole32_func;
- char _Ole32_names[] = // prefix = "CoTaskMem"
- "Alloc\0"
- "Realloc\0"
- "Free\0"
- "\0";
- _Winmm_func_t _Winmm_func;
- char _Winmm_names[] = // 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"
- "\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"
- "\0";
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\src\win32\input.cpp":
- #include <win32/input.hpp>
- #include <win32/misc.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;
- extern HWND win;
- int InputInit(){
- // This function has no failure condition
- InitializeCriticalSectionAndSpinCount(&events_lock,
- CRITICAL_SECTION_SPINCOUNT);
- // Get initial position of mouse
- // (Actually this isn't necessary due to the way deltas are calculated)
- //if(!GetCursorPos((POINT*)&mouse_position)) return -1;
- //if(!ScreenToClient(win, (POINT*)&mouse_position)) return -2;
- 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;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\src\win32\main.cpp":
- #include <win32/audio.hpp>
- #include <win32/video.hpp>
- #include <win32/input.hpp>
- #include <win32/misc.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, 256);
- 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) \
- if(!(_success)){ returnCode = (_code); 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);
- _Winmm_dll = LoadLibrary("Winmm.dll");
- MAIN_ASSERT(_Winmm_dll, -2);
- _User32_dll = LoadLibrary("User32.dll");
- MAIN_ASSERT(_User32_dll, -3);
- _Gdi32_dll = LoadLibrary("Gdi32.dll");
- MAIN_ASSERT(_Gdi32_dll, -4);
- MAIN_ASSERT(loadFunctions("CoTaskMem", _Ole32_names, _Ole32_dll,
- _Ole32_func.ptrs, 0), -10);
- MAIN_ASSERT(loadFunctions("waveOut", _Winmm_names, _Winmm_dll,
- _Winmm_func.ptrs, 0), -11);
- #define LOAD_USER32_FUNC(_prefix, _namegroup, _from, _errcode) \
- MAIN_ASSERT(loadFunctions(_prefix, _User32_names_##_namegroup, _User32_dll,\
- _User32_func.ptrs, (_from)), (_errcode))
- LOAD_USER32_FUNC("", a, 0, -12);
- #define LOAD_GDI32_FUNC(_prefix, _namegroup, _from, _errcode) \
- MAIN_ASSERT(loadFunctions(_prefix, _Gdi32_names_##_namegroup, _Gdi32_dll,\
- _Gdi32_func.ptrs, (_from)), (_errcode))
- LOAD_GDI32_FUNC("", a, 0, -13);
- err = WaveOutInit(); // Also starts audio automatically
- MAIN_ASSERT(err == 0, err-100);
- err = WindowInit(hThisInst);
- MAIN_ASSERT(err == 0, err-200);
- err = InputInit();
- MAIN_ASSERT(err == 0, err-300);
- _printf("Initialized in: %.4f seconds!\n", timeGetSeconds()-timeStart);
- returnCode = user_main(0, nullptr); // TBD: provide main's arguments
- InputQuit();
- WindowQuit();
- WaveOutQuit();
- // Make sure no unused parameter warnings occur for these
- (void)hThisInst;
- (void)hPrevInst;
- (void)lpszArg;
- (void)nCmdShow;
- goto _main_return;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\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_winmm_2025-03-28\src\win32\std.cpp":
- #include <public_stuff.hpp>
- void* memSet(void* dst, int val, size_t len){
- u8* _dst = (u8*)dst;
- u8 value = (u8 )val;
- while((len--) > 0)
- *_dst++ = value;
- 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;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_winmm_2025-03-28\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 256x144 at 8-bpp
- 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_rg(_c) ((u8)( (255.0f*((f32)(_c)/7)) + 0.5f ))
- #define conv_b(_c) ((_c)*85)
- pixels_palette[i].r = conv_rg(color.r);
- pixels_palette[i].g = conv_rg(color.g);
- pixels_palette[i].b = conv_b(color.b);
- pixels_palette[i]._ = 0;
- }
- 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
- if(immediate) UpdateWindow(win);
- }
- void close_window(){
- if(win_closed) return;
- DeleteObject(canvas);
- DeleteDC(canvas_dc);
- DestroyWindow(win);
- //UnregisterClassA
- //DeleteCriticalSection
- win_closed = true;
- }
- #ifndef INT_MAX
- #define INT_MAX 2147483647
- #endif /* INT_MAX */
- bool update_palette(const Color24* new_palette,
- u32 first_color, u32 num_colors)
- {
- // 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;
- }
- 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;
- 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; // ^^
- switch(message){
- case WM_DESTROY:
- PostQuitMessage(0);
- case WM_QUIT:
- {
- evt.type = EVENT_QUIT;
- win_closed = 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);
- 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);
- } 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);
- if(!AddToEventQueue(evt)){ _printf("EVENT QUEUE IS FULL!!!\n"); }
- }
- }
- 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
- } 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){
- bool keydown = message==WM_KEYDOWN;
- 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:;
- }
- }
- 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;
- // Get new mouse position
- Point2d mousePositionNew;
- mousePositionNew.x = GET_X_LPARAM(lParam);
- mousePositionNew.y = 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);
- 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;
- Point2d scrollMousePosition;
- scrollMousePosition.x = GET_X_LPARAM(lParam);
- scrollMousePosition.y = GET_Y_LPARAM(lParam);
- // The coordinates are relative to the screen, not the window (for some reason)
- ScreenToClient(winHandle, (POINT*)&scrollMousePosition);
- HANDLE_MOUSE_HVWHEEL(evt, verticalScroll, scrollAmount,
- buttonStates.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);
- u8 buttonStates = (u8)switchFlags;
- bool pressed = switchBool2;
- bool doubleClick = switchBool;
- HANDLE_MOUSE_UPDOWN(evt, clickPosition,
- buttonStates, pressed, doubleClick);
- } break;
- default: returnResult = DefWindowProcA(winHandle, message, wParam, lParam);
- }
- if(evt.type != EVENT_COMMON && evt.common.timestamp == 0)
- {
- // Idk how expensive QPC is, so I'll only call it if the event is valid
- QueryPerformanceCounter((LARGE_INTEGER*)&evt.common.timestamp);
- // I pray AddToEventQueue never fails ever
- if(!AddToEventQueue(evt)){ _printf("EVENT QUEUE IS FULL!!!\n"); }
- }
- return returnResult;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement