Add `Microsoft.Terminal.Remoting.dll` (#8607)
Adds a `Microsoft.Terminal.Remoting.dll` to our solution. This DLL will
be responsible for all the Monarch/Peasant work that's been described in
#7240 & #8135.
This PR does _not_ implement the Monarch/Peasant architecture in any
significant way. The goal of this PR is to just to establish the project
layout, and the most basic connections. This should make reviewing the
actual meat of the implementation (in a later PR) easier. It will also
give us the opportunity to include some of the basic weird things we're
doing (with `CoRegisterClass`) in the Terminal _now_, and get them
selfhosted, before building on them too much.
This PR does have windows registering the `Monarch` class with COM. When
windows are created, they'll as the Monarch if they should create a new
window or not. In this PR, the Monarch will always reply "yes, please
make a new window".
Similar to other projects in our solution, we're adding 3 projects here:
* `Microsoft.Terminal.Remoting.lib`: the actual implementation, as a
static lib.
* `Microsoft.Terminal.Remoting.dll`: The implementation linked as a DLL,
for use in `WindowsTerminal.exe`.
* `Remoting.UnitTests.dll`: A unit test dll that links with the static
lib.
There are plenty of TODOs scattered about the code. Clearly, most of
this isn't implemented yet, but I do have more WIP branches. I'm using
[`projects/5`](https://github.com/microsoft/terminal/projects/5) as my
notation for TODOs that are too small for an issue, but are part of the
whole Process Model 2.0 work.
## References
* #5000 - this is the process model megathread
* #7240 - The process model 2.0 spec.
* #8135 - the window management spec. (please review me, I have 0/3
signoffs even after the discussion we had 😢)
* #8171 - the Monarch/peasant sample. (please review me, I have 1/2)
## PR Checklist
* [x] Closes nothing, this is just infrastructure
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
2021-01-07 23:59:37 +01:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "Peasant.h"
|
|
|
|
#include "CommandlineArgs.h"
|
|
|
|
#include "Peasant.g.cpp"
|
|
|
|
#include "../../types/inc/utils.hpp"
|
|
|
|
|
|
|
|
using namespace winrt;
|
|
|
|
using namespace winrt::Microsoft::Terminal;
|
|
|
|
using namespace winrt::Windows::Foundation;
|
|
|
|
using namespace ::Microsoft::Console;
|
|
|
|
|
|
|
|
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|
|
|
{
|
|
|
|
Peasant::Peasant() :
|
|
|
|
_ourPID{ GetCurrentProcessId() }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a private constructor to be used in unit tests, where we don't
|
|
|
|
// want each Peasant to necessarily use the current PID.
|
|
|
|
Peasant::Peasant(const uint64_t testPID) :
|
|
|
|
_ourPID{ testPID }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Peasant::AssignID(uint64_t id)
|
|
|
|
{
|
|
|
|
_id = id;
|
|
|
|
}
|
|
|
|
uint64_t Peasant::GetID()
|
|
|
|
{
|
|
|
|
return _id;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Peasant::GetPID()
|
|
|
|
{
|
|
|
|
return _ourPID;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args)
|
|
|
|
{
|
|
|
|
// If this is the first set of args we were ever told about, stash them
|
|
|
|
// away. We'll need to get at them later, when we setup the startup
|
|
|
|
// actions for the window.
|
|
|
|
if (_initialArgs == nullptr)
|
|
|
|
{
|
|
|
|
_initialArgs = args;
|
|
|
|
}
|
|
|
|
|
Add support for running a commandline in another WT window (#8898)
## Summary of the Pull Request
**If you're reading this PR and haven't signed off on #8135, go there first.**
![window-management-000](https://user-images.githubusercontent.com/18356694/103932910-25199380-50e8-11eb-97e3-594a31da62d2.gif)
This provides the basic parts of the implementation of #4472. Namely:
* We add support for the `--window,-w <window-id>` argument to `wt.exe`, to allow a commandline to be given to another window.
* If `window-id` is `0`, run the given commands in _the current window_.
* If `window-id` is a negative number, run the commands in a _new_ Terminal window.
* If `window-id` is the ID of an existing window, then run the commandline in that window.
* If `window-id` is _not_ the ID of an existing window, create a new window. That window will be assigned the ID provided in the commandline. The provided subcommands will be run in that new window.
* If `window-id` is omitted, then create a new window.
## References
* Spec: #8135
* Megathread: #5000
* Project: projects/5
## PR Checklist
* [x] Closes #4472
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - **sure does**
## Detailed Description of the Pull Request / Additional comments
Note that `wt -w 1 -d c:\foo cmd.exe` does work, by causing window 1 to change
There are limitations, and there are plenty of things to work on in the future:
* [ ] We don't support names for windows yet
* [ ] We don't support window glomming by default, or a setting to configure what happens when `-w` is omitted. I thought it best to lay the groundwork first, then come back to that.
* [ ] `-w 0` currently just uses the "last activated" window, not "the current". There's more follow-up work to try and smartly find the actual window we're being called from.
* [ ] Basically anything else that's listed in projects/5.
I'm cutting this PR where it currently is, because this is already a huge PR. I believe the remaining tasks will all be easier to land, once this is in.
## Validation Steps Performed
I've been creating windows, and closing them, and running cmdlines for a while now. I'm gonna keep doing that while the PR is open, till no bugs remain.
# TODOs
* [x] There are a bunch of `GetID`, `GetPID` calls that aren't try/caught 😬
- [x] `Monarch.cpp`
- [x] `Peasant.cpp`
- [x] `WindowManager.cpp`
- [x] `AppHost.cpp`
* [x] If the monarch gets hung, then _you can't launch any Terminals_ 😨 We should handle this gracefully.
- Proposed idea: give the Monarch some time to respond to a proposal for a commandline. If there's no response in that timeframe, this window is now a _hermit_, outside of society entirely. It can't be elected Monarch. It can't receive command lines. It has no ID.
- Could we gracefully recover from such a state? maybe, probably not though.
- Same deal if a peasant hangs, it could end up hanging the monarch, right? Like if you do `wt -w 2`, and `2` is hung, then does the monarch get hung waiting on the hung peasant?
- After talking with @miniksa, **we're gonna punt this from the initial implementation**. If people legit hit this in the wild, we'll fix it then.
2021-02-10 12:28:09 +01:00
|
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
|
|
"Peasant_ExecuteCommandline",
|
|
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
|
|
TraceLoggingWideString(args.CurrentDirectory().c_str(), "directory", "the provided cwd"),
|
|
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
|
|
|
Add `Microsoft.Terminal.Remoting.dll` (#8607)
Adds a `Microsoft.Terminal.Remoting.dll` to our solution. This DLL will
be responsible for all the Monarch/Peasant work that's been described in
#7240 & #8135.
This PR does _not_ implement the Monarch/Peasant architecture in any
significant way. The goal of this PR is to just to establish the project
layout, and the most basic connections. This should make reviewing the
actual meat of the implementation (in a later PR) easier. It will also
give us the opportunity to include some of the basic weird things we're
doing (with `CoRegisterClass`) in the Terminal _now_, and get them
selfhosted, before building on them too much.
This PR does have windows registering the `Monarch` class with COM. When
windows are created, they'll as the Monarch if they should create a new
window or not. In this PR, the Monarch will always reply "yes, please
make a new window".
Similar to other projects in our solution, we're adding 3 projects here:
* `Microsoft.Terminal.Remoting.lib`: the actual implementation, as a
static lib.
* `Microsoft.Terminal.Remoting.dll`: The implementation linked as a DLL,
for use in `WindowsTerminal.exe`.
* `Remoting.UnitTests.dll`: A unit test dll that links with the static
lib.
There are plenty of TODOs scattered about the code. Clearly, most of
this isn't implemented yet, but I do have more WIP branches. I'm using
[`projects/5`](https://github.com/microsoft/terminal/projects/5) as my
notation for TODOs that are too small for an issue, but are part of the
whole Process Model 2.0 work.
## References
* #5000 - this is the process model megathread
* #7240 - The process model 2.0 spec.
* #8135 - the window management spec. (please review me, I have 0/3
signoffs even after the discussion we had 😢)
* #8171 - the Monarch/peasant sample. (please review me, I have 1/2)
## PR Checklist
* [x] Closes nothing, this is just infrastructure
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
2021-01-07 23:59:37 +01:00
|
|
|
// Raise an event with these args. The AppHost will listen for this
|
|
|
|
// event to know when to take these args and dispatch them to a
|
|
|
|
// currently-running window.
|
|
|
|
_ExecuteCommandlineRequestedHandlers(*this, args);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Remoting::CommandlineArgs Peasant::InitialArgs()
|
|
|
|
{
|
|
|
|
return _initialArgs;
|
|
|
|
}
|
|
|
|
|
Add support for running a commandline in another WT window (#8898)
## Summary of the Pull Request
**If you're reading this PR and haven't signed off on #8135, go there first.**
![window-management-000](https://user-images.githubusercontent.com/18356694/103932910-25199380-50e8-11eb-97e3-594a31da62d2.gif)
This provides the basic parts of the implementation of #4472. Namely:
* We add support for the `--window,-w <window-id>` argument to `wt.exe`, to allow a commandline to be given to another window.
* If `window-id` is `0`, run the given commands in _the current window_.
* If `window-id` is a negative number, run the commands in a _new_ Terminal window.
* If `window-id` is the ID of an existing window, then run the commandline in that window.
* If `window-id` is _not_ the ID of an existing window, create a new window. That window will be assigned the ID provided in the commandline. The provided subcommands will be run in that new window.
* If `window-id` is omitted, then create a new window.
## References
* Spec: #8135
* Megathread: #5000
* Project: projects/5
## PR Checklist
* [x] Closes #4472
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - **sure does**
## Detailed Description of the Pull Request / Additional comments
Note that `wt -w 1 -d c:\foo cmd.exe` does work, by causing window 1 to change
There are limitations, and there are plenty of things to work on in the future:
* [ ] We don't support names for windows yet
* [ ] We don't support window glomming by default, or a setting to configure what happens when `-w` is omitted. I thought it best to lay the groundwork first, then come back to that.
* [ ] `-w 0` currently just uses the "last activated" window, not "the current". There's more follow-up work to try and smartly find the actual window we're being called from.
* [ ] Basically anything else that's listed in projects/5.
I'm cutting this PR where it currently is, because this is already a huge PR. I believe the remaining tasks will all be easier to land, once this is in.
## Validation Steps Performed
I've been creating windows, and closing them, and running cmdlines for a while now. I'm gonna keep doing that while the PR is open, till no bugs remain.
# TODOs
* [x] There are a bunch of `GetID`, `GetPID` calls that aren't try/caught 😬
- [x] `Monarch.cpp`
- [x] `Peasant.cpp`
- [x] `WindowManager.cpp`
- [x] `AppHost.cpp`
* [x] If the monarch gets hung, then _you can't launch any Terminals_ 😨 We should handle this gracefully.
- Proposed idea: give the Monarch some time to respond to a proposal for a commandline. If there's no response in that timeframe, this window is now a _hermit_, outside of society entirely. It can't be elected Monarch. It can't receive command lines. It has no ID.
- Could we gracefully recover from such a state? maybe, probably not though.
- Same deal if a peasant hangs, it could end up hanging the monarch, right? Like if you do `wt -w 2`, and `2` is hung, then does the monarch get hung waiting on the hung peasant?
- After talking with @miniksa, **we're gonna punt this from the initial implementation**. If people legit hit this in the wild, we'll fix it then.
2021-02-10 12:28:09 +01:00
|
|
|
void Peasant::ActivateWindow(const Remoting::WindowActivatedArgs& args)
|
|
|
|
{
|
|
|
|
// TODO: projects/5 - somehow, pass an identifier for the current
|
|
|
|
// desktop into this method. The Peasant shouldn't need to be able to
|
|
|
|
// figure it out, but it will need to report it to the monarch.
|
|
|
|
|
|
|
|
// Store these new args as our last activated state. If a new monarch
|
|
|
|
// comes looking, we can use this info to tell them when we were last
|
|
|
|
// activated.
|
|
|
|
_lastActivatedArgs = args;
|
|
|
|
|
|
|
|
bool successfullyNotified = false;
|
|
|
|
// Raise our WindowActivated event, to let the monarch know we've been
|
|
|
|
// activated.
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Try/catch this, because the other side of this event is handled
|
|
|
|
// by the monarch. The monarch might have died. If they have, this
|
|
|
|
// will throw an exception. Just eat it, the election thread will
|
|
|
|
// handle hooking up the new one.
|
|
|
|
_WindowActivatedHandlers(*this, args);
|
|
|
|
successfullyNotified = true;
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
LOG_CAUGHT_EXCEPTION();
|
|
|
|
}
|
|
|
|
|
|
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
|
|
"Peasant_ActivateWindow",
|
|
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
|
|
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
|
|
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Retrieve the WindowActivatedArgs describing the last activation of this
|
|
|
|
// peasant. New monarchs can use this state to determine when we were last
|
|
|
|
// activated.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - a WindowActivatedArgs with info about when and where we were last activated.
|
|
|
|
Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs()
|
|
|
|
{
|
|
|
|
return _lastActivatedArgs;
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:08:03 +02:00
|
|
|
// Method Description:
|
|
|
|
// - Tell this window to display it's window ID. We'll raise a
|
|
|
|
// DisplayWindowIdRequested event, which will get handled in the AppHost,
|
|
|
|
// and used to tell the app to display the ID toast.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void Peasant::DisplayWindowId()
|
|
|
|
{
|
|
|
|
// Not worried about try/catching this. The handler is in AppHost, which
|
|
|
|
// is in-proc for us.
|
|
|
|
_DisplayWindowIdRequestedHandlers(*this, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Raises an event to ask that all windows be identified. This will come
|
|
|
|
// back to us when the Monarch handles the event and calls our
|
|
|
|
// DisplayWindowId method.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void Peasant::RequestIdentifyWindows()
|
|
|
|
{
|
|
|
|
bool successfullyNotified = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Try/catch this, because the other side of this event is handled
|
|
|
|
// by the monarch. The monarch might have died. If they have, this
|
|
|
|
// will throw an exception. Just eat it, the election thread will
|
|
|
|
// handle hooking up the new one.
|
|
|
|
_IdentifyWindowsRequestedHandlers(*this, nullptr);
|
|
|
|
successfullyNotified = true;
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
LOG_CAUGHT_EXCEPTION();
|
|
|
|
}
|
|
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
|
|
"Peasant_RequestIdentifyWindows",
|
|
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
|
|
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
|
|
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
|
|
}
|
|
|
|
|
Add support for renaming windows (#9662)
## Summary of the Pull Request
This PR adds support for renaming windows.
![window-renaming-000](https://user-images.githubusercontent.com/18356694/113034344-9a30be00-9157-11eb-9443-975f3c294f56.gif)
![window-renaming-001](https://user-images.githubusercontent.com/18356694/113034452-b5033280-9157-11eb-9e35-e5ac80fef0bc.gif)
It does so through two new actions:
* `renameWindow` takes a `name` parameter, and attempts to set the window's name
to the provided name. This is useful if you always want to hit <kbd>F3</kbd>
and rename a window to "foo" (READ: probably not that useful)
* `openWindowRenamer` is more interesting: it opens a `TeachingTip` with a
`TextBox`. When the user hits Ok, it'll request a rename for the provided
value. This lets the user pick a new name for the window at runtime.
In both cases, if there's already a window with that name, then the monarch will
reject the rename, and pop a `Toast` in the window informing the user that the
rename failed. Nifty!
## References
* Builds on the toasts from #9523
* #5000 - process model megathread
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50771747
* [x] I work here
* [x] Tests addded (and pass with the help of #9660)
* [ ] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
I'm sending this PR while finishing up the tests. I figured I'll have time to sneak them in before I get the necessary reviews.
> PAIN: We can't immediately focus the textbox in the TeachingTip. It's
> not technically focusable until it is opened. However, it doesn't
> provide an even tto tell us when it is opened. That's tracked in
> microsoft/microsoft-ui-xaml#1607. So for now, the user _needs_ to
> click on the text box manually.
> We're also not using a ContentDialog for this, because in Xaml
> Islands a text box in a ContentDialog won't recieve _any_ keypresses.
> Fun!
## Validation Steps Performed
I've been playing with
```json
{ "keys": "f1", "command": "identifyWindow" },
{ "keys": "f2", "command": "identifyWindows" },
{ "keys": "f3", "command": "openWindowRenamer" },
{ "keys": "f4", "command": { "action": "renameWindow", "name": "foo" } },
{ "keys": "f5", "command": { "action": "renameWindow", "name": "bar" } },
```
and they seem to work as expected
2021-04-02 18:00:04 +02:00
|
|
|
void Peasant::RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args)
|
|
|
|
{
|
|
|
|
bool successfullyNotified = false;
|
|
|
|
const auto oldName{ _WindowName };
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Try/catch this, because the other side of this event is handled
|
|
|
|
// by the monarch. The monarch might have died. If they have, this
|
|
|
|
// will throw an exception. Just eat it, the election thread will
|
|
|
|
// handle hooking up the new one.
|
|
|
|
_RenameRequestedHandlers(*this, args);
|
|
|
|
if (args.Succeeded())
|
|
|
|
{
|
|
|
|
_WindowName = args.NewName();
|
|
|
|
}
|
|
|
|
successfullyNotified = true;
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
LOG_CAUGHT_EXCEPTION();
|
|
|
|
}
|
|
|
|
TraceLoggingWrite(g_hRemotingProvider,
|
|
|
|
"Peasant_RequestRename",
|
|
|
|
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
|
|
|
TraceLoggingWideString(oldName.c_str(), "oldName", "Our old name"),
|
|
|
|
TraceLoggingWideString(args.NewName().c_str(), "newName", "The proposed name"),
|
|
|
|
TraceLoggingBoolean(args.Succeeded(), "succeeded", "true if the monarch ok'd this new name for us."),
|
|
|
|
TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"),
|
|
|
|
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
|
|
|
}
|
Add `Microsoft.Terminal.Remoting.dll` (#8607)
Adds a `Microsoft.Terminal.Remoting.dll` to our solution. This DLL will
be responsible for all the Monarch/Peasant work that's been described in
#7240 & #8135.
This PR does _not_ implement the Monarch/Peasant architecture in any
significant way. The goal of this PR is to just to establish the project
layout, and the most basic connections. This should make reviewing the
actual meat of the implementation (in a later PR) easier. It will also
give us the opportunity to include some of the basic weird things we're
doing (with `CoRegisterClass`) in the Terminal _now_, and get them
selfhosted, before building on them too much.
This PR does have windows registering the `Monarch` class with COM. When
windows are created, they'll as the Monarch if they should create a new
window or not. In this PR, the Monarch will always reply "yes, please
make a new window".
Similar to other projects in our solution, we're adding 3 projects here:
* `Microsoft.Terminal.Remoting.lib`: the actual implementation, as a
static lib.
* `Microsoft.Terminal.Remoting.dll`: The implementation linked as a DLL,
for use in `WindowsTerminal.exe`.
* `Remoting.UnitTests.dll`: A unit test dll that links with the static
lib.
There are plenty of TODOs scattered about the code. Clearly, most of
this isn't implemented yet, but I do have more WIP branches. I'm using
[`projects/5`](https://github.com/microsoft/terminal/projects/5) as my
notation for TODOs that are too small for an issue, but are part of the
whole Process Model 2.0 work.
## References
* #5000 - this is the process model megathread
* #7240 - The process model 2.0 spec.
* #8135 - the window management spec. (please review me, I have 0/3
signoffs even after the discussion we had 😢)
* #8171 - the Monarch/peasant sample. (please review me, I have 1/2)
## PR Checklist
* [x] Closes nothing, this is just infrastructure
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
2021-01-07 23:59:37 +01:00
|
|
|
}
|