Skip to content

Commit

Permalink
Integrate keyboard manager with settings v2[Part-2] (#2107)
Browse files Browse the repository at this point in the history
* Added CustomAction Data Models

* Updated data model

* Updated Button command trigger for keyboard manager

* Integerated custom actions in Keyboard manager module backend

* Allow Runner to launch windows on foreground

* Refactor and moved the execution logic to background thread

* Removed non-required unlock calls

* Fixed typo

* Resolve PR comments
  • Loading branch information
udit3333 committed Apr 14, 2020
1 parent 79fcd45 commit a88d66b
Show file tree
Hide file tree
Showing 13 changed files with 292 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text.Json.Serialization;

namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
{
public class CustomActionDataModel
{
[JsonPropertyName("action_name")]
public string Name { get; set; }

[JsonPropertyName("value")]
public string Value { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Text.Json;

namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
{
public class CustomNamePolicy : JsonNamingPolicy
{
private Func<string, string> convertDelegate;

public CustomNamePolicy(Func<string, string> convertDelegate)
{
this.convertDelegate = convertDelegate;
}

public override string ConvertName(string name)
{
return convertDelegate(name);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
{
public class ModuleCustomAction
{
public CustomActionDataModel ModuleAction { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Microsoft.PowerToys.Settings.UI.Lib.CustomAction
{
public class SendCustomAction
{
private readonly string moduleName;

public SendCustomAction(string moduleName)
{
this.moduleName = moduleName;
}

[JsonPropertyName("action")]
public ModuleCustomAction Action { get; set; }

public string ToJsonString()
{
var jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = new CustomNamePolicy((propertyName) =>
{
return propertyName.Equals("ModuleAction") ? moduleName : propertyName;
}),
};

return JsonSerializer.Serialize(this, jsonSerializerOptions);
}
}
}
47 changes: 47 additions & 0 deletions src/core/Microsoft.PowerToys.Settings.UI.Lib/Utilities/Helper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.PowerToys.Settings.UI.Lib.CustomAction;

namespace Microsoft.PowerToys.Settings.UI.Lib.Utilities
{
public class Helper
{
public static bool AllowRunnerToForeground()
{
var result = false;
var processes = Process.GetProcessesByName("PowerToys");
if (processes.Length > 0)
{
var pid = processes[0].Id;
result = AllowSetForegroundWindow(pid);
}

return result;
}

public static string GetSerializedCustomAction(string moduleName, string actionName, string actionValue)
{
var customAction = new CustomActionDataModel
{
Name = actionName,
Value = actionValue,
};

var moduleCustomAction = new ModuleCustomAction
{
ModuleAction = customAction,
};

var sendCustomAction = new SendCustomAction(moduleName);
sendCustomAction.Action = moduleCustomAction;
return sendCustomAction.ToJsonString();
}

[DllImport("user32.dll")]
private static extern bool AllowSetForegroundWindow(int dwProcessId);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.PowerToys.Settings.UI.Helpers;

namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public class KeyboardManagerViewModel : Observable
{
public KeyboardManagerViewModel()
{
}
}
}

using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.Views;

namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public class KeyboardManagerViewModel : Observable
{
private ICommand remapKeyboardCommand;
private ICommand editShortcutCommand;

public ICommand RemapKeyboardCommand => remapKeyboardCommand ?? (remapKeyboardCommand = new RelayCommand(OnRemapKeyboard));

public ICommand EditShortcutCommand => editShortcutCommand ?? (editShortcutCommand = new RelayCommand(OnEditShortcut));

public KeyboardManagerViewModel()
{
}

private async void OnRemapKeyboard()
{
await Task.Run(() => OnRemapKeyboardBackground());
}

private async void OnEditShortcut()
{
await Task.Run(() => OnEditShortcutBackground());
}

private async Task OnRemapKeyboardBackground()
{
Helper.AllowRunnerToForeground();
ShellPage.DefaultSndMSGCallback(Helper.GetSerializedCustomAction("Keyboard Manager", "RemapKeyboard", "Create Remap Keyboard Window"));
await Task.CompletedTask;
}

private async Task OnEditShortcutBackground()
{
Helper.AllowRunnerToForeground();
ShellPage.DefaultSndMSGCallback(Helper.GetSerializedCustomAction("Keyboard Manager", "EditShortcut", "Create Edit Shortcut Window"));
await Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@

<Button x:Uid="KeyboardManager_RemapKeyboardButton"
Margin="{StaticResource SmallTopMargin}"
Style="{StaticResource ButtonRevealStyle}"/>
Style="{StaticResource ButtonRevealStyle}"
Command="{Binding Path=RemapKeyboardCommand}"/>

<ListView x:Name="RemapKeysList"
ItemsSource="{StaticResource dummyData}"
Expand All @@ -143,7 +144,8 @@

<Button x:Uid="KeyboardManager_RemapShortcutsButton"
Margin="{StaticResource SmallTopMargin}"
Style="{StaticResource ButtonRevealStyle}"/>
Style="{StaticResource ButtonRevealStyle}"
Command="{Binding Path=EditShortcutCommand}"/>

<ListView x:Name="RemapShortcutsList"
ItemsSource="{StaticResource dummyData}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.PowerToys.Settings.UI.ViewModels;
using Windows.UI.Xaml.Controls;

namespace Microsoft.PowerToys.Settings.UI.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class KeyboardManagerPage : Page
{
public KeyboardManagerViewModel ViewModel { get; } = new KeyboardManagerViewModel();

public KeyboardManagerPage()
{
InitializeComponent();
}
}
}
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Windows.UI.Xaml.Controls;

namespace Microsoft.PowerToys.Settings.UI.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class KeyboardManagerPage : Page
{
public KeyboardManagerViewModel ViewModel { get; } = new KeyboardManagerViewModel();

public KeyboardManagerPage()
{
InitializeComponent();
DataContext = ViewModel;
}
}
}
23 changes: 17 additions & 6 deletions src/modules/keyboardmanager/dll/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#include <common/settings_objects.h>
#include "trace.h"
#include "resource.h"
#include <keyboardmanager/ui/MainWindow.h>
#include <keyboardmanager/ui/EditKeyboardWindow.h>
#include <keyboardmanager/ui/EditShortcutsWindow.h>
#include <keyboardmanager/common/KeyboardManagerState.h>
#include <keyboardmanager/common/Shortcut.h>
#include <keyboardmanager/common/RemapShortcut.h>
Expand Down Expand Up @@ -149,10 +150,22 @@ class KeyboardManager : public PowertoyModuleIface
// Parse the action values, including name.
PowerToysSettings::CustomActionObject action_object =
PowerToysSettings::CustomActionObject::from_json_string(action);
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);

//if (action_object.get_name() == L"custom_action_id") {
// // Execute your custom action
//}
if (action_object.get_name() == L"RemapKeyboard")
{
if (!CheckEditKeyboardWindowActive())
{
std::thread(createEditKeyboardWindow, hInstance, std::ref(keyboardManagerState)).detach();
}
}
else if (action_object.get_name() == L"EditShortcut")
{
if (!CheckEditShortcutsWindowActive())
{
std::thread(createEditShortcutsWindow, hInstance, std::ref(keyboardManagerState)).detach();
}
}
}
catch (std::exception&)
{
Expand Down Expand Up @@ -183,8 +196,6 @@ class KeyboardManager : public PowertoyModuleIface
virtual void enable()
{
m_enabled = true;
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
std::thread(createMainWindow, hInstance, std::ref(keyboardManagerState)).detach();
start_lowlevel_keyboard_hook();
}

Expand Down
31 changes: 31 additions & 0 deletions src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM);
HWND hWndXamlIslandEditKeyboardWindow = nullptr;
// This variable is used to check if window registration has been done to avoid repeated registration leading to an error.
bool isEditKeyboardWindowRegistrationCompleted = false;
// Holds the native window handle of EditKeyboard Window.
HWND hwndEditKeyboardNativeWindow = nullptr;
std::mutex editKeyboardWindowMutex;

// Function to create the Edit Keyboard Window
void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState)
Expand Down Expand Up @@ -52,6 +55,11 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
return;
}

// Store the newly created Edit Keyboard window's handle.
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
hwndEditKeyboardNativeWindow = _hWndEditKeyboardWindow;
hwndLock.unlock();

// This DesktopWindowXamlSource is the object that enables a non-UWP desktop application
// to host UWP controls in any UI element that is associated with a window handle (HWND).
DesktopWindowXamlSource desktopSource;
Expand Down Expand Up @@ -227,6 +235,10 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
DispatchMessage(&msg);
}
desktopSource.Close();

hWndXamlIslandEditKeyboardWindow = nullptr;
hwndLock.lock();
hwndEditKeyboardNativeWindow = nullptr;
}

LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam)
Expand All @@ -248,3 +260,22 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar

return 0;
}

bool CheckEditKeyboardWindowActive()
{
bool result = false;
std::unique_lock<std::mutex> hwndLock(editKeyboardWindowMutex);
if (hwndEditKeyboardNativeWindow != nullptr)
{
// Check if the window is minimized if yes then restore the window.
if (IsIconic(hwndEditKeyboardNativeWindow))
{
ShowWindow(hwndEditKeyboardNativeWindow, SW_RESTORE);
}
// If there is an already existing window no need to create a new open bring it on foreground.
SetForegroundWindow(hwndEditKeyboardNativeWindow);
result = true;
}

return result;
}
5 changes: 4 additions & 1 deletion src/modules/keyboardmanager/ui/EditKeyboardWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
#include <keyboardmanager/common/KeyboardManagerState.h>

// Function to create the Edit Keyboard Window
__declspec(dllexport) void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);
__declspec(dllexport) void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState);

// Function to check if there is already a window active if yes bring to foreground.
__declspec(dllexport) bool CheckEditKeyboardWindowActive();

0 comments on commit a88d66b

Please sign in to comment.