From be2926499af2e372c313d965533be3a7ee0dcf4d Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Mon, 16 May 2016 11:19:10 +0200 Subject: add tobii eyex sdk --- .../samples/ActivatableBoardGame/BoardWindow.cpp | 488 +++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100755 Tobii EyeX/samples/ActivatableBoardGame/BoardWindow.cpp (limited to 'Tobii EyeX/samples/ActivatableBoardGame/BoardWindow.cpp') diff --git a/Tobii EyeX/samples/ActivatableBoardGame/BoardWindow.cpp b/Tobii EyeX/samples/ActivatableBoardGame/BoardWindow.cpp new file mode 100755 index 0000000..7f8b9a7 --- /dev/null +++ b/Tobii EyeX/samples/ActivatableBoardGame/BoardWindow.cpp @@ -0,0 +1,488 @@ +/* + * ActivatableBoardGame sample: + * This is an example that demonstrates the use of Activatable interactors in the context of a board game. + * + * Copyright 2013 Tobii Technology AB. All rights reserved. + */ + +#include "stdafx.h" +#include "BoardWindow.h" +#include "Board.h" +#include "resource.h" + +// window messages used for notifications from the EyeXHost. +#define WM_EYEX_HOST_STATUS_CHANGED WM_USER + 0 +#define WM_FOCUSED_REGION_CHANGED WM_USER + 1 +#define WM_REGION_ACTIVATED WM_USER + 2 + +// color scheme. +const Gdiplus::Color BoardWindow::BackgroundGazeInteractionFunctionalColor = Gdiplus::Color(134, 152, 172); // blue +const Gdiplus::Color BoardWindow::BackgroundMouseOnlyColor = Gdiplus::Color(164, 156, 153); // grey +const Gdiplus::Color BoardWindow::GridColor = Gdiplus::Color(110, 57, 88, 109); // dark blue +const Gdiplus::Color BoardWindow::MarkerColor = Gdiplus::Color(242, 233, 216); // off white +const Gdiplus::Color BoardWindow::HighlightColor = Gdiplus::Color(20, 242, 233, 216); // transparent off white +const Gdiplus::Color BoardWindow::WonMessageColor = Gdiplus::Color(200, 64, 82, 60); // transparent dark green +const Gdiplus::Color BoardWindow::LostMessageColor = Gdiplus::Color(200, 155, 109, 135); // transparent grape + +// constants. +const int BoardWindow::SquareSize = 100; +const int BoardWindow::MarkerMargin = 14; +const int BoardWindow::MarkerDiameter = SquareSize - MarkerMargin * 2; +const TCHAR* BoardWindow::WindowClassName = _T("GobangBoard"); + +BoardWindow::BoardWindow(Board& board, HINSTANCE hInstance, int nCmdShow) + : _board(board), _hInstance(hInstance), _hWnd(0), _hMenu(0), _leftMargin(0), _upperMargin(0) +{ + // create the window instance. + _hWnd = CreateWindow( + WindowClassName, + _T("Gobang!"), + WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, + CW_USEDEFAULT, + CW_USEDEFAULT, + (_board.Size() + 1) * SquareSize, + (_board.Size() + 1) * SquareSize + 40, + NULL, + NULL, + hInstance, + NULL); + + if (!_hWnd) + { + throw new std::runtime_error("Could not create main window."); + } + + _hMenu = GetMenu(_hWnd); + + // write the "this" pointer to the extra window memory so that we can reference back to this + // object from the window function. + SetWindowLongPtr(_hWnd, 0, (LONG_PTR)this); + + ShowWindow(_hWnd, nCmdShow); + UpdateWindow(_hWnd); + + _board.RegisterBoardChangedObserver(this); + + // initialize the EyeX host and the activatable regions. + _eyeXHost.Init(_hWnd, WM_EYEX_HOST_STATUS_CHANGED, WM_FOCUSED_REGION_CHANGED, WM_REGION_ACTIVATED); + UpdateActivatableRegions(); +} + +BoardWindow::~BoardWindow() +{ +} + +void BoardWindow::RegisterWindowClass(HINSTANCE hInstance) +{ + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = sizeof(void*); // make room for the "this" pointer. + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GOBANG)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = 0; + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_GOBANG); + wcex.lpszClassName = WindowClassName; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + RegisterClassEx(&wcex); +} + +// Callback function invoked when the Board has changed. +void BoardWindow::SubjectChanged() +{ + InvalidateRect(_hWnd, NULL, FALSE); + UpdateActivatableRegions(); +} + +// Callback function invoked from the message loop to process a Windows message. +LRESULT CALLBACK BoardWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + + // read the "this" pointer from extra window memory. + BoardWindow* instance = reinterpret_cast(GetWindowLongPtr(hWnd, 0)); + + switch (message) + { + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + // Parse the menu selections: + switch (wmId) + { + case IDM_NEWGAME: + instance->OnNewGameClicked(); + break; + + case IDM_ABOUT: + DialogBox(instance->_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + + case IDM_EXIT: + DestroyWindow(hWnd); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_LBUTTONDOWN: + { + // mouse button pressed; possibly on a board square. + auto point = MAKEPOINTS(lParam); + instance->OnSquareClicked(point); + } + break; + + case WM_PAINT: + instance->UpdateMargins(); + hdc = BeginPaint(hWnd, &ps); + instance->OnDraw(hdc); + EndPaint(hWnd, &ps); + break; + + case WM_ERASEBKGND: + // no background erasing needed since our OnDraw method draws the entire window. + return TRUE; + + case WM_EYEX_HOST_STATUS_CHANGED: + // the background color of the board indicates whether we have a working connection to the engine or not. + // so, when the status changes, we force a redraw of the window. + InvalidateRect(hWnd, 0, FALSE); + break; + + case WM_FOCUSED_REGION_CHANGED: + // redraw to display the new focus state. + InvalidateRect(hWnd, 0, FALSE); + break; + + case WM_REGION_ACTIVATED: + instance->OnSquareActivated((UINT)wParam); + break; + + case WM_WINDOWPOSCHANGED: + // the window was moved, so we need to refresh the screen bounds of our activatable regions. + instance->UpdateActivatableRegions(); + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_KEYUP: + // trigger an activation command when space is released. + if (VK_SPACE == wParam) + { + instance->_eyeXHost.TriggerActivation(); + } + break; + + case WM_KEYDOWN: + // trigger activation mode on when space is pressed down (so activation focus changed events are received, and a highlight can be drawn). + if (VK_SPACE == wParam) + { + instance->_eyeXHost.TriggerActivationModeOn(); + } + break; + + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; +} + +void BoardWindow::OnDraw(HDC hdc) +{ + // use double-buffer drawing to speed up the drawing and reduce flicker. + // (the drawing is performed on a bitmap, which is then blitted to the screen in one fell swoop.) + + RECT rect; + GetClientRect(_hWnd, &rect); + Gdiplus::Bitmap bitmap(rect.right - rect.left, rect.bottom - rect.top); + Gdiplus::Graphics graphics(&bitmap); + + // draw the background in a color which depends on the state of the connection to the engine. + // blue if the connection is functional, gray otherwise. + Gdiplus::Color backgroundColor(BackgroundGazeInteractionFunctionalColor); + if (!_eyeXHost.IsFunctional()) + { + backgroundColor = BackgroundMouseOnlyColor; + } + Gdiplus::SolidBrush backgroundBrush(backgroundColor); + graphics.FillRectangle(&backgroundBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + + // draw the grid + DrawGrid(&graphics); + + // draw the markers + Gdiplus::Pen markerPen(MarkerColor, 5.0F); + for (int row = 0; row < _board.Size(); row++) + { + for (int col = 0; col < _board.Size(); col++) + { + switch (_board.GetMarkerAt(Board::Position(row, col))) + { + case Board::Marker::X: + DrawXMarker(&graphics, &markerPen, row, col); + break; + + case Board::Marker::O: + DrawOMarker(&graphics, &markerPen, row, col); + break; + } + } + } + + // draw the focus highlight + int focusedRegionId = _eyeXHost.GetFocusedRegionId(); + if (focusedRegionId >= 0) + { + DrawHighlight(&graphics, RegionIdToBoardRow(focusedRegionId), RegionIdToBoardColumn(focusedRegionId)); + } + + // draw the game over message + if (_board.IsGameOver()) + { + WCHAR* message; + Gdiplus::Color messageColor(0,0,0); + switch (_board.GetWinner()) + { + case Board::Marker::X: + message = L"YOU WON :)"; + messageColor = WonMessageColor; + break; + + case Board::Marker::O: + message = L"YOU LOST :("; + messageColor = LostMessageColor; + break; + + default: + message = L"DRAW"; + messageColor = LostMessageColor; + break; + } + + DrawGameOver(&graphics, message, messageColor); + } + + Gdiplus::Graphics screen(hdc); + screen.DrawImage(&bitmap, 0, 0); +} + +void BoardWindow::DrawGrid(Gdiplus::Graphics *graphics) +{ + const int lineMargin = 14; + + Gdiplus::Pen gridPen(GridColor); + gridPen.SetDashStyle(Gdiplus::DashStyleDash); + + // horizontal lines + auto x0 = _leftMargin - lineMargin; + auto x1 = _leftMargin + _board.Size() * SquareSize + lineMargin; + for (int i = 0; i <= _board.Size(); i++) + { + auto y = _upperMargin + i * SquareSize; + graphics->DrawLine(&gridPen, x0, y, x1, y); + } + + // vertical lines + auto y0 = _upperMargin - lineMargin; + auto y1 = _upperMargin + _board.Size() * SquareSize + lineMargin; + for (int j = 0; j <= _board.Size(); j++) + { + auto x = _leftMargin + j * SquareSize; + graphics->DrawLine(&gridPen, x, y0, x, y1); + } + + // dots that guide the eye to the center of the square + Gdiplus::SolidBrush gridBrush(GridColor); + for (int i = 0; i <= _board.Size(); i++) + { + for (int j = 0; j <= _board.Size(); j++) + { + auto x = _leftMargin + (j + 0.5) * SquareSize; + auto y = _upperMargin + (i + 0.5) * SquareSize; + graphics->FillEllipse(&gridBrush, (Gdiplus::REAL)(x - 1), (Gdiplus::REAL)(y - 1), (Gdiplus::REAL)3, (Gdiplus::REAL)3); + } + } +} + +void BoardWindow::DrawHighlight(Gdiplus::Graphics *graphics, INT row, INT column) +{ + Gdiplus::SolidBrush highlightBrush(HighlightColor); + + int x = BoardToClientAreaX(column) + 1; + int y = BoardToClientAreaY(row) + 1; + + graphics->FillRectangle(&highlightBrush, x, y, SquareSize - 1, SquareSize - 1); +} + +void BoardWindow::DrawOMarker(Gdiplus::Graphics *graphics, Gdiplus::Pen *markerPen, INT row, INT column) +{ + int x = BoardToClientAreaX(column) + MarkerMargin; + int y = BoardToClientAreaY(row) + MarkerMargin; + + graphics->DrawEllipse(markerPen, x, y, MarkerDiameter, MarkerDiameter); +} + +void BoardWindow::DrawXMarker(Gdiplus::Graphics *graphics, Gdiplus::Pen *markerPen, INT row, INT column) +{ + int x = BoardToClientAreaX(column) + MarkerMargin; + int y = BoardToClientAreaY(row) + MarkerMargin; + + graphics->DrawLine(markerPen, x, y + MarkerDiameter, x + MarkerDiameter, y); + graphics->DrawLine(markerPen, x, y, x + MarkerDiameter, y + MarkerDiameter); +} + +void BoardWindow::DrawGameOver(Gdiplus::Graphics *graphics, WCHAR* message, const Gdiplus::Color& messageColor) +{ + Gdiplus::Font myFont(L"Arial", 72); + + RECT rect; + GetClientRect(_hWnd, &rect); + Gdiplus::RectF layoutRect((float)rect.left, (float)rect.top, (float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); + + Gdiplus::StringFormat format; + format.SetAlignment(Gdiplus::StringAlignmentCenter); + format.SetLineAlignment(Gdiplus::StringAlignmentCenter); + + Gdiplus::SolidBrush messageBrush(messageColor); + + graphics->DrawString( + message, + (INT)wcslen(message), + &myFont, + layoutRect, + &format, + &messageBrush); +} + +INT BoardWindow::BoardToClientAreaX(INT column) +{ + return column * SquareSize + _leftMargin; +} + +INT BoardWindow::BoardToClientAreaY(INT row) +{ + return row * SquareSize + _upperMargin; +} + +INT BoardWindow::ClientAreaToBoardColumn(INT x) +{ + return (x - _upperMargin) / SquareSize; +} + +INT BoardWindow::ClientAreaToBoardRow(INT y) +{ + return (y - _leftMargin) / SquareSize; +} + +int BoardWindow::BoardPositionToRegionId(int row, int column) +{ + return column + row * _board.Size(); +} + +int BoardWindow::RegionIdToBoardRow(int regionId) +{ + return regionId / _board.Size(); +} + +int BoardWindow::RegionIdToBoardColumn(int regionId) +{ + return regionId % _board.Size(); +} + +void BoardWindow::UpdateMargins() +{ + RECT rect; + GetClientRect(_hWnd, &rect); + _upperMargin = ((rect.bottom - rect.top) - _board.Size() * SquareSize) / 2; + _leftMargin = ((rect.right - rect.left) - _board.Size() * SquareSize) / 2; +} + +void BoardWindow::OnSquareClicked(POINTS point) +{ + MakeHumanPlayerMove(ClientAreaToBoardRow(point.y), ClientAreaToBoardColumn(point.x)); +} + +void BoardWindow::OnSquareActivated(UINT regionId) +{ + MakeHumanPlayerMove(RegionIdToBoardRow(regionId), RegionIdToBoardColumn(regionId)); +} + +void BoardWindow::MakeHumanPlayerMove(int row, int column) +{ + Board::Position position(row, column); + if (_board.CanMakeMoveAt(position)) + { + _board.MakeHumanPlayerMove(position); + } +} + +void BoardWindow::UpdateActivatableRegions() +{ + std::vector regions; + + if (!_board.IsGameOver()) + { + for (int row = 0; row < _board.Size(); row++) + { + for (int column = 0; column < _board.Size(); column++) + { + POINT point; + point.x = BoardToClientAreaX(column); + point.y = BoardToClientAreaY(row); + ClientToScreen(_hWnd, &point); + + RECT bounds; + bounds.left = point.x; + bounds.top = point.y; + bounds.right = point.x + SquareSize; + bounds.bottom = point.y + SquareSize; + + regions.push_back(EyeXHost::ActivatableRegion(BoardPositionToRegionId(row, column), bounds)); + } + } + } + + _eyeXHost.SetActivatableRegions(regions); +} + +void BoardWindow::OnNewGameClicked() +{ + _board.BeginNewGame(); + UpdateActivatableRegions(); +} + +// Message handler for about box. +INT_PTR CALLBACK BoardWindow::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + + return (INT_PTR)FALSE; +} -- cgit v1.2.3