feat(ads/seo/release): v1.2.0 with live Google Ads, isolated ad profile, ads.txt, and SEO/AEO/GEO optimization
This commit is contained in:
parent
a88aafa1e5
commit
0e568f1f0a
975 changed files with 130593 additions and 16783 deletions
106
src/Paca.App/Services/GlobalHotkeyService.cs
Normal file
106
src/Paca.App/Services/GlobalHotkeyService.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Paca.App.Services;
|
||||
|
||||
public sealed class GlobalHotkeyService : IDisposable
|
||||
{
|
||||
public const uint MOD_ALT = 0x0001;
|
||||
public const uint MOD_CONTROL = 0x0002;
|
||||
public const uint MOD_SHIFT = 0x0004;
|
||||
public const uint MOD_WIN = 0x0008;
|
||||
public const uint MOD_NOREPEAT = 0x4000;
|
||||
public const int WM_HOTKEY = 0x0312;
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||
|
||||
private IntPtr _hWnd;
|
||||
private int _currentId;
|
||||
private bool _isRegistered;
|
||||
|
||||
public bool IsRegistered => _isRegistered;
|
||||
|
||||
public event Action? HotkeyPressed;
|
||||
|
||||
public bool Register(IntPtr hWnd, int id, string hotkeyStr)
|
||||
{
|
||||
Unregister();
|
||||
|
||||
if (!TryParseHotkey(hotkeyStr, out uint modifiers, out uint vk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_hWnd = hWnd;
|
||||
_currentId = id;
|
||||
_isRegistered = RegisterHotKey(_hWnd, _currentId, modifiers | MOD_NOREPEAT, vk);
|
||||
return _isRegistered;
|
||||
}
|
||||
|
||||
public void Unregister()
|
||||
{
|
||||
if (_isRegistered && _hWnd != IntPtr.Zero)
|
||||
{
|
||||
UnregisterHotKey(_hWnd, _currentId);
|
||||
_isRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr HandleWindowMessages(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
if (msg == WM_HOTKEY && wParam.ToInt32() == _currentId)
|
||||
{
|
||||
HotkeyPressed?.Invoke();
|
||||
handled = true;
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
public static bool TryParseHotkey(string? hotkeyStr, out uint fsModifiers, out uint vk)
|
||||
{
|
||||
fsModifiers = 0;
|
||||
vk = 0;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(hotkeyStr)) return false;
|
||||
|
||||
var parts = hotkeyStr.Split('+', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 0) return false;
|
||||
|
||||
for (int i = 0; i < parts.Length - 1; i++)
|
||||
{
|
||||
var part = parts[i].ToLowerInvariant();
|
||||
if (part is "ctrl" or "control") fsModifiers |= MOD_CONTROL;
|
||||
else if (part is "alt") fsModifiers |= MOD_ALT;
|
||||
else if (part is "shift") fsModifiers |= MOD_SHIFT;
|
||||
else if (part is "win" or "windows") fsModifiers |= MOD_WIN;
|
||||
else return false;
|
||||
}
|
||||
|
||||
var keyPart = parts[^1].ToUpperInvariant();
|
||||
if (keyPart is "SPACE") vk = 0x20;
|
||||
else if (keyPart is "RETURN" or "ENTER") vk = 0x0D;
|
||||
else if (keyPart is "ESCAPE" or "ESC") vk = 0x1B;
|
||||
else if (keyPart.Length == 1 && keyPart[0] >= 'A' && keyPart[0] <= 'Z') vk = (uint)keyPart[0];
|
||||
else if (keyPart.Length == 1 && keyPart[0] >= '0' && keyPart[0] <= '9') vk = (uint)keyPart[0];
|
||||
else if (keyPart.StartsWith("F") && int.TryParse(keyPart[1..], out int fNum) && fNum >= 1 && fNum <= 24)
|
||||
{
|
||||
vk = (uint)(0x70 + (fNum - 1)); // VK_F1 = 0x70
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Unregister();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue