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
Please enable JavaScript to view the comments powered by Disqus.