mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
添加文档
This commit is contained in:
parent
ccb9855b89
commit
2a1df02e1d
5 changed files with 50 additions and 19 deletions
|
|
@ -33,9 +33,9 @@ namespace Magpie.CursorHook {
|
|||
private readonly IntPtr _hwndSrc;
|
||||
|
||||
private readonly (int x, int y) _cursorSize = NativeMethods.GetCursorSize();
|
||||
|
||||
// 用于保存窗口类的 HCURSOR,以在卸载钩子时还原
|
||||
private readonly Dictionary<IntPtr, IntPtr> _hwndTohCursor = new Dictionary<IntPtr, IntPtr>();
|
||||
|
||||
// 保存已替换 HCURSOR 的窗口,以在卸载钩子时还原
|
||||
private readonly HashSet<IntPtr> _replacedHwnds = new HashSet<IntPtr>();
|
||||
|
||||
// 原光标到透明光标的映射
|
||||
private readonly Dictionary<IntPtr, IntPtr> _hCursorToTptCursor = new Dictionary<IntPtr, IntPtr>();
|
||||
|
|
@ -94,7 +94,7 @@ namespace Magpie.CursorHook {
|
|||
|
||||
// 将窗口类中的 HCURSOR 替换为透明光标
|
||||
void replaceHCursor(IntPtr hWnd) {
|
||||
if (_hwndTohCursor.ContainsKey(hWnd)) {
|
||||
if (_replacedHwnds.Contains(hWnd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ namespace Magpie.CursorHook {
|
|||
|
||||
// 替换窗口类的 HCURSOR
|
||||
if (NativeMethods.SetClassLong(hWnd, NativeMethods.GCLP_HCURSOR, hTptCursor) == hCursor) {
|
||||
_hwndTohCursor[hWnd] = hCursor;
|
||||
_replacedHwnds.Add(hWnd);
|
||||
} else {
|
||||
ReportIfFalse(false, "SetClassLong 失败");
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ namespace Magpie.CursorHook {
|
|||
// 替换窗口类的 HCURSOR
|
||||
if (NativeMethods.SetClassLong(hWnd, NativeMethods.GCLP_HCURSOR, hTptCursor) == hCursor) {
|
||||
// 替换成功
|
||||
_hwndTohCursor[hWnd] = hCursor;
|
||||
_replacedHwnds.Add(hWnd);
|
||||
_hCursorToTptCursor[hCursor] = hTptCursor;
|
||||
} else {
|
||||
NativeMethods.DestroyCursor(hTptCursor);
|
||||
|
|
@ -173,7 +173,7 @@ namespace Magpie.CursorHook {
|
|||
),
|
||||
"PostMessage 失败"
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
// Loop until FileMonitor closes (i.e. IPC fails)
|
||||
while (true) {
|
||||
|
|
@ -201,9 +201,23 @@ namespace Magpie.CursorHook {
|
|||
}
|
||||
|
||||
// 退出前重置窗口类的光标
|
||||
foreach (var item in _hwndTohCursor) {
|
||||
foreach (var hwnd in _replacedHwnds) {
|
||||
IntPtr hCursor = (IntPtr)NativeMethods.GetClassLong(hwnd, NativeMethods.GCLP_HCURSOR);
|
||||
if (hCursor == IntPtr.Zero) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 在 _hCursorToTptCursor 中反向查找
|
||||
var item = _hCursorToTptCursor
|
||||
.FirstOrDefault(pair => pair.Value == hCursor);
|
||||
|
||||
if(item.Key == IntPtr.Zero || item.Value == IntPtr.Zero) {
|
||||
// 找不到就不替换
|
||||
continue;
|
||||
}
|
||||
|
||||
// 忽略错误
|
||||
NativeMethods.SetClassLong(item.Key, NativeMethods.GCLP_HCURSOR, item.Value);
|
||||
NativeMethods.SetClassLong(hwnd, NativeMethods.GCLP_HCURSOR, item.Key);
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
|
|
@ -223,10 +237,9 @@ namespace Magpie.CursorHook {
|
|||
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public delegate IntPtr SetCursor_Delegate(IntPtr hCursor);
|
||||
|
||||
|
||||
// 取代 SetCursor 的钩子
|
||||
public IntPtr SetCursor_Hook(IntPtr hCursor) {
|
||||
ReportToServer("setcursor");
|
||||
// ReportToServer("setcursor");
|
||||
|
||||
if (!NativeMethods.IsWindow(_hwndHost) || hCursor == IntPtr.Zero) {
|
||||
// 全屏窗口关闭后钩子不做任何操作
|
||||
|
|
@ -309,7 +322,9 @@ namespace Magpie.CursorHook {
|
|||
|
||||
private void ReportToServer(string msg) {
|
||||
#if DEBUG
|
||||
_messageQueue.Enqueue(msg);
|
||||
if(_messageQueue.Count < 1000) {
|
||||
_messageQueue.Enqueue(msg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -2,21 +2,19 @@
|
|||
|
||||
窗口放大镜!
|
||||
|
||||
可以将任意窗口全屏显示,支持高级缩放算法,包括 Jinc2、[Anime4K](https://github.com/bloc97/Anime4K)(本项目包含一个hlsl移植)、Lanczos6等。
|
||||
可以将任意窗口全屏显示,支持高级缩放算法,包括 Jinc、[Anime4K](https://github.com/bloc97/Anime4K)(本项目包含一个hlsl移植)、Lanczos等。
|
||||
|
||||
主要用于游戏窗口的放大显示,适用于那些不支持全屏模式,或者游戏自带的全屏模式会使画面模糊的情况。
|
||||
|
||||
本项目还处于早期阶段,欢迎fork和star,欢迎任何形式的贡献。
|
||||
|
||||
注意:欲使用本程序你需要一个性能足够的GPU,尤其是使用Anime4K缩放模式时。
|
||||
|
||||
## 窗口截图
|
||||
|
||||

|
||||
|
||||
使用方法:程序启动后,激活要放大的窗口,按下热键即可全屏显示该窗口,再次按下热键将退出全屏。
|
||||
|
||||
目前缩放模式仅支持通用(Jinc2+自适应锐化)以及Anime4K(Anime4K+HQBicubic)。(程序内使用json,因此你可以轻松地组合出自己的缩放模式)
|
||||
目前缩放模式仅支持通用(Jinc2+自适应锐化)以及Anime4K(Anime4K+mitchell+自适应锐化)。(程序内使用json,因此你可以轻松地组合出自己的缩放模式)
|
||||
|
||||
## 效果截图
|
||||
|
||||
|
|
@ -42,9 +40,13 @@
|
|||
|
||||

|
||||
|
||||
## 已知限制
|
||||
## 实现原理
|
||||
|
||||
由于实现的限制,在帧数较低时鼠标将运动迟缓。目前不支持使用自定义鼠标的窗口。
|
||||
尽管功能与[Lossless Scaling](https://store.steampowered.com/app/993090/Lossless_Scaling/)和[IntegerScaler](https://tanalin.com/en/projects/integer-scaler/)类似,但本程序的实现原理与它们完全不同。Lossless Scaling和IntegerScaler使用[Magnification API](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/magapi/entry-magapi-sdk)实现对窗口的放大,但此API无法实现高级缩放算法,其核心函数[MagSetImageScalingCallback](https://docs.microsoft.com/en-us/windows/win32/api/magnification/nf-magnification-magsetimagescalingcallback)已被废弃,因此它们必须与显卡驱动打交道,而你的显卡很可能不被支持。此外,它们只支持整数倍的放大,这极大限制了它们的使用场景。举例来说,它们无法把一个1024x768大小的窗口放大到1920x1080。
|
||||
|
||||
本程序使用了一个十分符合直觉的方式放大窗口:使用一个全屏窗口覆盖屏幕,捕获原窗口的内容放大后在该全屏窗口显示出来。这种方式使得缩放算法不受任何限制,让我们可以自由使用现存的优秀缩放算法。为了使用GPU加速,全屏窗口使用了Direct2D技术,将缩放算法实现为[Direct2D Effect](https://docs.microsoft.com/en-us/windows/win32/direct2d/effects-overview),通过Effect的堆叠,我们可以用任何方式缩放窗口,以取得完美的效果。
|
||||
|
||||
这种方案唯一的限制便是系统光标,因此这里使用了一点hack:将系统的光标替换为透明,然后在全屏窗口上绘制它,因此虽然光标始终处于源窗口内,但其不可见。大多数情况下,这些更改不会被用户感知到,尽管如此,如果源窗口使用了自定义光标,用户会在屏幕上看到两个光标。为了解决这个问题,我们提供了一个更深入的hack选项,即注入源窗口的进程,将其自定义光标也替换为透明,然后在全屏窗口上将其绘制,更深入的解释见[光标映射](./光标映射.md)。大多数情况下它可以工作的很好,但因为Windows生态的复杂性,实际效果还有待测试。
|
||||
|
||||
## 开发计划
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public:
|
|||
noDisturb
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// 不可复制,不可移动
|
||||
MagWindow(const MagWindow&) = delete;
|
||||
MagWindow(MagWindow&&) = delete;
|
||||
|
|
|
|||
BIN
img/CursorHook.png
Normal file
BIN
img/CursorHook.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
14
光标映射.md
Normal file
14
光标映射.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Magpie中的光标映射
|
||||
|
||||
CursorHook的功能是隐藏源窗口中的光标,并在全屏窗口上绘制该光标。下图演示了它的工作原理:
|
||||
|
||||

|
||||
|
||||
1. Magpie使用[EasyHook](http://easyhook.github.io/)将CursorHook.dll注入源窗口进程,替换源窗口中的[SetCursor](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setcursor)
|
||||
2. 源窗口试图使用SetCursor设置自定义光标
|
||||
3. 注入程序维护一个源光标到对应透明光标的映射,首先在该映射中查找该光标。如果找到,使用该光标对应的透明光标调用系统的SetCursor,转到第7步;如果未找到,转到第4步
|
||||
4. 创建一个热点相同的透明光标,创建新的映射
|
||||
5. 使用PostMessage将该映射通知全屏窗口。全屏窗口维护着一个透明光标到源光标的映射,收到消息后创建该映射
|
||||
6. 使用第4步创建的透明光标调用系统的SetCursor
|
||||
7. 全屏窗口渲染时使用[GetCursorInfo](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorinfo)获取当前光标,此时系统返回注入程序里设置的透明光标
|
||||
8. 全屏窗口在映射中查找该透明光标,并在窗口上绘制其对应的源光标
|
||||
Loading…
Add table
Add a link
Reference in a new issue