Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate keyboard manager with settings v2[Part-2] #2107

Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -152,10 +153,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 @@ -186,8 +199,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 @@ -50,6 +53,12 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
MessageBox(NULL, L"Call to CreateWindow failed!", L"Error", NULL);
return;
}
// Store the newly created window's handle.
std::unique_lock<std::mutex> hwmdLock(editKeyboardWindowMutex);
udit3333 marked this conversation as resolved.
Show resolved Hide resolved
hwndEditKeyboardNativeWindow = _hWndEditKeyboardWindow;
hwmdLock.unlock();

SetForegroundWindow(_hWndEditKeyboardWindow);
udit3333 marked this conversation as resolved.
Show resolved Hide resolved
udit3333 marked this conversation as resolved.
Show resolved Hide resolved

// 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).
Expand Down Expand Up @@ -228,6 +237,9 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan
DispatchMessage(&msg);
}
desktopSource.Close();

hwmdLock.lock();
hwndEditKeyboardNativeWindow = nullptr;
udit3333 marked this conversation as resolved.
Show resolved Hide resolved
}

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

return 0;
}

bool CheckEditKeyboardWindowActive()
{
bool result = false;
std::unique_lock<std::mutex> hwmdLock(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();
Loading