c33a97955f
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
120 lines
3.6 KiB
C++
120 lines
3.6 KiB
C++
#include "pch.h"
|
|
#include "AppState.h"
|
|
#include "../../types/inc/utils.hpp"
|
|
|
|
using namespace winrt;
|
|
using namespace winrt::Windows::Foundation;
|
|
using namespace ::Microsoft::Console;
|
|
|
|
void AppState::_setupConsole()
|
|
{
|
|
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
hInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
DWORD dwMode = 0;
|
|
GetConsoleMode(hOutput, &dwMode);
|
|
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
SetConsoleMode(hOutput, dwMode);
|
|
}
|
|
|
|
void AppState::initializeState()
|
|
{
|
|
// Initialize the console handles
|
|
_setupConsole();
|
|
|
|
// Set up WinRT
|
|
init_apartment();
|
|
}
|
|
|
|
bool AppState::areWeTheKing(const bool logPIDs)
|
|
{
|
|
auto kingPID = monarch.GetPID();
|
|
auto ourPID = GetCurrentProcessId();
|
|
if (logPIDs)
|
|
{
|
|
if (ourPID == kingPID)
|
|
{
|
|
printf(fmt::format("We're the\x1b[33m king\x1b[m - our PID is {}\n", ourPID).c_str());
|
|
}
|
|
else
|
|
{
|
|
printf(fmt::format("We're a lowly peasant - the king is {}\n", kingPID).c_str());
|
|
}
|
|
}
|
|
return (ourPID == kingPID);
|
|
}
|
|
|
|
void AppState::remindKingWhoTheyAre(const winrt::MonarchPeasantSample::IPeasant& iPeasant)
|
|
{
|
|
winrt::com_ptr<MonarchPeasantSample::implementation::Monarch> monarchImpl;
|
|
monarchImpl.copy_from(winrt::get_self<MonarchPeasantSample::implementation::Monarch>(monarch));
|
|
if (monarchImpl)
|
|
{
|
|
auto ourID = iPeasant.GetID();
|
|
monarchImpl->SetSelfID(ourID);
|
|
monarchImpl->AddPeasant(iPeasant);
|
|
printf("The king is peasant #%lld\n", ourID);
|
|
}
|
|
else
|
|
{
|
|
printf("Shoot, we wanted to be able to get the monarchImpl here but couldnt\n");
|
|
}
|
|
}
|
|
|
|
winrt::MonarchPeasantSample::Monarch AppState::instantiateMonarch()
|
|
{
|
|
// Heads up! This only works because we're using
|
|
// "metadata-based-marshalling" for our WinRT types. THat means the OS is
|
|
// using the .winmd file we generate to figure out the proxy/stub
|
|
// definitions for our types automatically. This only works in the following
|
|
// cases:
|
|
//
|
|
// * If we're running unpackaged: the .winmd but be a sibling of the .exe
|
|
// * If we're running packaged: the .winmd must be in the package root
|
|
auto monarch = create_instance<winrt::MonarchPeasantSample::Monarch>(Monarch_clsid,
|
|
CLSCTX_LOCAL_SERVER);
|
|
return monarch;
|
|
}
|
|
|
|
MonarchPeasantSample::IPeasant AppState::_createOurPeasant()
|
|
{
|
|
auto peasant = winrt::make_self<MonarchPeasantSample::implementation::Peasant>();
|
|
auto ourID = monarch.AddPeasant(*peasant);
|
|
printf("The monarch assigned us the ID %llu\n", ourID);
|
|
|
|
if (areWeTheKing())
|
|
{
|
|
remindKingWhoTheyAre(*peasant);
|
|
}
|
|
|
|
return *peasant;
|
|
}
|
|
|
|
void AppState::createMonarch()
|
|
{
|
|
monarch = AppState::instantiateMonarch();
|
|
}
|
|
|
|
// return true to exit early, false if we should continue into the main loop
|
|
bool AppState::processCommandline()
|
|
{
|
|
const bool isKing = areWeTheKing(false);
|
|
// If we're the king, we _definitely_ want to process the arguments, we were
|
|
// launched with them!
|
|
//
|
|
// Otherwise, the King will tell us if we should make a new window
|
|
const bool createNewWindow = isKing || monarch.ProposeCommandline({ args }, { L"placeholder CWD" });
|
|
|
|
if (createNewWindow)
|
|
{
|
|
peasant = _createOurPeasant();
|
|
peasant.ExecuteCommandline({ args }, { L"placeholder CWD" });
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n");
|
|
}
|
|
|
|
return true;
|
|
}
|