Skip to content

Instantly share code, notes, and snippets.

@McMartin
Last active February 11, 2018 17:28
Show Gist options
  • Save McMartin/2b922c0c1ba69142cbf3519ae1f94e73 to your computer and use it in GitHub Desktop.
Save McMartin/2b922c0c1ba69142cbf3519ae1f94e73 to your computer and use it in GitHub Desktop.

Implementations of the Action-Model-View pattern:

  • with PDCurses
  • with Win32
#include <curses.h>
#define curses
#include <memory>
struct action
{
int key;
};
struct application
{
int y = 0;
int x = 0;
};
std::unique_ptr<application> update(application app, action a)
{
switch (a.key)
{
case 'q':
return nullptr;
case KEY_UP:
app.y--;
break;
case KEY_DOWN:
app.y++;
break;
case KEY_LEFT:
app.x--;
break;
case KEY_RIGHT:
app.x++;
break;
}
return std::make_unique<application>(std::move(app));
}
void draw(WINDOW* window, const application& app)
{
curses::wmove(window, app.y, app.x);
}
int main()
{
curses::initscr();
curses::curs_set(2);
curses::noecho();
curses::keypad(curses::stdscr, TRUE);
auto state = application{};
while (auto new_state = update(state, {curses::wgetch(curses::stdscr)}))
{
state = *new_state;
draw(curses::stdscr, state);
}
curses::curs_set(1);
curses::endwin();
return 0;
}
#include <windows.h>
#include <new>
#include <tuple>
struct action
{
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
};
struct application
{
int y = 0;
int x = 0;
};
bool operator==(const application& lhs, const application& rhs)
{
return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y);
}
bool operator!=(const application& lhs, const application& rhs)
{
return !(lhs == rhs);
}
application update(application app, action a)
{
switch (a.message)
{
case WM_KEYDOWN:
switch (a.wParam)
{
case VK_UP:
app.y -= 10;
break;
case VK_DOWN:
app.y += 10;
break;
case VK_LEFT:
app.x -= 10;
break;
case VK_RIGHT:
app.x += 10;
break;
}
break;
}
return app;
}
void draw(HWND hWnd, const application& app)
{
PAINTSTRUCT ps;
HDC hdc = ::BeginPaint(hWnd, &ps);
::TextOut(hdc, app.x, app.y, "X", 1);
::EndPaint(hWnd, &ps);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
auto pState = reinterpret_cast<application*>(::GetWindowLongPtr(hWnd, 0));
switch (message)
{
case WM_PAINT:
if (!pState)
{
return FALSE;
}
draw(hWnd, *pState);
return 0;
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
}
if (pState)
{
auto newState = update(*pState, {hWnd, message, wParam, lParam});
if (*pState != newState)
{
::InvalidateRect(hWnd, nullptr, TRUE); // trigger re-paint
}
*pState = newState;
}
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
WNDCLASSEX wcex{};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbWndExtra = sizeof(application*);
wcex.hInstance = hInstance;
wcex.hCursor = ::LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszClassName = "AMOV";
if (!::RegisterClassEx(&wcex))
{
::MessageBox(nullptr, "Call to RegisterClassEx failed!", nullptr, 0);
return 1;
}
HWND hWnd =
::CreateWindow("AMOV", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 250,
250, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
::MessageBox(nullptr, "Call to CreateWindow failed!", nullptr, 0);
return 1;
}
application state;
::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(&state));
::ShowWindow(hWnd, nCmdShow);
::UpdateWindow(hWnd);
MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0) > 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return (int)msg.wParam;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment