Skip to content

Commit

Permalink
[KBM]Launch apps / URI with keyboard shortcuts, support chords (micro…
Browse files Browse the repository at this point in the history
…soft#30121)

* Working UI update with just runProgram Path and isRunProgram

* First working, basic. no args or path, or setting change detections.

* Revert and fixed.

* Some clean up, working with config file monitor

* Args and Start-in should be working.

* File monitor, quotes, xaml screens one

* Fixed enable/disable toogle from XAML

* Code cleanup.

* Betting logging.

* Cleanup, start of RunProgramDescriptor and usage of run_non_elevated/run_elevated

* Code moved to KeyboardEventHandlers, but not enabled since it won't build as is, needs elevation.h. Other testing..

* Key chords working, pretty much

* Added gui for elevation level, need to refresh on change...

* f: include shellapi.h and reference wil in KBMEL

* run_elevated/run_non_elevated sorted out. Working!

* Removed lots of old temp code.

* Fix some speling errors.

* Cleanup before trying to add a UI for the chord

* Added "DifferentUser" option

* Closer on UI for chords.

* Better UI, lots working.

* Clean up

* Text for “Allow chords” – needs to look better…

* Bugs and clean-up

* Cleanup

* Refactor and clean up.

* More clean up

* Some localization.

* Don’t show “Allow chords“ to the “to” shortcut

* Maybe better foreground after opening new app

* Better chord matching.

* Runprogram fix for stealing existing shortcut.

* Better runProgram stuff

* Temp commit

* Working well

* Toast test

* More toast

* Added File and Folder picker UI

* Pre-check on run program file exists.

* Refactor to SetupRunProgramControls

* Open URI UI is going.

* Open URI working well

* Open URI stuff working well

* Allowed AppSpecific shortcut and fixed backup/restore shortcut dups

* Fixed settings screen

* Start of code to find by name...

* UI fixed

* Small fixes

* Some single edit code working.

* UI getting better.

* Fixes

* Fixed and merge from main

* UI updates

* UI updates.

* UI stuff

* Fixed crash from move ui item locations.

* Fixed crash from move ui item locations.

* Added delete confirm

* Basic sound working.

* Localized some stuff

* Added sounds

* Better experiance when shortcut is in use.

* UI tweaks

* Fixed KBM ui for unicode shortcut not having ","

* Some clean up

* Cleanup

* Cleanup

* Fixed applyXamlStyling

* Added back stuff lost in merge

* applyXamlStyling, again

* Fixed crash on change from non shortcut to shortcut

* Update src/modules/keyboardmanager/KeyboardManagerEngineTest/KeyboardManagerEngineTest.vcxproj

* Fixed some spelling type issues.

* ImplementationLibrary 231216

* Comment bump to see if the Microsoft.Windows.ImplementationLibrary version thing gets picked up

* Correct, Microsoft.Windows.ImplementationLibrary, finally?

* Fixed two test that failed because we now allow key-chords.

* Removed shortcut sounds.

* use original behavior when "allow chords" is off in shortcut window

* fix crash when editing a shortcut that has apps specified for it

* split KBM chords with comma on dashboard page

* Fix some spelling items.

* More "spelling"

* Fix XAML styling

* align TextBlock and ToggleSwitch

* fix cutoff issue at the top

* increase ComboBox width

* Added *Unsupported* for backwards compat on config of KBM

* fix spellcheck

* Fix crash on Remap key screen

* Fixed Remap Keys ComboBox width too short.

* Removed KBM Single Edit mode, fixed crash.

* Fix Xaml with xaml cops

* Fix crash on setting "target app" for some types of shortcuts.

* Space to toggle chord, combobox back

* fix spellcheck

* fix some code nits

* Code review updates.

* Add exclusions to the bug report tool

* Code review and kill CloseAndEndTask

* Fix alignment / 3 comboboxes per row

* Fix daily telemetry events to exclude start app and open URI

* Add chords and remove app start and open uri from config telemetry

* comma instead of plus in human readable shortcut telemetry data

* Code review, restore default-old state when new row added in KBM

* Code review, restore default-old state when new row added in KBM, part 2

* Still show target app on Settings

* Only allow enabling chords for origin shortcuts

---------

Co-authored-by: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
  • Loading branch information
3 people authored Feb 27, 2024
1 parent 1a5349b commit 725c8e8
Show file tree
Hide file tree
Showing 48 changed files with 4,331 additions and 1,529 deletions.
1 change: 1 addition & 0 deletions .github/actions/spell-check/allow/code.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Ctrls
EXSEL
HOLDENTER
HOLDESC
HOLDSPACE
KBDLLHOOKSTRUCT
keyevent
LAlt
Expand Down
2 changes: 2 additions & 0 deletions .github/actions/spell-check/allow/names.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Pooja
Quriz
randyrants
ricardosantos
ritchielawrence
robmikh
Rutkas
ryanbodrug
Expand Down Expand Up @@ -128,6 +129,7 @@ Zykova

# OTHERS

cmdow
Controlz
cortana
fancymouse
Expand Down
16 changes: 16 additions & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ bbwe
bck
BESTEFFORT
bhid
BIF
bigbar
bigobj
binlog
Expand All @@ -125,6 +126,7 @@ BPBF
bpmf
bpp
Browsable
BROWSEINFO
bsd
bthprops
bti
Expand Down Expand Up @@ -212,6 +214,7 @@ cominterop
commandline
COMMANDTITLE
commctrl
commdlg
compmgmt
COMPOSITIONFULL
comsupp
Expand Down Expand Up @@ -473,6 +476,7 @@ FILELOCKSMITHCONTEXTMENU
FILELOCKSMITHEXT
FILELOCKSMITHLIB
FILELOCKSMITHLIBINTEROP
FILEMUSTEXIST
FILEOP
FILEOS
FILESUBTYPE
Expand Down Expand Up @@ -801,13 +805,15 @@ LPCTSTR
lpdw
lpfn
LPINPUT
LPITEMIDLIST
lpmi
LPMINMAXINFO
LPMONITORINFO
LPOSVERSIONINFOEXW
LPQUERY
lprc
LPSAFEARRAY
lpstr
lpsz
lpt
LPTHREAD
Expand Down Expand Up @@ -971,6 +977,7 @@ netsetup
netsh
newcolor
newdev
NEWDIALOGSTYLE
newitem
newpath
newrow
Expand Down Expand Up @@ -1041,6 +1048,7 @@ ocr
Ocrsettings
odbccp
officehubintl
OFN
ofs
oldcolor
olditem
Expand All @@ -1051,6 +1059,7 @@ OLECHAR
onebranch
OOBEPT
opencode
OPENFILENAME
opensource
openxmlformats
OPTIMIZEFORINVOKE
Expand Down Expand Up @@ -1085,6 +1094,7 @@ parray
PARTIALCONFIRMATIONDIALOGTITLE
PATCOPY
pathcch
PATHMUSTEXIST
Pathto
PATINVERT
PATPAINT
Expand Down Expand Up @@ -1173,6 +1183,7 @@ PRINTCLIENT
printmanagement
prm
proactively
PROCESSENTRY
PROCESSKEY
processthreadsapi
PRODEXT
Expand Down Expand Up @@ -1292,6 +1303,7 @@ RESTORETOMAXIMIZED
restrictedcapabilities
restrictederrorinfo
resultlist
RETURNONLYFSDIRS
RGBQUAD
rgbs
rgelt
Expand Down Expand Up @@ -1436,6 +1448,7 @@ sln
SMALLICON
smartphone
SMTO
SNAPPROCESS
snwprintf
softline
SOURCECLIENTAREAONLY
Expand Down Expand Up @@ -1578,6 +1591,7 @@ tlbimp
TMPVAR
TNP
toggleswitch
Toolhelp
toolkitconverters
Toolset
toolwindow
Expand Down Expand Up @@ -1634,6 +1648,7 @@ Updatelayout
UPGRADINGPRODUCTCODE
Uptool
urld
urlmon
Usb
USEDEFAULT
USEFILEATTRIBUTES
Expand Down Expand Up @@ -1709,6 +1724,7 @@ wcautil
WCE
wcex
WClass
wcsicmp
wcsnicmp
WDA
wdp
Expand Down
11 changes: 11 additions & 0 deletions src/common/interop/keyboard_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ std::wstring LayoutMap::GetKeyName(DWORD key)
return impl->GetKeyName(key);
}

