[New Module] Workspaces (#34324)

* spell checker

* Adding OOBE Projects page

* changed the default hotkey

* module interface

* rename projects editor

* bug report tool

* installer

* gpo

* exit event constant

* extend search for projects by search over the containing apps' names

* [Projects] fix grammatical issue #43 (1 app - many apps)

* [Projects] Editor: Main page: fix layout if there are many apps, launch button not disappearing on the right side

* dsc

* github

* pipeline

* guid prefix

* [Projects] fixing general settings gpo handling in runner + minor changes

* arm build fix

* Do not allow saving project if name or applist is empty. Also minor UI changes

* version

* editor version

* spellcheck

* editor dll signing

* update projects names to filter them out

* shortcut saving fix

* [Projects] Editor: brining the highlighted app's icon into the foreground. + minor UI fixes

* spell checker

* spellcheck

* [Projects] re-implementing icon size calculation to have similar sized icons for every app.

* [projects] Adding info message for cases: there are no projects or no results for the search

* [Projects] Adding Edit button to the popup. + minor changes

* [Projects] Making popup having rounded corners

* changed "no projects" text color and position

* remove opening the first proj

* fix placing windows of the same app in the project

* [Projects] bringing back the breadcrumb on the editor page. Make it clickable.

* [Projects] optimizing click handlers

* [Projects] Removing not selected apps on save

* moved on thread executor to common

* moved display utils

* added convert rect

* unsigned monitor number

* set awareness

* app placement

* [Projects] Re-implementing preview drawing - one common image

* [Projects] fix boundary calculation, use DPI aware values

* fix launching with command line args

* Fix ARM64 CI build

* launch packaged apps using names when possible

* spell-check

* update packaged apps path

* projects editor single instance

* [Projects] Add Select all checkbox, Delete selected button

* Add Checkbox for per monitor selection

* modifying highlight in preview

* spell checker

* logs

* exclude help windows

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/49

* Add intermediate step to project creation

* minor bugfix

* mutex fix

* modifying highlight for minimized apps

* Fixing bug: re-draw the preview on app deletion in the editor

* Adding helper class for getting the right bounds for screens

* spell checker

* spell checker

* Minor fixes in the capture dialog

* get dpi unaware screen bounds

* refactoring: added utils

* changed window filter

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/2

* clean up

* refactoring

* projects common lib

* localizable default project prefix

* launcher resources

* clean up

* change snapshot project saving

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project data

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project creation save-cancel handles

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* spell-check

* Remove checkboxes, delete feature

* remove unused from the project

* get command line args in the snapshot

* minimized settings snap fix

* set window property after launching

* FZ: ignore projects launched windows

* Implementing major new features: remove button, position manipulation, arguments, admin, minimized, maximized

* modifying colors

* launcher project filters

* clean up

* Hide Admin checkbox

* hide WIP

* spell-check

* Revert "Hide Admin checkbox"

This reverts commit 3036df9d7f.

* get app elevated property

* Implementing Launch and Edit feature

* fixing: update of listed projects on the main page after hitting save in editor

* Fix for packaged app's icons

* fixing scroll speed issue

* change scroll speed to 15

* launch elevated apps

* minor fixes

* minor fix

* enhancing shortcut handling

* can-launch-elevated check

* projects module interface telemetry

* Implementing store of setting "order by".

* minor string correction

* moved projects data parsing

* telemetry

* add move apps checkbox

* notification about elevated apps

* restart unelevated

* move existing windows

* keep opened windows at the same positions

* handle powertoys settings

* use common theme

* fix corrupted data: project id and monitor id

* project launch on "launch and edit"

* clean up

* show screen numbers instead of monitor names

* launcher error messages

* fix default shortcut

* Adding launch button to projects settings, dashboard and flyout

* Adding new app which is launched when launching a project. It shows the status of the launch process

* spell checker

* Renaming Projects to App Layouts. Replacing only string values, not the variable names

* Re-ordering modules after Renaming Projects + spell checker

* setting window size according to the screen (making it bigger)

* commenting out feature "move apps if exist"

* spell checker

* Add ProjectsLauncherUI to signing

* opening apps in minimized state which are placed on a monitor, which is not found at the moment of launching

* consistent file name

* removed unused sln

* telemetry: create event

* WindowPosition comparison

* telemetry: edit event

* fix muted Launch as admin checkbox

* telemetry: delete event

* updated Edit telemetry event

* added invoke point to launcher args

* added utils

* parse invoke point

* replaced tuple with struct

* telemetry: launch event

* MonitorRect comparison

* resources

* rename: folders

* remove outdated

* rename: window property

* rename: files and folders

* rename: common data structures

* rename: telemetry namespace

* rename: workspaces data

* rename ProjectsLib -> WorkspacesLib

* rename: gpo

* rename: settings

* rename: launcher UI

* rename: other

* rename: pt run

* rename: fz

* rename: module interface

* rename: icon

* rename: snapshot tool

* rename: editor

* rename: common files

* rename: launcher

* rename: editor resources

* fix empty file crash

* rename: json

* rename: module interface

* fix custom actions build

* added launch editor event constant

* xaml formatting

* Add missing method defition to interop::Constants idl
Remove Any CPU config

* more .sln cleanup

* [Run][PowerToys] Fix Workspaces utility (#34336)

polished workspaces utility

* build fix - align CppWinRT version

* address PR comment: fix isdigit

* indentation

* address PR comment: rename function

* address PR comment: changed version for workspaces and revision

* added supported version definition

* addressPR comment: use BringToForeground

* address PR comments: updated projects

* address PR comment: uncomment gpo in settings

* address PR comment: rename oobe view

* update OOBE image with current module name

* moved AppUtils

* launching with AppUserModel.ID

* fixed module order in settings

* fix xaml formatting

* [Workspaces] Close launcher if there are failed launches. Plus adding new spinner gif

* fix topmost LauncherUI

* clean up

* UI closing

* BugReportTool - omit cmd arg data

* Delete icon on workspace removal

* Adding cancellation to launcher UI.

* reordered launching

* fix terminating UI

* Removing old shortcut on workspace renaming

* Sentence case labels

* get process path without waiting

* comment out unused

* remove unused argument

* logs

* New icon

* fix launch and edit for the new project

* fix launch and edit: save new project

* Update exe icons

---------

Co-authored-by: donlaci <laszlo@janeasystems.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
Co-authored-by: Davide Giacometti <davide.giacometti@outlook.it>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Seraphima Zykova
2024-08-23 09:28:13 +02:00
committed by GitHub
parent 2a8e211cfd
commit 579619952d
221 changed files with 12805 additions and 12 deletions

View File

@@ -0,0 +1,346 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include <common/interop/shared_constants.h>
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/utils/logger_helper.h>
#include <common/utils/resources.h>
#include <common/utils/winapi_error.h>
#include <WorkspacesLib/trace.h>
#include <WorkspacesLib/WorkspacesData.h>
#include <shellapi.h>
#include "resource.h"
#include <common/utils/EventWaiter.h>
// Non-localizable
const std::wstring workspacesLauncherPath = L"PowerToys.WorkspacesLauncher.exe";
const std::wstring workspacesSnapshotToolPath = L"PowerToys.WorkspacesSnapshotTool.exe";
const std::wstring workspacesEditorPath = L"PowerToys.WorkspacesEditor.exe";
namespace
{
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
const wchar_t JSON_KEY_RUN_EDITOR_HOTKEY[] = L"hotkey";
const wchar_t JSON_KEY_RUN_SNAPSHOT_TOOL_HOTKEY[] = L"run-snapshot-tool-hotkey";
const wchar_t JSON_KEY_RUN_LAUNCHER_HOTKEY[] = L"run-launcher-hotkey";
const wchar_t JSON_KEY_VALUE[] = L"value";
}
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
class WorkspacesModuleInterface : public PowertoyModuleIface
{
public:
EventWaiter m_toggleEditorEventWaiter;
// Return the localized display name of the powertoy
virtual PCWSTR get_name() override
{
return app_name.c_str();
}
// Return the non localized key of the powertoy, this will be cached by the runner
virtual const wchar_t* get_key() override
{
return app_key.c_str();
}
virtual std::optional<HotkeyEx> GetHotkeyEx() override
{
return m_hotkey;
}
virtual void OnHotkeyEx() override
{
launch_editor();
}
// Return the configured status for the gpo policy for the module
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
{
return powertoys_gpo::getConfiguredWorkspacesEnabledValue();
}
// Return JSON with the configuration options.
// These are the settings shown on the settings page along with their current values.
virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
// Create a Settings object.
PowerToysSettings::Settings settings(hinstance, get_name());
settings.set_description(GET_RESOURCE_STRING(IDS_WORKSPACES_SETTINGS_DESC));
settings.set_overview_link(L"https://aka.ms/PowerToysOverview_Workspaces");
return settings.serialize_to_buffer(buffer, buffer_size);
}
// Passes JSON with the configuration settings for the powertoy.
// This is called when the user hits Save on the settings page.
virtual void set_config(PCWSTR config) override
{
try
{
// Parse the input JSON string.
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
parse_hotkeys(values);
auto settingsObject = values.get_raw_json();
values.save_to_settings_file();
}
catch (std::exception&)
{
// Improper JSON.
}
}
// Signal from the Settings editor to call a custom action.
// This can be used to spawn more complex editors.
virtual void call_custom_action(const wchar_t* /*action*/) override
{
SetEvent(m_toggleEditorEvent);
}
// Enable the powertoy
virtual void enable()
{
Logger::info("Workspaces enabling");
Enable();
}
// Disable the powertoy
virtual void disable()
{
Logger::info("Workspaces disabling");
Disable(true);
}
// Returns if the powertoy is enabled
virtual bool is_enabled() override
{
Logger::info("enabled = {}", m_enabled);
return m_enabled;
}
// Destroy the powertoy and free memory
virtual void destroy() override
{
Disable(false);
if (m_toggleEditorEvent)
{
CloseHandle(m_toggleEditorEvent);
m_toggleEditorEvent = nullptr;
}
delete this;
}
virtual void send_settings_telemetry() override
{
}
WorkspacesModuleInterface()
{
app_name = GET_RESOURCE_STRING(IDS_WORKSPACES_NAME);
app_key = L"Workspaces";
LoggerHelpers::init_logger(app_key, L"ModuleInterface", "Workspaces");
init_settings();
m_toggleEditorEvent = CreateDefaultEvent(CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT);
if (!m_toggleEditorEvent)
{
Logger::error(L"Failed to create launch editor event");
auto message = get_last_error_message(GetLastError());
if (message.has_value())
{
Logger::error(message.value());
}
}
m_toggleEditorEventWaiter = EventWaiter(CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT, [&](int err) {
if (err == ERROR_SUCCESS)
{
Logger::trace(L"{} event was signaled", CommonSharedConstants::WORKSPACES_LAUNCH_EDITOR_EVENT);
launch_editor();
}
});
}
private:
void Enable()
{
Logger::info("Enable");
m_enabled = true;
Trace::Workspaces::Enable(true);
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"";
executable_args.append(std::to_wstring(powertoys_pid));
}
void SendCloseEvent()
{
auto exitEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::WORKSPACES_EXIT_EVENT);
if (!exitEvent)
{
Logger::warn(L"Failed to create exitEvent. {}", get_last_error_or_default(GetLastError()));
}
else
{
Logger::trace(L"Signaled exitEvent");
if (!SetEvent(exitEvent))
{
Logger::warn(L"Failed to signal exitEvent. {}", get_last_error_or_default(GetLastError()));
}
ResetEvent(exitEvent);
CloseHandle(exitEvent);
}
}
void Disable(bool const traceEvent)
{
Logger::info("Disable");
m_enabled = false;
if (traceEvent)
{
Trace::Workspaces::Enable(false);
}
if (m_toggleEditorEvent)
{
ResetEvent(m_toggleEditorEvent);
}
if (m_hProcess)
{
TerminateProcess(m_hProcess, 0);
SendCloseEvent();
m_hProcess = nullptr;
}
}
// Load the settings file.
void init_settings()
{
try
{
Logger::trace(L"Read settings {}", get_key());
// Load and parse the settings file for this PowerToy.
PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
parse_hotkeys(settings);
}
catch (std::exception&)
{
Logger::warn(L"An exception occurred while loading the settings file");
// Error while loading from the settings file. Let default values stay as they are.
}
}
void parse_hotkeys(PowerToysSettings::PowerToyValues& settings)
{
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
{
if (settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).HasKey(JSON_KEY_RUN_EDITOR_HOTKEY))
{
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_RUN_EDITOR_HOTKEY).GetNamedObject(JSON_KEY_VALUE);
auto hotkey = PowerToysSettings::HotkeyObject::from_json(jsonPropertiesObject);
m_hotkey = HotkeyEx();
if (hotkey.win_pressed())
{
m_hotkey.modifiersMask |= MOD_WIN;
}
if (hotkey.ctrl_pressed())
{
m_hotkey.modifiersMask |= MOD_CONTROL;
}
if (hotkey.shift_pressed())
{
m_hotkey.modifiersMask |= MOD_SHIFT;
}
if (hotkey.alt_pressed())
{
m_hotkey.modifiersMask |= MOD_ALT;
}
m_hotkey.vkCode = static_cast<WORD>(hotkey.get_code());
}
}
}
void launch_editor()
{
Logger::trace(L"Starting Workspaces Editor");
/*unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"";
executable_args.append(std::to_wstring(powertoys_pid));*/
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpFile = L"PowerToys.WorkspacesEditor.exe";
sei.nShow = SW_SHOWNORMAL;
//sei.lpParameters = executable_args.data();
if (ShellExecuteExW(&sei))
{
Logger::trace("Successfully started the Workspaces Editor");
}
else
{
Logger::error(L"Workspaces Editor failed to start. {}", get_last_error_or_default(GetLastError()));
}
m_hProcess = sei.hProcess;
}
std::wstring app_name;
//contains the non localized key of the powertoy
std::wstring app_key;
bool m_enabled = false;
HANDLE m_hProcess = nullptr;
// Handle to event used to invoke Workspaces Editor
HANDLE m_toggleEditorEvent;
// Hotkey to invoke the module
HotkeyEx m_hotkey{
.modifiersMask = MOD_SHIFT | MOD_WIN,
.vkCode = 0x4F, // O key;
};
};
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new WorkspacesModuleInterface();
}