#include <interaction_lib/InteractionLib.h>
#include <interaction_lib/misc/InteractionLibPtr.h>
#include <windows.h>
#include <vector>
#include <functional>
#define DISPLAY_ENUMERATION_HELPER_IMPLEMENTATION
#include <DisplayEnumerationHelper.h>
class Window final
{
public:
void Create();
void Repaint();
std::function<void(int&)> OnPaint;
std::function<void(float, float)> OnMove;
std::function<void(float, float)> OnSize;
private:
static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
HWND m_hwnd{};
};
void Window::Create()
{
WNDCLASSA wc{};
wc.hInstance = GetModuleHandle(nullptr);
wc.lpfnWndProc = WindowProcedure;
wc.lpszClassName = "TobiiInteractorWindow";
RegisterClassA(&wc);
CreateWindowA(wc.lpszClassName, "Interactor Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, GetModuleHandle(nullptr), this);
}
void Window::Repaint()
{
InvalidateRect(m_hwnd, NULL, FALSE);
}
LRESULT Window::WindowProcedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
static Window* window{};
switch (msg)
{
case WM_CREATE:
{
CREATESTRUCTA* cs{reinterpret_cast<CREATESTRUCTA*>(lp)};
window = static_cast<Window*>(cs->lpCreateParams);
window->m_hwnd = hwnd;
return 0;
}
case WM_MOVE:
{
if (window->OnMove)
{
POINT offset{};
ClientToScreen(hwnd, &offset);
const float x = static_cast<float>(offset.x);
const float y = static_cast<float>(offset.y);
window->OnMove(x, y);
}
return 0;
}
case WM_SIZE:
{
if (window->OnSize)
{
RECT rect{};
GetClientRect(hwnd, &rect);
const float width = static_cast<float>(rect.right - rect.left);
const float height = static_cast<float>(rect.bottom - rect.top);
window->OnSize(width, height);
}
return 0;
}
case WM_ERASEBKGND:
{
return 1;
}
case WM_PAINT:
{
PAINTSTRUCT ps{};
HDC hdc{BeginPaint(hwnd, &ps)};
int sysColorIndex = COLOR_WINDOW;
if (window->OnPaint)
window->OnPaint(sysColorIndex);
FillRect(hdc, &ps.rcPaint, GetSysColorBrush(sysColorIndex));
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
{
window->m_hwnd = nullptr;
window = nullptr;
PostQuitMessage(0);
return 0;
}
default:
{
return DefWindowProc(hwnd, msg, wp, lp);
}
}
}
bool ProcessMessages()
{
MSG msg{};
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
return false;
}
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return true;
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
IL::UniqueInteractionLibPtr intlib{IL::CreateInteractionLib(IL::FieldOfUse::Interactive)};
auto GetMonitorDisplayAreas = []
{
std::vector<MonitorDisplayAreaA> areas{5};
int found{};
EnumerateDisplayAreasA(areas.data(), &found, static_cast<int>(areas.size()));
areas.resize(found);
return areas;
};
for (const auto& area : GetMonitorDisplayAreas())
{
intlib->CoordinateTransformAddOrUpdateDisplayArea(
area.wVirtual, area.hVirtual,
area.wVirtual, area.hVirtual,
area.xVirtual, area.yVirtual,
area.monitorId);
}
Window window;
window.OnMove = [&] (float x, float y)
{
intlib->CoordinateTransformSetOriginOffset(x, y);
};
window.OnSize = [&] (float width, float height)
{
intlib->BeginInteractorUpdates();
intlib->AddOrUpdateInteractor(0, {0.0f, 0.0f, width, height}, 0.0f);
intlib->CommitInteractorUpdates();
};
struct GazeFocusEventContext final
{
bool focused;
Window& window;
} gazeFocusEventContext{false, window};
intlib->SubscribeGazeFocusEvents([](IL::GazeFocusEvent evt, void* context)
{
auto gazeFocusEventContext{static_cast<GazeFocusEventContext*>(context)};
gazeFocusEventContext->focused = evt.hasFocus;
gazeFocusEventContext->window.Repaint();
}, &gazeFocusEventContext);
window.OnPaint = [&] (int& sysColorIndex)
{
sysColorIndex = gazeFocusEventContext.focused ? COLOR_HIGHLIGHT : COLOR_WINDOW;
};
window.Create();
while (ProcessMessages())
{
intlib->Update();
}
return 0;
}