terminal/src/tools/MonarchPeasantSample/PeasantMain.cpp
Mike Griese c33a97955f
Add a Monarch/Peasant sample app (#8171)
This PR adds a sample monarch/peasant application. This is a type of
application where a single "Monarch" can coordinate the actions of multiple
other "Peasant" processes, as described by the specs in #7240 and #8135.

This project is intended to be a standalone sample of how the architecture would
work, without involving the entirety of the Windows Terminal build. Eventually,
this architecture will be incorporated into `wt.exe` itself, to enable scenarios
like:
* Run `wt` in the current window (#4472)
* Single Instance Mode (#2227)

For an example of this sample running, see the below GIF:

![monarch-peasant-sample-001](https://user-images.githubusercontent.com/18356694/98262202-f39b1500-1f4a-11eb-9220-4af4d922339f.gif)

This sample operates largely by printing to the console, to help the reader
understand how it's working through its logic.

I'm doing this mostly so we can have a _committed_ sample of this type of application, kinda like how VtPipeTerm is a sample ConPTY application. It's a lot easier to understand (& build on) when there aren't any window shenanigans, settings loading, Island instantiation, or anything else that the whole of `WindowsTerminal.exe` needs

* [x] I work here
* [x] This is sample code, so I'm not shipping tests for it.
* [x] Go see the doc over in #8135
2021-01-19 21:55:30 +00:00

109 lines
3.3 KiB
C++

#include "pch.h"
#include "AppState.h"
#include "../../types/inc/utils.hpp"
bool peasantReadInput(AppState& state)
{
DWORD cNumRead, i;
std::array<INPUT_RECORD, 128> irInBuf;
if (!ReadConsoleInput(state.hInput, // input buffer handle
irInBuf.data(), // buffer to read into
static_cast<DWORD>(irInBuf.size()), // size of read buffer
&cNumRead)) // number of records read
{
printf("\x1b[31mReadConsoleInput failed\x1b[m\n");
ExitProcess(0);
}
for (i = 0; i < cNumRead; i++)
{
switch (irInBuf[i].EventType)
{
case KEY_EVENT: // keyboard input
{
auto key = irInBuf[i].Event.KeyEvent;
if (key.bKeyDown == false &&
key.uChar.UnicodeChar == L'q')
{
return true;
}
else
{
printf("This window was activated\n");
winrt::com_ptr<winrt::MonarchPeasantSample::implementation::Peasant> peasantImpl;
peasantImpl.copy_from(winrt::get_self<winrt::MonarchPeasantSample::implementation::Peasant>(state.peasant));
if (peasantImpl)
{
peasantImpl->raiseActivatedEvent();
}
}
break;
}
case MOUSE_EVENT:
case WINDOW_BUFFER_SIZE_EVENT:
case FOCUS_EVENT:
case MENU_EVENT:
break;
default:
printf("\x1b[33mUnknown event from ReadConsoleInput - this is probably impossible\x1b[m\n");
ExitProcess(0);
break;
}
}
return false;
}
// Returns true if we want to just exit the application.
// Returns false if the monarch dies, and we need to elect a new one.
bool peasantAppLoop(AppState& state)
{
wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast<DWORD>(state.monarch.GetPID())) };
if (hMonarch.get() == nullptr)
{
const auto gle = GetLastError();
printf("\x1b[31mFailed to open the monarch process, error was %d\x1b[m\n", gle);
return false;
}
HANDLE handlesToWaitOn[2]{ hMonarch.get(), state.hInput };
bool exitRequested = false;
printf("Press `q` to quit\n");
while (!exitRequested)
{
auto waitResult = WaitForMultipleObjects(2, handlesToWaitOn, false, INFINITE);
switch (waitResult)
{
case WAIT_OBJECT_0 + 0: // handlesToWaitOn[0] was signaled
printf("THE KING IS \x1b[31mDEAD\x1b[m\n");
// Return false here - this will trigger us to find the new monarch
return false;
case WAIT_OBJECT_0 + 1: // handlesToWaitOn[1] was signaled
exitRequested = peasantReadInput(state);
break;
case WAIT_TIMEOUT:
printf("Wait timed out. This should be impossible.\n");
break;
// Return value is invalid.
default:
{
auto gle = GetLastError();
printf("WaitForMultipleObjects returned: %d\n", waitResult);
printf("Wait error: %d\n", gle);
ExitProcess(0);
}
}
}
printf("Bottom of peasantAppLoop\n");
return true;
}