DWORD LayoutMap::GetKeyFromName(const std::wstring& name)
{
auto list = impl->GetKeyNameList(false);
for (const auto& [value, key] : list)
{
if (key == name)
return value;
}
return {};
}

std::vector<DWORD> LayoutMap::GetKeyCodeList(const bool isShortcut)
{
return impl->GetKeyCodeList(isShortcut);
Expand Down
1 change: 1 addition & 0 deletions src/common/interop/keyboard_layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class LayoutMap
~LayoutMap();
void UpdateLayout();
std::wstring GetKeyName(DWORD key);
DWORD GetKeyFromName(const std::wstring& name);
std::vector<DWORD> GetKeyCodeList(const bool isShortcut = false);
std::vector<std::pair<DWORD, std::wstring>> GetKeyNameList(const bool isShortcut = false);

Expand Down
69 changes: 57 additions & 12 deletions src/common/utils/elevation.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ namespace
}

result = spView->QueryInterface(riid, ppv);
if (result != S_OK || ppv == nullptr || *ppv == nullptr )
if (result != S_OK || ppv == nullptr || *ppv == nullptr)
{
Logger::warn(L"Failed to query interface. {}", GetErrorString(result));
return false;
Expand All @@ -83,7 +83,7 @@ namespace
inline bool GetDesktopAutomationObject(REFIID riid, void** ppv)
{
CComPtr<IShellView> spsv;

// Desktop may not be available on startup
auto attempts = 5;
for (auto i = 1; i <= attempts; i++)
Expand Down Expand Up @@ -207,8 +207,34 @@ inline bool drop_elevated_privileges()
return result;
}

// Run command as different user, returns true if succeeded
inline HANDLE run_as_different_user(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true)
{
Logger::info(L"run_elevated with params={}", params);
SHELLEXECUTEINFOW exec_info = { 0 };
exec_info.cbSize = sizeof(SHELLEXECUTEINFOW);
exec_info.lpVerb = L"runAsUser";
exec_info.lpFile = file.c_str();
exec_info.lpParameters = params.c_str();
exec_info.hwnd = 0;
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
exec_info.lpDirectory = workingDir;
exec_info.hInstApp = 0;
if (showWindow)
{
exec_info.nShow = SW_SHOWDEFAULT;
}
else
{
// might have limited success, but only option with ShellExecuteExW
exec_info.nShow = SW_HIDE;
}

return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
}

// Run command as elevated user, returns true if succeeded
inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params)
inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params, const wchar_t* workingDir = nullptr, const bool showWindow = true)
{
Logger::info(L"run_elevated with params={}", params);
SHELLEXECUTEINFOW exec_info = { 0 };
Expand All @@ -218,15 +244,24 @@ inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params)
exec_info.lpParameters = params.c_str();
exec_info.hwnd = 0;
exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
exec_info.lpDirectory = 0;
exec_info.lpDirectory = workingDir;
exec_info.hInstApp = 0;
exec_info.nShow = SW_SHOWDEFAULT;

