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

添加和其他程序交互的机制 #895

Merged
merged 7 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@

</div>

:earth_africa: **English** | [简体中文](./README_ZH.md)
🌍 **English** | [简体中文](./README_ZH.md)

Magpie is a lightweight window scaling tool that comes equipped with various efficient scaling algorithms and filters. Its primary purpose is to enhance game graphics and enable non-fullscreen games to display in fullscreen mode.

We are using [Weblate](https://weblate.org/) for localization work and would appreciate your help in translating Magpie into more languages.

[![Translation status](https://hosted.weblate.org/widgets/magpie/-/287x66-white.png)](https://hosted.weblate.org/engage/magpie/)

:point_right: [Download](https://github.com/Blinue/Magpie/releases)
👉 [Download](https://github.com/Blinue/Magpie/releases)

:point_right: [FAQ](https://github.com/Blinue/Magpie/wiki/FAQ%20(EN))
👉 [FAQ](https://github.com/Blinue/Magpie/wiki/FAQ%20(EN))

:point_right: [Built-in effects](https://github.com/Blinue/Magpie/wiki/Built-in%20effects)
👉 [Built-in effects](https://github.com/Blinue/Magpie/wiki/Built-in%20effects)

:point_right: [Compilation guide](https://github.com/Blinue/Magpie/wiki/Compilation%20guide)
👉 [Compilation guide](https://github.com/Blinue/Magpie/wiki/Compilation%20guide)

:point_right: [Contributing](./CONTRIBUTING.md)
👉 [Contributing](./CONTRIBUTING.md)

## Features

Expand Down
10 changes: 5 additions & 5 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ Magpie 是一个轻量级的窗口缩放工具,内置了多种高效的缩放

[![翻译状态](https://hosted.weblate.org/widgets/magpie/-/287x66-white.png)](https://hosted.weblate.org/engage/magpie/)

:point_right: [下载](https://github.com/Blinue/Magpie/releases)
👉 [下载](https://github.com/Blinue/Magpie/releases)

:point_right: [FAQ](https://github.com/Blinue/Magpie/wiki/FAQ)
👉 [FAQ](https://github.com/Blinue/Magpie/wiki/FAQ)

:point_right: [内置效果介绍](https://github.com/Blinue/Magpie/wiki/内置效果介绍)
👉 [内置效果介绍](https://github.com/Blinue/Magpie/wiki/内置效果介绍)

:point_right: [编译指南](https://github.com/Blinue/Magpie/wiki/编译指南)
👉 [编译指南](https://github.com/Blinue/Magpie/wiki/编译指南)

:point_right: [贡献指南](./CONTRIBUTING_ZH.md)
👉 [贡献指南](./CONTRIBUTING_ZH.md)

## 功能

Expand Down
87 changes: 87 additions & 0 deletions docs/Interact with Magpie programally.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
Magpie provides mechanisms for interaction with other programs. Through these mechanisms, your application can cooperate with Magpie.

[MagpieWatcher](https://github.com/Blinue/MagpieWatcher) demonstrates how to use these mechanisms.

## How to Receive Notifications When Scaling State Changes

You should listen for the MagpieScalingChanged message.

```c++
UINT WM_MAGPIE_SCALINGCHANGED = RegisterWindowMessage(L"MagpieScalingChanged");
```

### Parameters

`wParam` is the event ID. For different events, `lParam` has different meanings. Currently, two events are supported:
* 0: Scaling has ended. `lParam` is not used.
* 1: Scaling has started. `lParam` is the handle of the scaling window.

### Notes

If your process has a higher integrity level than Magpie, you won't receive messages broadcasted by Magpie due to User Interface Privilege Isolation (UIPI). In such cases, call ChangeWindowMessageFilter to allow receiving the MagpieScalingChanged message.

```c++
ChangeWindowMessageFilter(WM_MAGPIE_SCALINGCHANGED, MSGFLT_ADD);
```

## How to Get the Handle of the Scaling Window

You can listen for the MagpieScalingChanged message to obtain the handle of the scaling window. Additionally, while Magpie is scaling, you can also search for the window with the class name `Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22`. Magpie ensures that this class name remains unchanged and that only one scaling window exists at a time.

```c++
HWND hwndScaling = FindWindow(L"Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22", nullptr);
```

## How to Place Your Window Above the Scaling Window

Your window must be topmost. You should also listen for the MagpieScalingChanged message; you will receive one after the scaling window is shown, and then you can use `BringWindowToTop` to place your window above it. The scaling window does not attempt to adjust its position on the Z-axis while it exists.

```c++
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST, ...);
...
if (message == WM_MAGPIE_SCALINGCHANGED) {
switch (wParam) {
case 0:
// Scaling has ended
break;
case 1:
// Scaling has started
// Place this window above the scaling window
BringWindowToTop(hWnd);
break;
default:
break;
}
}
```

## How to Obtain Scaling Information

Scaling information is stored in the [window properties](https://learn.microsoft.com/en-us/windows/win32/winmsg/about-window-properties) of the scaling window. Currently available properties include:
* `Magpie.SrcHWND`: Handle of the source window
* `Magpie.SrcLeft`、`Magpie.SrcTop`、`Magpie.SrcRight`、`Magpie.SrcBottom`: Source region of scaling
* `Magpie.DestLeft`、`Magpie.DestTop`、`Magpie.DestRight`、`Magpie.DestBottom`: Destination region of scaling

```c++
HWND hwndSrc = (HWND)GetProp(hwndScaling, L"Magpie.SrcHWND");

RECT srcRect;
srcRect.left = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcLeft");
srcRect.top = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcTop");
srcRect.right = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcRight");
srcRect.bottom = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcBottom");

RECT destRect;
destRect.left = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestLeft");
destRect.top = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestTop");
destRect.right = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestRight");
destRect.bottom = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestBottom");
```

## How to Keep Magpie Scaling When Your Window Is in the Foreground

Magpie stops scaling when the foreground window changes, with some system windows being exceptions. By setting the `Magpie.ToolWindow` property, you can include your window and all its owned windows in the exceptions list.

```c++
SetProp(hWnd, L"Magpie.ToolWindow", (HANDLE)TRUE);
```
87 changes: 87 additions & 0 deletions docs/以编程方式与 Magpie 交互.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
Magpie 提供了和其他程序交互的机制。通过它们,你的应用可以和 Magpie 配合使用。

[MagpieWatcher](https://github.com/Blinue/MagpieWatcher) 演示了如何使用这些机制。

## 如何在缩放状态改变时得到通知

你应该监听 MagpieScalingChanged 消息。

```c++
UINT WM_MAGPIE_SCALINGCHANGED = RegisterWindowMessage(L"MagpieScalingChanged");
```

### 参数

`wParam` 为事件 ID,对于不同的事件 `lParam` 有不同的含义。目前支持两个事件:
* 0: 缩放已结束。不使用 `lParam`。
* 1: 缩放已开始。`lParam` 为缩放窗口句柄。

### 注意事项

如果你的进程完整性级别 (Integration level) 比 Magpie 更高,由于用户界面特权隔离 (UIPI),你将无法收到 Magpie 广播的消息。这种情况下请调用 ChangeWindowMessageFilter 允许接收 MagpieScalingChanged 消息。

```c++
ChangeWindowMessageFilter(WM_MAGPIE_SCALINGCHANGED, MSGFLT_ADD);
```

## 如何获取缩放窗口句柄

你可以监听 MagpieScalingChanged 消息来获取缩放窗口句柄,也可以查找类名为`Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22`的窗口以在缩放中途获取该句柄。Magpie 将确保此类名不会改变,且不会同时存在多个缩放窗口。

```c++
HWND hwndScaling = FindWindow(L"Window_Magpie_967EB565-6F73-4E94-AE53-00CC42592A22", nullptr);
```

## 如何将你的窗口置于缩放窗口上方

你的窗口必须是置顶的。你还应该监听 MagpieScalingChanged 消息,当收到该消息时缩放窗口已经显示,然后你可以使用 `BringWindowToTop` 函数将自己的窗口置于缩放窗口上方。缩放窗口在存在期间不会尝试调整自己在 Z 轴的位置。

```c++
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST, ...);
...
if (message == WM_MAGPIE_SCALINGCHANGED) {
switch (wParam) {
case 0:
// 缩放已结束
break;
case 1:
// 缩放已开始
// 将本窗口置于缩放窗口上面
BringWindowToTop(hWnd);
break;
default:
break;
}
}
```

## 如何获取缩放信息

缩放窗口的[窗口属性](https://learn.microsoft.com/en-us/windows/win32/winmsg/about-window-properties)中存储着缩放信息。目前支持以下属性:
* `Magpie.SrcHWND`: 源窗口句柄
* `Magpie.SrcLeft`、`Magpie.SrcTop`、`Magpie.SrcRight`、`Magpie.SrcBottom`: 被缩放区域的边界
* `Magpie.DestLeft`、`Magpie.DestTop`、`Magpie.DestRight`、`Magpie.DestBottom`: 缩放后区域矩形边界

```c++
HWND hwndSrc = (HWND)GetProp(hwndScaling, L"Magpie.SrcHWND");

RECT srcRect;
srcRect.left = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcLeft");
srcRect.top = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcTop");
srcRect.right = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcRight");
srcRect.bottom = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.SrcBottom");

RECT destRect;
destRect.left = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestLeft");
destRect.top = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestTop");
destRect.right = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestRight");
destRect.bottom = (LONG)(INT_PTR)GetProp(hwndScaling, L"Magpie.DestBottom");
```

## 如何使 Magpie 在你的窗口位于前台时保持缩放

前台窗口改变时 Magpie 会停止缩放,只对某些系统窗口例外。你可以通过设置属性 `Magpie.ToolWindow` 将自己的窗口添加入例外,这对由该窗口拥有 (owned) 的窗口也有效。

```c++
SetProp(hWnd, L"Magpie.ToolWindow", (HANDLE)TRUE);
```
60 changes: 55 additions & 5 deletions src/Magpie.Core/ScalingWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,21 @@
#include "FrameSourceBase.h"
#include "ExclModeHelper.h"
#include "StrUtils.h"
#include "Utils.h"

namespace Magpie::Core {

static UINT WM_MAGPIE_SCALING_CHANGED;

static void InitMessage() noexcept {
static Utils::Ignore _ = []() {
WM_MAGPIE_SCALING_CHANGED =
RegisterWindowMessage(CommonSharedConstants::WM_MAGPIE_SCALING_CHANGED);
return Utils::Ignore();
}();
}


ScalingWindow::ScalingWindow() noexcept {}

ScalingWindow::~ScalingWindow() noexcept {}
Expand Down Expand Up @@ -116,6 +128,8 @@ bool ScalingWindow::Create(
return false;
}

InitMessage();

#if _DEBUG
OutputDebugString(fmt::format(L"可执行文件路径: {}\n窗口类: {}\n",
Win32Utils::GetPathOfWnd(hwndSrc), Win32Utils::GetWndClassName(hwndSrc)).c_str());
Expand Down Expand Up @@ -169,7 +183,7 @@ bool ScalingWindow::Create(

const HINSTANCE hInstance = wil::GetModuleInstanceHandle();

static const int _ = [](HINSTANCE hInstance) {
static Utils::Ignore _ = [](HINSTANCE hInstance) {
WNDCLASSEXW wcex{
.cbSize = sizeof(wcex),
.lpfnWndProc = _WndProc,
Expand All @@ -179,7 +193,7 @@ bool ScalingWindow::Create(
};
RegisterClassEx(&wcex);

return 0;
return Utils::Ignore();
}(hInstance);

CreateWindowEx(
Expand Down Expand Up @@ -235,6 +249,8 @@ bool ScalingWindow::Create(
}
}

_SetWindowProps();

// 缩放窗口可能有 WS_MAXIMIZE 样式,因此使用 SetWindowsPos 而不是 ShowWindow
// 以避免 OS 更改窗口尺寸和位置。
SetWindowPos(
Expand Down Expand Up @@ -273,6 +289,9 @@ bool ScalingWindow::Create(
})();
};

// 广播开始缩放
PostMessage(HWND_BROADCAST, WM_MAGPIE_SCALING_CHANGED, 1, (LPARAM)_hWnd);

return true;
}

Expand Down Expand Up @@ -397,6 +416,9 @@ LRESULT ScalingWindow::_MessageHandler(UINT msg, WPARAM wParam, LPARAM lParam) n

// 还原时钟精度
timeEndPeriod(1);

// 广播停止缩放
PostMessage(HWND_BROADCAST, WM_MAGPIE_SCALING_CHANGED, 0, 0);
break;
}
}
Expand Down Expand Up @@ -445,7 +467,18 @@ int ScalingWindow::_CheckSrcState() const noexcept {
}

bool ScalingWindow::_CheckForeground(HWND hwndForeground) const noexcept {
std::wstring className = Win32Utils::GetWndClassName(hwndForeground);
// 检查所有者链是否存在 Magpie.ToolWindow 属性
{
HWND hWnd = hwndForeground;
do {
if (GetProp(hWnd, L"Magpie.ToolWindow")) {
// 继续缩放
return true;
}

hWnd = GetWindowOwner(hWnd);
} while (hWnd);
}

if (WindowHelper::IsForbiddenSystemWindow(hwndForeground)) {
return true;
Expand All @@ -471,7 +504,7 @@ bool ScalingWindow::_DisableDirectFlip(HINSTANCE hInstance) noexcept {
// 没有显式关闭 DirectFlip 的方法
// 将全屏窗口设为稍微透明,以灰色全屏窗口为背景

static const int _ = [](HINSTANCE hInstance) {
static Utils::Ignore _ = [](HINSTANCE hInstance) {
WNDCLASSEXW wcex{
.cbSize = sizeof(wcex),
.lpfnWndProc = DefWindowProc,
Expand All @@ -482,7 +515,7 @@ bool ScalingWindow::_DisableDirectFlip(HINSTANCE hInstance) noexcept {
};
RegisterClassEx(&wcex);

return 0;
return Utils::Ignore();
}(hInstance);

_hwndDDF = CreateWindowEx(
Expand Down Expand Up @@ -522,4 +555,21 @@ bool ScalingWindow::_DisableDirectFlip(HINSTANCE hInstance) noexcept {
return true;
}

// 用于和其他程序交互
void ScalingWindow::_SetWindowProps() const noexcept {
SetProp(_hWnd, L"Magpie.SrcHWND", _hwndSrc);

const RECT& srcRect = _renderer->SrcRect();
SetProp(_hWnd, L"Magpie.SrcLeft", (HANDLE)(INT_PTR)srcRect.left);
SetProp(_hWnd, L"Magpie.SrcTop", (HANDLE)(INT_PTR)srcRect.top);
SetProp(_hWnd, L"Magpie.SrcRight", (HANDLE)(INT_PTR)srcRect.right);
SetProp(_hWnd, L"Magpie.SrcBottom", (HANDLE)(INT_PTR)srcRect.bottom);

const RECT& destRect = _renderer->DestRect();
SetProp(_hWnd, L"Magpie.DestLeft", (HANDLE)(INT_PTR)destRect.left);
SetProp(_hWnd, L"Magpie.DestTop", (HANDLE)(INT_PTR)destRect.top);
SetProp(_hWnd, L"Magpie.DestRight", (HANDLE)(INT_PTR)destRect.right);
SetProp(_hWnd, L"Magpie.DestBottom", (HANDLE)(INT_PTR)destRect.bottom);
}

}
2 changes: 2 additions & 0 deletions src/Magpie.Core/ScalingWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class ScalingWindow : public WindowBase<ScalingWindow> {

bool _DisableDirectFlip(HINSTANCE hInstance) noexcept;

void _SetWindowProps() const noexcept;

winrt::DispatcherQueue _dispatcher{ nullptr };

RECT _wndRect{};
Expand Down
5 changes: 3 additions & 2 deletions src/Magpie/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
#include "ThemeHelper.h"
#include "XamlApp.h"
#include <ShellScalingApi.h>
#include "Utils.h"

#pragma comment(lib, "Shcore.lib")

namespace Magpie {

bool MainWindow::Create(HINSTANCE hInstance, winrt::Point windowCenter, winrt::Size windowSizeInDips, bool isMaximized) noexcept {
static const int _ = [](HINSTANCE hInstance) {
static Utils::Ignore _ = [](HINSTANCE hInstance) {
WNDCLASSEXW wcex{
.cbSize = sizeof(wcex),
.lpfnWndProc = _WndProc,
Expand All @@ -28,7 +29,7 @@ bool MainWindow::Create(HINSTANCE hInstance, winrt::Point windowCenter, winrt::S
wcex.lpszClassName = CommonSharedConstants::TITLE_BAR_WINDOW_CLASS_NAME;
RegisterClassEx(&wcex);

return 0;
return Utils::Ignore();
}(hInstance);

const auto& [posToSet, sizeToSet] = _CreateWindow(hInstance, windowCenter, windowSizeInDips);
Expand Down
Loading