Framework

    Need to write class that will have all what I need. I found very interesting to use static classes, so I will use it here too.

    Here slFramework.h

    // Simple typeswithout methods
    struct slPoint {
    	int32_t x = 0;
    	int32_t y = 0;
    };
    struct slRect {
    	int32_t left = 0;
    	int32_t top = 0;
    	int32_t right = 0;
    	int32_t bottom = 0;
    };
    struct slRectf {
    	float left = 0.f;
    	float top = 0.f;
    	float right = 0.f;
    	float bottom = 0.f;
    };
    
    #include "slowlib.base/input/slInput.h"
    
    // Some callbacks. Some information from framework will be here.
    class slFrameworkCallback
    {
    public:
    	slFrameworkCallback() {}
    	virtual ~slFrameworkCallback() {}
    
    	virtual void OnMessage();
    };
    
    // Main API for framework.
    // It will be static methods.
    class slFramework
    {
    public:
    	static void Start(slFrameworkCallback*);
    	static void Stop();
    	static void Update();
    
    	static slWindow* SummonWindow(slWindowCallback*);
    
    	static bool PointInRect(slRect* r, slPoint* p);
    	static void PointSet(slPoint* p, int32_t x, int32_t y);
    	static void RectSet(slRect* rct, int32_t l, int32_t t, int32_t r, int32_t b);
    	
    	// Need to test. can I use it like this? RectfSet(&r, {0.f,1.f,2.f,3.f});
    	static void RectfSet(slRectf* rct, float*);
    };
    

    slPoint slRect slRectf - I need it for cursor position, window size, it will be used for GUI. I don't want to create another .h file for this.

    #include "slowlib.base/input/slInput.h" will be here too

    class slFrameworkCallback - I think framework can do something and let user know about it, and probably user will be able to interact with framework using this callback.

    class slFramework:

  • Start - allocate implementation.
  • Stop - deallocate implementation.
  • Update - calculate deltatime, update input, update GUI, update window.
  • SummonWindow - create window. CreateWindow is winapi function so I will use word `summon`.
  • The rest is simple methods like set point using numbers.
  • Some implementation

    slFrameworkImpl* g_framework = 0;
    void slFrameworkImpl::OnDestroy(){}
    void slFramework::Start(slFrameworkCallback* cb)
    {
    	SL_ASSERT_ST(cb);
    	SL_ASSERT_ST(g_framework == 0);
    	if (!g_framework)
    	{
    		g_framework = slCreate<slFrameworkImpl>();
    		g_framework->m_callback = cb;
    	}
    }
    
    void slFramework::Stop()
    {
    	SL_ASSERT_ST(g_framework);
    	if (g_framework)
    	{
    		g_framework->OnDestroy();
    
    		slDestroy(g_framework);
    		g_framework = 0;
    	}
    }
    
    void slFramework::Update()
    {
    	slInputUpdatePre(&g_framework->m_input);
    
    #ifdef SL_PLATFORM_WINDOWS
    	MSG msg;
    	while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
    	{
    		GetMessage(&msg, NULL, 0, 0);
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    #endif
    
    	slInputUpdatePost(&g_framework->m_input);
    }
    slWindow* slFramework::SummonWindow(slWindowCallback* cb)
    {
    	SL_ASSERT_ST(cb);
    	return slCreate<slWindow>(cb);
    }
    

    slFrameworkImpl

    class slFrameworkImpl
    {
    public:
    	slFrameworkImpl() {}
    	~slFrameworkImpl() {}
    
    	slFrameworkCallback* m_callback = 0;
    	slInputData m_input;
    	void OnDestroy();
    };
    

    Window

    Window will have common for all OS data. OS data will be inside this common data (void* m_implementation).

    Window will use callback class.

    class slWindowCallback
    {
    public:
    	slWindowCallback() {}
    	virtual ~slWindowCallback() {}
    
    	virtual void OnActivate(slWindow*) {}
    	virtual void OnDeactivate(slWindow*) {}
    	virtual void OnSize(slWindow*) {}
    	virtual void OnMinimize(slWindow*) {}
    	virtual void OnMaximize(slWindow*) {}
    	virtual void OnRestore(slWindow*) {}
    	virtual void OnDraw(slWindow*) {}
    	virtual void OnMove(slWindow*) {}
    	virtual void OnClose(slWindow*) {}
    };
    
    struct slWindowCommonData
    {
    	slWindowCallback* m_cb = 0;
    	slPoint m_borderSize;
    	slPoint m_borderSizeCurrent;
    	slPoint m_sizeMinimum;
    	bool m_isVisible = false;
    
    	void* m_implementation = 0;
    };
    
    class slWindow
    {
    	slWindowCommonData m_data;
    public:
    	slWindow(slWindowCallback* cb);
    	~slWindow();
    
    	void SetTitle(const char*);
    	void SetVisible(bool v);
    	bool IsVisible();
    	void Maximize();
    	void Minimize();
    	void Restore();
    	void SetBorderless(bool);
    	void SetNoResize(bool);
    	void SetNoMinimize(bool);
    	slPoint* GetSizeMinimum();
    	slPoint* GetBorderSize();
    
    	// it will call callback functions
    	void OnActivate();
    	void OnDeactivate();
    	void OnSize();
    	void OnMinimize();
    	void OnMaximize();
    	void OnRestore();
    	void OnDraw();
    	void OnMove();
    	void OnClose();
    
    	slWindowCommonData* GetData() { return &m_data; }
    };
    

    Later I will add function to change window size and position.

    Creating window. I need to allocate memory for OS data. Store all winapi thing in it. Use WS_OVERLAPPEDWINDOW, user will use methods to change style.

    slWindow::slWindow(slWindowCallback* cb)
    {
    #ifdef SL_PLATFORM_WINDOWS
        slWindowWin32* w32 = (slWindowWin32*)slMemory::malloc(sizeof(slWindowWin32));
        m_data.m_implementation = w32;
    
        WNDCLASSEXW wcex;
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc = WndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = 0;
        wcex.hInstance = GetModuleHandle(0);
        wcex.hIcon = 0;// LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_WIN32TEST));
        wcex.hCursor = LoadCursor(0, IDC_ARROW);
        wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wcex.lpszMenuName = 0;
    
        static int windowCount = 0;
        wsprintf(w32->m_className, L"w%i", windowCount++);
        wcex.lpszClassName = w32->m_className;
        wcex.hIconSm = 0;// LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
        RegisterClassExW(&wcex);
    
        w32->m_hWnd = CreateWindowExW(
            0,
            w32->m_className,
            L"slWindow",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            0,
            0,
            wcex.hInstance,
            this
        );
        
        w32->m_style = GetWindowLongPtr(w32->m_hWnd, GWL_STYLE);
    
        int padding = GetSystemMetrics(SM_CXPADDEDBORDER);
        m_data.m_borderSize.x = GetSystemMetrics(SM_CXFRAME) + padding;
        m_data.m_borderSize.y = (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) + padding);
        m_data.m_borderSizeCurrent = m_data.m_borderSize;
    
        RAWINPUTDEVICE device;
        device.usUsagePage = 0x01;
        device.usUsage = 0x02;
        device.dwFlags = 0;
        device.hwndTarget = 0;
        RegisterRawInputDevices(&device, 1, sizeof device);
    
    #endif
    
        m_data.m_cb = cb;
    }
    

    Get border size...probably this need to do also in method SetBorderless().

    RegisterRawInputDevices - for mouse input I will use raw input.

    Destruction

    slWindow::~slWindow()
    {
        if (m_data.m_implementation)
        {
    #ifdef SL_PLATFORM_WINDOWS
            slWindowWin32* w32 = (slWindowWin32*)m_data.m_implementation;
            if (w32->m_hWnd)
            {
                DestroyWindow((HWND)w32->m_hWnd);
                UnregisterClass(w32->m_className, GetModuleHandle(0));
            }
    #endif
            slMemory::free(m_data.m_implementation);
        }
    }
    

    Some methods...

    Set borderless window

    void slWindow::SetBorderless(bool v)
    {
        SL_ASSERT_ST(m_data.m_implementation);
    
    #ifdef SL_PLATFORM_WINDOWS
        slWindowWin32* w32 = (slWindowWin32*)m_data.m_implementation;
        if (v)
            SetWindowLongPtr(w32->m_hWnd, GWL_STYLE, WS_POPUP);
        else
            SetWindowLongPtr(w32->m_hWnd, GWL_STYLE, w32->m_style);
        SetWindowPos(w32->m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
        ShowWindow(w32->m_hWnd, SW_NORMAL);
    #endif
    }
    

    Callbacks

    void slWindow::OnMaximize()
    {
        SL_ASSERT_ST(m_data.m_cb);
        m_data.m_cb->OnMaximize(this);
    }
    

    WndProc too big. Keyboard and mouse input will come from window messages:

    For mouse

    case WM_INPUT:
    

    For keyboard

    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
    case WM_KEYDOWN:
    case WM_KEYUP:
    

    For typed character

    case WM_CHAR:
    

    Input

    When you need input, just create struct and store all states in variables.

    struct slInputData
    {
    	slPoint mousePosition;
    	slPoint mousePositionOld; // for mouseMoveDelta
    
    	slPoint mouseMoveDelta;
    	//slPoint mouseMoveDeltaOld;
    
    	float   mouseWheelDelta = 0.f;
    	//float   mouseWheelDeltaOld = 0.f;
    
    	uint32_t mouseButtonFlags1;/*MBFL...*/
    	uint32_t mouseButtonFlags2;/*MBFL... HOLD, DOUBLE*/
    
    	//uint32_t LMBClickCount;
    
    	char32_t character = 0;
    
    	uint64_t keyFlagsHit[2];
    	uint64_t keyFlagsHold[2];
    	uint64_t keyFlagsRelease[2];
    
    	/*keyboardModifier == KBMOD_SHIFT*/
    	uint8_t keyboardModifier; /*KBMOD...*/
    };
    

    I use bits to know if button was hit, hold or released. `mouseButtonFlags1` for hit and release. `mouseButtonFlags2` for hold.

    For keyboard I use two uint64_t

    keyboardModifier is CTRL, SHIFT, ALT and their combinations.

    Input class is a static class

    class slInput
    {
    public:
    	static slInputData* GetData();
    	
    	//slInputData::KEY_
    	static void SetKeyHit(uint32_t, bool);
    	static void SetKeyHold(uint32_t, bool);
    	static void SetKeyRelease(uint32_t, bool);
    
    	//slInputData::KEY_
    	static bool IsKeyHit(uint32_t);
    	static bool IsKeyHold(uint32_t);
    	static bool IsKeyRelease(uint32_t);
    
    	static void EnableLMBDown();
    	static void EnableLMBHold();
    	static void EnableLMBUp();
    	static void EnableRMBDown();
    	static void EnableRMBHold();
    	static void EnableRMBUp();
    	static void EnableMMBDown();
    	static void EnableMMBHold();
    	static void EnableMMBUp();
    	static void EnableX1MBDown();
    	static void EnableX1MBHold();
    	static void EnableX1MBUp();
    	static void EnableX2MBDown();
    	static void EnableX2MBHold();
    	static void EnableX2MBUp();
    
    	static void DisableLMBDown();
    	static void DisableLMBHold();
    	static void DisableLMBUp();
    	static void DisableRMBDown();
    	static void DisableRMBHold();
    	static void DisableRMBUp();
    	static void DisableMMBDown();
    	static void DisableMMBHold();
    	static void DisableMMBUp();
    	static void DisableX1MBDown();
    	static void DisableX1MBHold();
    	static void DisableX1MBUp();
    	static void DisableX2MBDown();
    	static void DisableX2MBHold();
    	static void DisableX2MBUp();
    
    	static bool IsLMBHit();
    	static bool IsLMBHold();
    	static bool IsLMBRelease();
    	static bool IsRMBHit();
    	static bool IsRMBHold();
    	static bool IsRMBRelease();
    	static bool IsMMBHit();
    	static bool IsMMBHold();
    	static bool IsMMBRelease();
    	static bool IsX1MBHit();
    	static bool IsX1MBHold();
    	static bool IsX1MBRelease();
    	static bool IsX2MBHit();
    	static bool IsX2MBHold();
    	static bool IsX2MBRelease();
    
    	// keyboard button code
    	enum : uint32_t {
    		KEY_NONE = 0,
    		KEY_BACKSPACE = 8,
    		KEY_TAB = 9,
    		... big list of keycodes
    		KEY_end__,
    	};
    };
    

    For using bits (for keyboard) I need bit masks and array for fast acces to this masks.

    // masks
    enum : uint64_t {
    	KBIT_NONE = 0,
    	KBIT_BACKSPACE = 0x1,
    	KBIT_TAB = 0x2,
    	...
    
    static const uint64_t g_keyToBin[] =
    {
    	0, //KEY_NONE = 0,
    	0, 0, 0, 0, 0, 0, 0,
    	KBIT_BACKSPACE,	//KEY_BACKSPACE = 8,
    	KBIT_TAB,	//KEY_TAB = 9,
    	...
    

    How it works

    void slInput::SetKeyHit(uint32_t k, bool v)
    {
    	SL_ASSERT_ST(g_framework);
    	uint64_t bit = g_keyToBin[k];
    	if (k < slInput::KEY_NUM_1)
    	{
    		if (v)
    			g_framework->m_input.keyFlagsHit[0] |= bit;
    		else
    			g_framework->m_input.keyFlagsHit[0] &= ~bit;
    	}
    	else
    	{
    		if (v)
    			g_framework->m_input.keyFlagsHit[1] |= bit;
    		else
    			g_framework->m_input.keyFlagsHit[1] &= ~bit;
    	}
    }
    
    bool slInput::IsKeyHit(uint32_t i)
    {
    	SL_ASSERT_ST(g_framework);
    	uint64_t bit = g_keyToBin[i];
    	if (i < slInput::KEY_NUM_1) // первый uint64_t
    		return (g_framework->m_input.keyFlagsHit[0] & bit);
    	else
    		return (g_framework->m_input.keyFlagsHit[1] & bit);
    }
    

    For mouse I need to create Enable... Disable... functions. Because I need to remove bits (for button down and up) every new frame. Or I need to use addiditional variable for this. Maybe I will do this in future. `if`is very expensive thing, I don't want to use function like this SetLMBDown(bool)

    void slInput::EnableLMBDown() { g_framework->m_input.mouseButtonFlags1 |= MBFL_LMBDOWN; }
    void slInput::EnableLMBHold() { g_framework->m_input.mouseButtonFlags2 |= MBFL_LMBHOLD; }
    void slInput::EnableLMBUp() { g_framework->m_input.mouseButtonFlags1 |= MBFL_LMBUP; }
    
    void slInput::DisableLMBDown() { g_framework->m_input.mouseButtonFlags1 &= ~MBFL_LMBDOWN; }
    void slInput::DisableLMBHold() { g_framework->m_input.mouseButtonFlags2 &= ~MBFL_LMBHOLD; }
    void slInput::DisableLMBUp()   { g_framework->m_input.mouseButtonFlags1 &= ~MBFL_LMBUP; }
    
    bool slInput::IsLMBHit() { return (g_framework->m_input.mouseButtonFlags1 & MBFL_LMBDOWN) == MBFL_LMBDOWN; }
    bool slInput::IsLMBHold() { return (g_framework->m_input.mouseButtonFlags2 & MBFL_LMBHOLD) == MBFL_LMBHOLD; }
    bool slInput::IsLMBRelease() { return (g_framework->m_input.mouseButtonFlags1 & MBFL_LMBUP) == MBFL_LMBUP; }
    

    Update functions.

    slInputUpdatePre called before winapi wndproc.

    slInputUpdatePost called after winapi wndproc.

    void slInputUpdatePre(slInputData* id)
    {
    	id->mousePositionOld = id->mousePosition;
    	id->mouseButtonFlags1 = 0;
    	id->mouseWheelDelta = 0;
    	id->character = 0;
    	slInput::DisableLMBDown();
    	slInput::DisableRMBDown();
    	slInput::DisableMMBDown();
    	slInput::DisableX1MBDown();
    	slInput::DisableX2MBDown();
    	slInput::DisableLMBUp();
    	slInput::DisableRMBUp();
    	slInput::DisableMMBUp();
    	slInput::DisableX1MBUp();
    	slInput::DisableX2MBUp();
    	id->keyFlagsHit[0] = 0;
    	id->keyFlagsHit[1] = 0;
    	id->keyFlagsRelease[0] = 0;
    	id->keyFlagsRelease[1] = 0;
    }
    
    void slInputUpdatePost(slInputData* id)
    {
    	unsigned int ctrl_shift_alt = 0;
    	if (slInput::IsKeyHold(slInput::KEY_ALT) || slInput::IsKeyHold(slInput::KEY_RALT))
    		ctrl_shift_alt |= 1;
    
    	if (slInput::IsKeyHold(slInput::KEY_LSHIFT) || slInput::IsKeyHold(slInput::KEY_RSHIFT))
    		ctrl_shift_alt |= 2;
    
    	if (slInput::IsKeyHold(slInput::KEY_LCTRL) || slInput::IsKeyHold(slInput::KEY_RCTRL))
    		ctrl_shift_alt |= 4;
    
    	switch (ctrl_shift_alt)
    	{
    	default:
    	case 0:  id->keyboardModifier = KBMOD_clear;         break;
    	case 1:  id->keyboardModifier = KBMOD_ALT;           break;
    	case 2:  id->keyboardModifier = KBMOD_SHIFT;         break;
    	case 3:  id->keyboardModifier = KBMOD_SHIFTALT;      break;
    	case 4:  id->keyboardModifier = KBMOD_CTRL;          break;
    	case 5:  id->keyboardModifier = KBMOD_CTRLALT;       break;
    	case 6:  id->keyboardModifier = KBMOD_CTRLSHIFT;     break;
    	case 7:  id->keyboardModifier = KBMOD_CTRLSHIFTALT;  break;
    	}
    
    	id->mouseMoveDelta.x = id->mousePosition.x - id->mousePositionOld.x;
    	id->mouseMoveDelta.y = id->mousePosition.y - id->mousePositionOld.y;
    }
    

    File located here SlowLib\src\slowlib.base\input\slInput.cpp

    All states are filled in WndProc SlowLib\src\slowlib.base\system\slWindow.cpp

    Download code