if (showWindow)
{
exec_info.nShow = SW_SHOWDEFAULT;
}
else
{
// might have limited success, but only option with ShellExecuteExW
exec_info.nShow = SW_HIDE;
}

return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
}

// Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL
inline bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr)
inline bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr, const bool showWindow = true)
{
Logger::info(L"run_non_elevated with params={}", params);
auto executable_args = L"\"" + file + L"\"";
Expand Down Expand Up @@ -292,15 +327,22 @@ inline bool run_non_elevated(const std::wstring& file, const std::wstring& param
STARTUPINFOEX siex = { 0 };
siex.lpAttributeList = pptal;
siex.StartupInfo.cb = sizeof(siex);

PROCESS_INFORMATION pi = { 0 };
auto dwCreationFlags = EXTENDED_STARTUPINFO_PRESENT;

if (!showWindow)
{
siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
siex.StartupInfo.wShowWindow = SW_HIDE;
dwCreationFlags = CREATE_NO_WINDOW;
}

auto succeeded = CreateProcessW(file.c_str(),
&executable_args[0],
nullptr,
nullptr,
FALSE,
EXTENDED_STARTUPINFO_PRESENT,
dwCreationFlags,
nullptr,
workingDir,
&siex.StartupInfo,
Expand Down Expand Up @@ -341,7 +383,10 @@ inline bool RunNonElevatedEx(const std::wstring& file, const std::wstring& param
catch (...)
{
}
if (SUCCEEDED(co_init)) CoUninitialize();
if (SUCCEEDED(co_init))
{
CoUninitialize();
}

return success;
}
Expand Down Expand Up @@ -372,7 +417,7 @@ inline std::optional<ProcessInfo> RunNonElevatedFailsafe(const std::wstring& fil
}
}

auto handles = getProcessHandlesByName(std::filesystem::path{ file }.filename().wstring(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE | handleAccess );
auto handles = getProcessHandlesByName(std::filesystem::path{ file }.filename().wstring(), PROCESS_QUERY_INFORMATION | SYNCHRONIZE | handleAccess);

if (handles.empty())
return std::nullopt;
Expand All @@ -385,7 +430,7 @@ inline std::optional<ProcessInfo> RunNonElevatedFailsafe(const std::wstring& fil
}

// Run command with the same elevation, returns true if succeeded
inline bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid)
inline bool run_same_elevation(const std::wstring& file, const std::wstring& params, DWORD* returnPid, const wchar_t* workingDir = nullptr)
{
auto executable_args = L"\"" + file + L"\"";
if (!params.empty())
Expand All @@ -403,7 +448,7 @@ inline bool run_same_elevation(const std::wstring& file, const std::wstring& par
FALSE,
0,
nullptr,
nullptr,
workingDir,
&si,
&pi);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,26 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
type = static_cast<KeyboardManagerEditorType>(_wtoi(cmdArgs[1]));
}

if (numArgs == 3)
std::wstring keysForShortcutToEdit = L"";
std::wstring action = L"";


// do some parsing of the cmdline arg to see if we need to behave different
// like, single edit mode, or "delete" mode.
// These extra args are from "OpenEditor" in the KeyboardManagerViewModel
if (numArgs >= 3)
{
if (numArgs >= 4)
{
keysForShortcutToEdit = std::wstring(cmdArgs[3]);
}

if (numArgs >= 5)
{
action = std::wstring(cmdArgs[4]);
}


std::wstring pid = std::wstring(cmdArgs[2]);
Logger::trace(L"Editor started from the settings with pid {}", pid);
if (!pid.empty())
Expand Down Expand Up @@ -108,7 +126,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
return -1;
}

editor->OpenEditorWindow(type);
editor->OpenEditorWindow(type, keysForShortcutToEdit, action);

editor = nullptr;

Expand Down Expand Up @@ -149,15 +167,15 @@ bool KeyboardManagerEditor::StartLowLevelKeyboardHook()
return (hook != nullptr);
}

void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type)
void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type, std::wstring keysForShortcutToEdit, std::wstring action)
{
switch (type)
{
case KeyboardManagerEditorType::KeyEditor:
CreateEditKeyboardWindow(hInstance, keyboardManagerState, mappingConfiguration);
break;
case KeyboardManagerEditorType::ShortcutEditor:
CreateEditShortcutsWindow(hInstance, keyboardManagerState, mappingConfiguration);
CreateEditShortcutsWindow(hInstance, keyboardManagerState, mappingConfiguration, keysForShortcutToEdit, action);
}
}

Expand Down
Loading

0 comments on commit 725c8e8

Please sign in to comment.