├── .gitattributes ├── .gitignore ├── .gitmodules ├── KeyMouse.sln ├── KeyMouse ├── KeyMouse.cpp ├── KeyMouse.h ├── KeyMouse.ico ├── KeyMouse.rc ├── KeyMouse.vcxproj ├── KeyMouse.vcxproj.filters ├── ReadMe.txt ├── Resource.h ├── Tray.ico ├── UIAHandler.cpp ├── UIAHandler.h ├── config.json ├── ctx.cpp ├── ctx.h ├── def.h ├── hotkey_handler.cpp ├── hotkey_handler.h ├── small.ico ├── stdafx.cpp ├── stdafx.h ├── tag.cpp ├── tag.h ├── targetver.h ├── utils.cpp ├── utils.h └── wndproc_handler.cpp ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "KeyMouse/json"] 2 | path = KeyMouse/json 3 | url = https://github.com/nlohmann/json.git 4 | -------------------------------------------------------------------------------- /KeyMouse.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.902 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeyMouse", "KeyMouse\KeyMouse.vcxproj", "{074EAA01-0649-449D-B909-E9672C9C534A}" 7 | EndProject 8 | Global 9 | GlobalSection(Performance) = preSolution 10 | HasPerformanceSessions = true 11 | EndGlobalSection 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|x64 = Debug|x64 14 | Debug|x86 = Debug|x86 15 | Release|x64 = Release|x64 16 | Release|x86 = Release|x86 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {074EAA01-0649-449D-B909-E9672C9C534A}.Debug|x64.ActiveCfg = Debug|x64 20 | {074EAA01-0649-449D-B909-E9672C9C534A}.Debug|x64.Build.0 = Debug|x64 21 | {074EAA01-0649-449D-B909-E9672C9C534A}.Debug|x86.ActiveCfg = Debug|Win32 22 | {074EAA01-0649-449D-B909-E9672C9C534A}.Debug|x86.Build.0 = Debug|Win32 23 | {074EAA01-0649-449D-B909-E9672C9C534A}.Release|x64.ActiveCfg = Release|x64 24 | {074EAA01-0649-449D-B909-E9672C9C534A}.Release|x64.Build.0 = Release|x64 25 | {074EAA01-0649-449D-B909-E9672C9C534A}.Release|x86.ActiveCfg = Release|Win32 26 | {074EAA01-0649-449D-B909-E9672C9C534A}.Release|x86.Build.0 = Release|Win32 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | GlobalSection(ExtensibilityGlobals) = postSolution 32 | SolutionGuid = {75C3B7DA-57CF-44D4-B74B-DCFB70449922} 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /KeyMouse/KeyMouse.cpp: -------------------------------------------------------------------------------- 1 | // KeyMouse.cpp : Defines the entry point for the application. 2 | // 3 | 4 | #include "stdafx.h" 5 | #include "KeyMouse.h" 6 | 7 | 8 | 9 | // Global Variables: 10 | HINSTANCE hInst; // current instance 11 | WCHAR szTitle[MAX_LOADSTRING]; // The title bar text 12 | WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name 13 | NOTIFYICONDATA structNID; // The tray struct 14 | HHOOK g_hKeyboardHook; // for keyboard hook 15 | HWND g_hMainWndForHook; 16 | 17 | 18 | // Forward declarations of functions included in this code module: 19 | ATOM MyRegisterClass(HINSTANCE hInstance); 20 | HWND InitInstance(HINSTANCE, int); 21 | LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 22 | INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); 23 | void InitTray(HINSTANCE hInstance, HWND hwnd); 24 | 25 | 26 | /** 27 | * @brief: This hook tries to detect if users use keyboard. If it's true, make 28 | * cache expired. 29 | * 30 | * 31 | * @param: int nCode: 32 | * WPARAM wParam: 33 | * LPARAM lParam: check these params in mcrosoft docs. 34 | * 35 | * @return: LRESULT 36 | */ 37 | LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 38 | { 39 | KBDLLHOOKSTRUCT *pKbdllHookStruct = (KBDLLHOOKSTRUCT*)lParam; 40 | 41 | KeyMouse::Context *pCtx = 42 | reinterpret_cast( 43 | GetClassLongPtr(g_hMainWndForHook, 0) 44 | ); 45 | 46 | if (wParam == WM_KEYDOWN) { 47 | if (pKbdllHookStruct) { 48 | if (!(pKbdllHookStruct->flags & LLKHF_EXTENDED) && !(pKbdllHookStruct->flags & LLKHF_ALTDOWN)) { 49 | KeyMouse::KeybindingMap KeyBindings = pCtx->GetKeybindingMap(); 50 | WORD VirtualKey = HIWORD(KeyBindings["escape"].lParam); 51 | if (LOWORD(pKbdllHookStruct->vkCode) != VirtualKey) { 52 | pCtx->SetCacheExpiredState(true); 53 | } 54 | } 55 | 56 | } 57 | } 58 | return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam); 59 | } 60 | 61 | int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 62 | _In_opt_ HINSTANCE hPrevInstance, 63 | _In_ LPWSTR lpCmdLine, 64 | _In_ int nCmdShow) 65 | { 66 | _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)|_CRTDBG_LEAK_CHECK_DF); 67 | //_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 68 | UNREFERENCED_PARAMETER(hPrevInstance); 69 | UNREFERENCED_PARAMETER(lpCmdLine); 70 | 71 | // TODO: Place code here. 72 | 73 | // Initialize global strings 74 | LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 75 | LoadStringW(hInstance, IDC_KEYMOUSE, szWindowClass, MAX_LOADSTRING); 76 | MyRegisterClass(hInstance); 77 | 78 | HWND hWnd; 79 | // Perform application initialization: 80 | if (!(hWnd = InitInstance (hInstance, nCmdShow))) 81 | { 82 | return FALSE; 83 | } 84 | 85 | HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KEYMOUSE)); 86 | 87 | // Initialize COM before using UI Automation. 88 | HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); 89 | //hr = InitializeUIAutomation(&pAutomation); 90 | std::unique_ptr pCtx(new KeyMouse::Context()); 91 | if (pCtx->GetProfile().enable_cache) { 92 | pCtx->InitTimer(hWnd); 93 | } 94 | g_hMainWndForHook = hWnd; 95 | 96 | HINSTANCE hNullInstance = GetModuleHandle(NULL); 97 | g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hNullInstance, 0); 98 | // Store a Context pointer in extra space of window hWnd. 99 | SetClassLongPtr(hWnd, 0, reinterpret_cast(pCtx.get())); 100 | 101 | KeyMouse::RegisterAllHotKey(hWnd); 102 | 103 | MSG msg; 104 | 105 | // Main message loop: 106 | while (GetMessage(&msg, nullptr, 0, 0)) 107 | { 108 | if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 109 | { 110 | TranslateMessage(&msg); 111 | DispatchMessage(&msg); 112 | } 113 | } 114 | 115 | KeyMouse::UnregisterAllHotKey(hWnd); 116 | //pAutomation->RemoveAllEventHandlers(); 117 | //pAutomation->Release(); 118 | //CComPtr pAutomation = pCtx->GetAutomation(); 119 | //pAutomation->RemoveAllEventHandlers(); 120 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 121 | pAutomation->RemoveAllEventHandlers(); 122 | pAutomation->Release(); 123 | CoUninitialize(); 124 | 125 | pCtx.reset(); 126 | _CrtDumpMemoryLeaks(); 127 | 128 | return (int) msg.wParam; 129 | } 130 | 131 | 132 | 133 | // 134 | // FUNCTION: MyRegisterClass() 135 | // 136 | // PURPOSE: Registers the window class. 137 | // 138 | ATOM MyRegisterClass(HINSTANCE hInstance) 139 | { 140 | WNDCLASSEXW wcex; 141 | 142 | wcex.cbSize = sizeof(WNDCLASSEX); 143 | 144 | wcex.style = CS_HREDRAW | CS_VREDRAW; 145 | wcex.lpfnWndProc = WndProc; 146 | wcex.cbClsExtra = sizeof(KeyMouse::Context*); // Extra space for Context. 147 | wcex.cbWndExtra = 0; 148 | wcex.hInstance = hInstance; 149 | wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KEYMOUSE)); 150 | wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 151 | wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 152 | wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_KEYMOUSE); 153 | wcex.lpszClassName = szWindowClass; 154 | wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 155 | 156 | return RegisterClassExW(&wcex); 157 | } 158 | 159 | void InitTray(HINSTANCE hInstance, HWND hwnd) { 160 | structNID.cbSize = sizeof(NOTIFYICONDATA); 161 | structNID.hWnd = hwnd; 162 | structNID.uID = IDI_TRAY; 163 | structNID.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO; 164 | structNID.uCallbackMessage = WM_TRAY; 165 | structNID.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TRAY)); 166 | lstrcpy(structNID.szTip, TEXT("KeyMouse")); 167 | 168 | Shell_NotifyIcon(NIM_ADD, &structNID); 169 | } 170 | 171 | 172 | // 173 | // FUNCTION: InitInstance(HINSTANCE, int) 174 | // 175 | // PURPOSE: Saves instance handle and creates main window 176 | // 177 | // COMMENTS: 178 | // 179 | // In this function, we save the instance handle in a global variable and 180 | // create and display the main program window. 181 | // 182 | HWND InitInstance(HINSTANCE hInstance, int nCmdShow) 183 | { 184 | hInst = hInstance; // Store instance handle in our global variable 185 | 186 | HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 187 | CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); 188 | 189 | if (!hWnd) 190 | { 191 | return hWnd; 192 | } 193 | 194 | //ShowWindow(hWnd, nCmdShow); 195 | UpdateWindow(hWnd); 196 | InitTray(hInst, hWnd); 197 | 198 | return hWnd; 199 | } 200 | 201 | // 202 | // 203 | // 204 | // FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) 205 | // 206 | // PURPOSE: Processes messages for the main window. 207 | // 208 | // WM_COMMAND - process the application menu 209 | // WM_PAINT - Paint the main window 210 | // WM_DESTROY - post a quit message and return 211 | LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 212 | { 213 | KeyMouse::WndEventArgs Wea; 214 | Wea.hWnd = hWnd; 215 | Wea.wParam = wParam; 216 | Wea.lParam = lParam; 217 | Wea.hInst = hInst; 218 | 219 | // Get current context. 220 | KeyMouse::Context *pCtx = 221 | reinterpret_cast( 222 | GetClassLongPtr(hWnd, 0) 223 | ); 224 | if (pCtx) { 225 | KeyMouse::WndProcHandler WPHandler = pCtx->GetWndProcHandler(); 226 | return WPHandler.HandlerEntrance(message, Wea); 227 | } 228 | 229 | return DefWindowProc(hWnd, message, wParam, lParam); 230 | 231 | } 232 | 233 | -------------------------------------------------------------------------------- /KeyMouse/KeyMouse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "resource.h" 4 | #include "utils.h" 5 | #include "ctx.h" 6 | #include "hotkey_handler.h" 7 | 8 | #define MAX_LOADSTRING 100 9 | -------------------------------------------------------------------------------- /KeyMouse/KeyMouse.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iscooool/KeyMouse/433ab713645ac98033fca61c2ce5dfe68a169c3d/KeyMouse/KeyMouse.ico -------------------------------------------------------------------------------- /KeyMouse/KeyMouse.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iscooool/KeyMouse/433ab713645ac98033fca61c2ce5dfe68a169c3d/KeyMouse/KeyMouse.rc -------------------------------------------------------------------------------- /KeyMouse/KeyMouse.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {074EAA01-0649-449D-B909-E9672C9C534A} 23 | Win32Proj 24 | KeyMouse 25 | 10.0.17763.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | Unicode 53 | false 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | Disabled 90 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 91 | true 92 | 93 | 94 | Windows 95 | true 96 | 97 | 98 | 99 | 100 | NotUsing 101 | Level3 102 | Disabled 103 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Windows 108 | true 109 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 110 | 111 | 112 | PerMonitorHighDPIAware 113 | 114 | 115 | 116 | 117 | Level3 118 | Use 119 | MaxSpeed 120 | true 121 | true 122 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 123 | true 124 | 125 | 126 | Windows 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | Level3 135 | Use 136 | MaxSpeed 137 | true 138 | true 139 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 140 | true 141 | MultiThreaded 142 | 143 | 144 | Windows 145 | true 146 | true 147 | true 148 | 149 | 150 | PerMonitorHighDPIAware 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | Create 174 | Create 175 | Create 176 | Create 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /KeyMouse/KeyMouse.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | 79 | 80 | Resource Files 81 | 82 | 83 | 84 | 85 | Resource Files 86 | 87 | 88 | Resource Files 89 | 90 | 91 | Resource Files 92 | 93 | 94 | -------------------------------------------------------------------------------- /KeyMouse/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | WIN32 APPLICATION : KeyMouse Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this KeyMouse application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your KeyMouse application. 9 | 10 | 11 | KeyMouse.vcxproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | KeyMouse.vcxproj.filters 18 | This is the filters file for VC++ projects generated using an Application Wizard. 19 | It contains information about the association between the files in your project 20 | and the filters. This association is used in the IDE to show grouping of files with 21 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 22 | "Source Files" filter). 23 | 24 | KeyMouse.cpp 25 | This is the main application source file. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | AppWizard has created the following resources: 29 | 30 | KeyMouse.rc 31 | This is a listing of all of the Microsoft Windows resources that the 32 | program uses. It includes the icons, bitmaps, and cursors that are stored 33 | in the RES subdirectory. This file can be directly edited in Microsoft 34 | Visual C++. 35 | 36 | Resource.h 37 | This is the standard header file, which defines new resource IDs. 38 | Microsoft Visual C++ reads and updates this file. 39 | 40 | KeyMouse.ico 41 | This is an icon file, which is used as the application's icon (32x32). 42 | This icon is included by the main resource file KeyMouse.rc. 43 | 44 | small.ico 45 | This is an icon file, which contains a smaller version (16x16) 46 | of the application's icon. This icon is included by the main resource 47 | file KeyMouse.rc. 48 | 49 | ///////////////////////////////////////////////////////////////////////////// 50 | Other standard files: 51 | 52 | StdAfx.h, StdAfx.cpp 53 | These files are used to build a precompiled header (PCH) file 54 | named KeyMouse.pch and a precompiled types file named StdAfx.obj. 55 | 56 | ///////////////////////////////////////////////////////////////////////////// 57 | Other notes: 58 | 59 | AppWizard uses "TODO:" comments to indicate parts of the source code you 60 | should add to or customize. 61 | 62 | ///////////////////////////////////////////////////////////////////////////// 63 | -------------------------------------------------------------------------------- /KeyMouse/Resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iscooool/KeyMouse/433ab713645ac98033fca61c2ce5dfe68a169c3d/KeyMouse/Resource.h -------------------------------------------------------------------------------- /KeyMouse/Tray.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iscooool/KeyMouse/433ab713645ac98033fca61c2ce5dfe68a169c3d/KeyMouse/Tray.ico -------------------------------------------------------------------------------- /KeyMouse/UIAHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ctx.h" 3 | #include "utils.h" 4 | 5 | namespace KeyMouse { 6 | HRESULT STDMETHODCALLTYPE EventHandler::HandleStructureChangedEvent(IUIAutomationElement* pSender, StructureChangeType changeType, SAFEARRAY* pRuntimeID) { 7 | eventCount_++; 8 | switch (changeType) 9 | { 10 | case StructureChangeType_ChildAdded: 11 | case StructureChangeType_ChildRemoved: 12 | case StructureChangeType_ChildrenInvalidated: 13 | case StructureChangeType_ChildrenBulkAdded: 14 | case StructureChangeType_ChildrenBulkRemoved: 15 | case StructureChangeType_ChildrenReordered: { 16 | Context *pCtx = reinterpret_cast(GetClassLongPtr(hMainWnd_, 0)); 17 | KeybindingMap keybinding_map = pCtx->GetKeybindingMap(); 18 | PostMessage(hMainWnd_, WM_HOTKEY, 0, keybinding_map["selectMode"].lParam); 19 | } 20 | } 21 | return S_OK; 22 | }; 23 | 24 | //TODO: eventhandler needs to be cleaned up. 25 | HRESULT STDMETHODCALLTYPE CacheEventHandler::HandleStructureChangedEvent(IUIAutomationElement* pSender, StructureChangeType changeType, SAFEARRAY* pRuntimeID) { 26 | eventCount_++; 27 | switch (changeType) 28 | { 29 | case StructureChangeType_ChildAdded: 30 | case StructureChangeType_ChildRemoved: 31 | case StructureChangeType_ChildrenInvalidated: 32 | case StructureChangeType_ChildrenBulkAdded: 33 | case StructureChangeType_ChildrenBulkRemoved: 34 | case StructureChangeType_ChildrenReordered: { 35 | Context *pCtx = reinterpret_cast(GetClassLongPtr(hMainWnd_, 0)); 36 | pCtx->SetCacheExpiredStructChanged(true); 37 | break; 38 | } 39 | } 40 | return S_OK; 41 | }; 42 | 43 | HRESULT STDMETHODCALLTYPE PropertyChangedEventHandler::HandlePropertyChangedEvent(IUIAutomationElement* pSender, PROPERTYID propertyID, VARIANT newValue) 44 | { 45 | eventCount_++; 46 | switch (propertyID) 47 | { 48 | case UIA_CenterPointPropertyId: 49 | case UIA_BoundingRectanglePropertyId: 50 | case UIA_IsOffscreenPropertyId: { 51 | Context *pCtx = reinterpret_cast(GetClassLongPtr(hMainWnd_, 0)); 52 | //pCtx->SetCacheExpiredState(true); 53 | break; 54 | } 55 | 56 | default: 57 | break; 58 | } 59 | 60 | return S_OK; 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /KeyMouse/UIAHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Defines an event handler for structure-changed events, and 3 | // listens for them on the element specifies by the user. 4 | #include "stdafx.h" 5 | namespace KeyMouse { 6 | class EventHandler : 7 | public IUIAutomationStructureChangedEventHandler 8 | { 9 | private: 10 | LONG refConut_; 11 | 12 | public: 13 | int eventCount_; 14 | HWND hMainWnd_; 15 | 16 | // Constructor. 17 | EventHandler(HWND hMainWnd) : 18 | refConut_(0), 19 | eventCount_(0), 20 | hMainWnd_(hMainWnd) 21 | { 22 | } 23 | 24 | // IUnknown methods. 25 | ULONG STDMETHODCALLTYPE AddRef() 26 | { 27 | ULONG ret = InterlockedIncrement(&refConut_); 28 | return ret; 29 | } 30 | 31 | ULONG STDMETHODCALLTYPE Release() 32 | { 33 | ULONG ret = InterlockedDecrement(&refConut_); 34 | if (ret == 0) 35 | { 36 | delete this; 37 | return 0; 38 | } 39 | return ret; 40 | } 41 | 42 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface) 43 | { 44 | if (riid == __uuidof(IUnknown)) 45 | *ppInterface = static_cast(this); 46 | else if (riid == __uuidof(IUIAutomationStructureChangedEventHandler)) 47 | *ppInterface = static_cast(this); 48 | else 49 | { 50 | *ppInterface = NULL; 51 | return E_NOINTERFACE; 52 | } 53 | this->AddRef(); 54 | return S_OK; 55 | } 56 | 57 | // IUIAutomationStructureChangedEventHandler methods 58 | HRESULT STDMETHODCALLTYPE HandleStructureChangedEvent(IUIAutomationElement* pSender, StructureChangeType changeType, SAFEARRAY* pRuntimeID); 59 | }; 60 | class CacheEventHandler : 61 | public IUIAutomationStructureChangedEventHandler 62 | { 63 | private: 64 | LONG refConut_; 65 | 66 | public: 67 | int eventCount_; 68 | HWND hMainWnd_; 69 | HWND hTargetWnd_; 70 | 71 | // Constructor. 72 | CacheEventHandler(HWND hMainWnd, HWND hTargetWnd) : 73 | refConut_(0), 74 | eventCount_(0), 75 | hMainWnd_(hMainWnd), 76 | hTargetWnd_(hTargetWnd) 77 | { 78 | } 79 | 80 | // IUnknown methods. 81 | ULONG STDMETHODCALLTYPE AddRef() 82 | { 83 | ULONG ret = InterlockedIncrement(&refConut_); 84 | return ret; 85 | } 86 | 87 | ULONG STDMETHODCALLTYPE Release() 88 | { 89 | ULONG ret = InterlockedDecrement(&refConut_); 90 | if (ret == 0) 91 | { 92 | delete this; 93 | return 0; 94 | } 95 | return ret; 96 | } 97 | 98 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface) 99 | { 100 | if (riid == __uuidof(IUnknown)) 101 | *ppInterface = static_cast(this); 102 | else if (riid == __uuidof(IUIAutomationStructureChangedEventHandler)) 103 | *ppInterface = static_cast(this); 104 | else 105 | { 106 | *ppInterface = NULL; 107 | return E_NOINTERFACE; 108 | } 109 | this->AddRef(); 110 | return S_OK; 111 | } 112 | 113 | // IUIAutomationStructureChangedEventHandler methods 114 | HRESULT STDMETHODCALLTYPE HandleStructureChangedEvent(IUIAutomationElement* pSender, StructureChangeType changeType, SAFEARRAY* pRuntimeID); 115 | }; 116 | 117 | 118 | class PropertyChangedEventHandler: 119 | public IUIAutomationPropertyChangedEventHandler 120 | { 121 | private: 122 | LONG refCount_; 123 | 124 | public: 125 | int eventCount_; 126 | HWND hMainWnd_; 127 | 128 | //Constructor. 129 | PropertyChangedEventHandler(HWND hMainWnd): refCount_(0), eventCount_(0), hMainWnd_(hMainWnd) 130 | { 131 | } 132 | 133 | //IUnknown methods. 134 | ULONG STDMETHODCALLTYPE AddRef() 135 | { 136 | ULONG ret = InterlockedIncrement(&refCount_); 137 | return ret; 138 | } 139 | 140 | ULONG STDMETHODCALLTYPE Release() 141 | { 142 | ULONG ret = InterlockedDecrement(&refCount_); 143 | if (ret == 0) 144 | { 145 | delete this; 146 | return 0; 147 | } 148 | return ret; 149 | } 150 | 151 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface) 152 | { 153 | if (riid == __uuidof(IUnknown)) 154 | *ppInterface=static_cast(this); 155 | else if (riid == __uuidof(IUIAutomationPropertyChangedEventHandler)) 156 | *ppInterface=static_cast(this); 157 | else 158 | { 159 | *ppInterface = NULL; 160 | return E_NOINTERFACE; 161 | } 162 | this->AddRef(); 163 | return S_OK; 164 | } 165 | 166 | // IUIAutomationPropertyChangedEventHandler methods. 167 | HRESULT STDMETHODCALLTYPE HandlePropertyChangedEvent(IUIAutomationElement* pSender, PROPERTYID propertyID, VARIANT newValue); 168 | }; 169 | } 170 | -------------------------------------------------------------------------------- /KeyMouse/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "profile": 3 | { 4 | "runOnStartUp": true, 5 | "backgroundColor": "#CCFFCC", 6 | "fontColor": "#000000", 7 | "fontSize": 9, 8 | "font": "Arial Rounded MT Bold", 9 | "windowBkgdColor": "#CCBBAA", 10 | "windowFontSize": 11, 11 | "opacity": 70, 12 | "invertClickType": false, 13 | "onlyForeWindow": true, 14 | "enableWindowSwitching": true, 15 | "enableCache": true 16 | }, 17 | "keybindings": 18 | { 19 | "toggleEnable": "alt+[", 20 | "scrollUp": "alt+k", 21 | "scrollDown": "alt+j", 22 | "selectMode": "alt+;", 23 | "escape": "esc", 24 | "fastSelectMode": "alt+i", 25 | "rightClickPrefix": "shift+a", 26 | "singleClickPrefix": "shift+s" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /KeyMouse/ctx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "stdafx.h" 3 | #include "ctx.h" 4 | 5 | #include "hotkey_handler.h" 6 | #include "utils.h" 7 | namespace KeyMouse { 8 | const int MAX_PATH_LEN = 200; 9 | // split std::string by delim. 10 | template 11 | void split(const std::string &s, char delim, Out result) { 12 | std::istringstream iss(s); 13 | std::string item; 14 | while (std::getline(iss, item, delim)) { 15 | *result++ = item; 16 | } 17 | } 18 | 19 | std::vector split(const std::string &s, char delim) { 20 | std::vector elems; 21 | split(s, delim, std::back_inserter(elems)); 22 | if (elems.empty()) { 23 | elems.push_back(std::string("")); 24 | } 25 | return elems; 26 | } 27 | 28 | std::wstring Str2Wstr(std::string& src) { 29 | size_t size = src.size() + 1; 30 | std::wstring temp(size, L' '); 31 | size_t out_size; 32 | mbstowcs_s(&out_size , &temp[0], size, src.c_str(), size - 1); 33 | temp.resize(out_size - 1); 34 | return temp; 35 | } 36 | 37 | COLORREF Str2RGB(std::string color) { 38 | COLORREF BGR = 0; 39 | COLORREF RGB = 0; 40 | color.erase(color.begin()); 41 | 42 | if (!std::all_of(color.begin(), color.end(), 43 | [](unsigned char c) {return std::isxdigit(c); }) || 44 | color.length() > 6) { 45 | return 0; 46 | } 47 | BGR = std::stol(color, nullptr, 16); 48 | long xxx = (BGR & 0xFF) << 4; 49 | RGB = (BGR & 0xFF) << 16 | (BGR & 0xFF00) | (BGR & 0xFF0000) >> 16; 50 | return RGB; 51 | } 52 | 53 | std::map Config::lo_map_ = { 54 | {"alt", MOD_ALT}, 55 | {"ctrl", MOD_CONTROL}, 56 | {"norepeat", MOD_NOREPEAT}, 57 | {"shift", MOD_SHIFT}, 58 | {"win", MOD_WIN} 59 | }; 60 | 61 | std::map Config::hi_map_ = { 62 | {"tab", VK_TAB}, 63 | {"enter", VK_RETURN}, 64 | {"esc", VK_ESCAPE}, 65 | {"space", VK_SPACE}, 66 | {"end", VK_END}, 67 | {"home", VK_HOME}, 68 | {"left", VK_LEFT}, 69 | {"right", VK_RIGHT}, 70 | {"up", VK_UP}, 71 | {"down", VK_DOWN}, 72 | {"insert", VK_INSERT}, 73 | {"0", 0x30}, 74 | {"1", 0x31}, 75 | {"2", 0x32}, 76 | {"3", 0x33}, 77 | {"4", 0x34}, 78 | {"5", 0x35}, 79 | {"6", 0x36}, 80 | {"7", 0x37}, 81 | {"8", 0x38}, 82 | {"9", 0x39}, 83 | {"a", 0x41}, 84 | {"b", 0x42}, 85 | {"c", 0x43}, 86 | {"d", 0x44}, 87 | {"e", 0x45}, 88 | {"f", 0x46}, 89 | {"g", 0x47}, 90 | {"h", 0x48}, 91 | {"i", 0x49}, 92 | {"j", 0x4A}, 93 | {"k", 0x4B}, 94 | {"l", 0x4C}, 95 | {"m", 0x4D}, 96 | {"n", 0x4E}, 97 | {"o", 0x4F}, 98 | {"p", 0x50}, 99 | {"q", 0x51}, 100 | {"r", 0x52}, 101 | {"s", 0x53}, 102 | {"t", 0x54}, 103 | {"u", 0x55}, 104 | {"v", 0x56}, 105 | {"w", 0x57}, 106 | {"x", 0x58}, 107 | {"y", 0x59}, 108 | {"z", 0x5A}, 109 | {"f1", VK_F1}, 110 | {"f2", VK_F2}, 111 | {"f3", VK_F3}, 112 | {"f4", VK_F4}, 113 | {"f5", VK_F5}, 114 | {"f6", VK_F6}, 115 | {"f7", VK_F7}, 116 | {"f8", VK_F8}, 117 | {"f9", VK_F9}, 118 | {"f10", VK_F10}, 119 | {"f11", VK_F11}, 120 | {"f12", VK_F12}, 121 | {";", VK_OEM_1}, 122 | {"=", VK_OEM_PLUS}, 123 | {",", VK_OEM_COMMA}, 124 | {"-", VK_OEM_MINUS}, 125 | {".", VK_OEM_PERIOD}, 126 | {"/", VK_OEM_2}, 127 | {"`", VK_OEM_3}, 128 | {"[", VK_OEM_4}, 129 | {"\\", VK_OEM_5}, 130 | {"]", VK_OEM_6}, 131 | {"'", VK_OEM_7} 132 | }; 133 | std::map Config::command_id_map_{ 134 | {"toggleEnable", TOGGLEENABLE}, 135 | {"scrollUp", SCROLLUP}, 136 | {"scrollDown", SCROLLDOWN}, 137 | {"selectMode", SHOWTAG}, 138 | {"escape", CLEANTAG}, 139 | {"fastSelectMode", FASTSELECTMODE}, 140 | {"rightClickPrefix", RIGHTCLICKPREFIX}, 141 | {"singleClickPrefix", SINGLELEFTCLICKPREFIX}, 142 | {"forceNotUseCache", FORCENOTUSECACHE}, 143 | {"selectModeSingle", SELECTMODESINGLE} 144 | }; 145 | Config::Config() { 146 | 147 | } 148 | 149 | Config::Config(const std::wstring& json_name) { 150 | has_error_ = false; 151 | default_json_ = json({ 152 | {"profile", { 153 | {"runOnStartUp", true}, 154 | {"fontColor", "#000000"}, 155 | {"fontSize", 10}, 156 | {"font", "Arial Rounded MT Bold"}, 157 | {"backgroundColor", "#CCFFCC"}, 158 | {"windowFontColor", "#000000"}, 159 | {"windowFontSize", 12}, 160 | {"windowFont", "Arial Rounded MT Bold"}, 161 | {"windowBkgdColor", "#CCBBAA"}, 162 | {"opacity", 100}, 163 | {"invertClickType", false}, 164 | {"onlyForeWindow", true}, 165 | {"enableWindowSwitching", true}, 166 | {"enableCache", false} 167 | }}, 168 | {"keybindings", { 169 | {"toggleEnable", "alt+["}, 170 | {"scrollUp", "alt+k"}, 171 | {"scrollDown", "alt+j"}, 172 | {"selectMode", "alt+;"}, 173 | {"escape", "esc"}, 174 | {"fastSelectMode", "alt+j"}, 175 | {"rightClickPrefix", "shift+a"}, 176 | {"singleClickPrefix", "shift+s"}, 177 | {"forceNotUseCache", "space"}, 178 | {"selectModeSingle", "disabled"} 179 | }} 180 | }); 181 | if (!LoadJson(json_name)) { 182 | // set defalut configuration. 183 | config_json_ = default_json_; 184 | 185 | } 186 | else { 187 | // this step guarantees the left side are valid options, but not 188 | // guarantees valid option values on the right. 189 | PatchCustomJson_(); 190 | } 191 | 192 | } 193 | 194 | Config::~Config() { 195 | 196 | } 197 | 198 | bool Config::LoadJson(const std::wstring& json_name) { 199 | std::fstream json_file(json_name, std::ios::in); 200 | 201 | if (!json_file.is_open()) { 202 | return false; 203 | } 204 | try { 205 | json_file >> config_json_; 206 | } 207 | catch (json::exception& e) { 208 | // output exception information 209 | cout << "message: " << e.what() << '\n' 210 | << "exception id: " << e.id << std::endl; 211 | return false; 212 | 213 | } 214 | json_file.close(); 215 | return true; 216 | } 217 | 218 | bool Config::WriteJson(const std::wstring& json_name) { 219 | std::fstream json_file(json_name, std::ios::out); 220 | 221 | if (!json_file.is_open()) { 222 | return false; 223 | } 224 | try { 225 | json_file << config_json_; 226 | } 227 | catch (json::exception& e) { 228 | // output exception information 229 | cout << "message: " << e.what() << '\n' 230 | << "exception id: " << e.id << std::endl; 231 | return false; 232 | 233 | } 234 | json_file.close(); 235 | return true; 236 | } 237 | 238 | // patch the custom configure json with default json. 239 | void Config::PatchCustomJson_() { 240 | 241 | json diff = json::diff(config_json_, default_json_); 242 | json::iterator it = diff.begin(); 243 | while (it != diff.end()) { 244 | // don't replace user's custom option. 245 | if ((*it)["op"] == "replace") { 246 | it = diff.erase(it); 247 | } 248 | else { 249 | if ((*it)["op"] == "remove") { 250 | if (!has_error_) { // only warn user one time. 251 | has_error_ = true; 252 | std::wstring temp = Str2Wstr((*it)["path"].get()); 253 | std::wstring output_str = TEXT("Unrecognized option: ") + 254 | temp + TEXT(". Please check your configuration."); 255 | MessageBox(nullptr, output_str.c_str(), TEXT("Warning"), MB_OK); 256 | } 257 | } 258 | ++it; 259 | } 260 | 261 | } 262 | config_json_ = config_json_.patch(diff); 263 | 264 | } 265 | LPARAM Config::ExtractSingleKeyBinding_(std::string key, std::string binding) { 266 | // remove all space in binding. 267 | binding.erase(std::remove_if(binding.begin(), binding.end(), ::isspace), binding.end()); 268 | 269 | std::vector split_keys = split(binding, '+'); 270 | WORD lo = 0; 271 | WORD hi = 0; 272 | if (binding == "disabled") 273 | return MAKELPARAM(lo, hi); 274 | 275 | for (auto& key_str : split_keys) { 276 | auto search = lo_map_.find(key_str); 277 | if (search != lo_map_.end()) { 278 | lo = lo | lo_map_[key_str]; 279 | } 280 | else if (hi_map_.find(key_str) != hi_map_.end()) { 281 | hi = hi | hi_map_[key_str]; 282 | } 283 | else { 284 | if (!has_error_) { // only warn user one time. 285 | has_error_ = true; 286 | std::wstring temp = Str2Wstr(binding); 287 | std::wstring output_str = TEXT("Unrecognized key binding: ") + 288 | temp + TEXT(". Please check your configuration."); 289 | MessageBox(nullptr, output_str.c_str(), TEXT("Warning"), MB_OK); 290 | std::string defalut_binding = default_json_["keybindings"][key].get(); 291 | return ExtractSingleKeyBinding_(key, defalut_binding); 292 | } 293 | } 294 | } 295 | 296 | return MAKELPARAM(lo, hi); 297 | 298 | } 299 | 300 | KeybindingMap Config::ExtractKeyBinding() { 301 | json keybinding_json = config_json_["keybindings"]; 302 | KeybindingMap keybinding_map; 303 | 304 | for (auto& keybinding : keybinding_json.items()) { 305 | std::string str = keybinding.value(); 306 | LPARAM lp = ExtractSingleKeyBinding_(keybinding.key(), str); 307 | 308 | auto key = keybinding.key(); 309 | if (command_id_map_.find(key) != command_id_map_.end()) { 310 | if (str == "disabled") { 311 | IdlParam id_lParam = { DISABLED, lp }; 312 | keybinding_map.insert( 313 | std::pair(keybinding.key(), id_lParam) 314 | ); 315 | } 316 | IdlParam id_lParam = { command_id_map_[key], lp}; 317 | keybinding_map.insert( 318 | std::pair(keybinding.key(), id_lParam) 319 | ); 320 | } else { 321 | if (!has_error_) { // only warn user one time. 322 | has_error_ = true; 323 | std::wstring temp = Str2Wstr(key); 324 | std::wstring output_str = TEXT("Unrecognized key binding command: ") + 325 | temp + TEXT(". Please check your configuration."); 326 | MessageBox(nullptr, output_str.c_str(), TEXT("Warning"), MB_OK); 327 | } 328 | } 329 | 330 | } 331 | return keybinding_map; 332 | 333 | } 334 | 335 | Profile Config::ExtractProfile() { 336 | json profile_json = config_json_["profile"]; 337 | Profile profile; 338 | profile.run_startup = profile_json["runOnStartUp"].get(); 339 | profile.font.background_color = Str2RGB(profile_json["backgroundColor"].get()); 340 | profile.font.font_name = Str2Wstr(profile_json["font"].get()); 341 | profile.font.font_size = profile_json["fontSize"].get(); 342 | profile.font.font_color = Str2RGB(profile_json["fontColor"].get()); 343 | profile.window_tag_font.background_color = Str2RGB(profile_json["windowBkgdColor"].get()); 344 | profile.window_tag_font.font_name = Str2Wstr(profile_json["windowFont"].get()); 345 | profile.window_tag_font.font_size = profile_json["windowFontSize"].get(); 346 | profile.window_tag_font.font_color = Str2RGB(profile_json["windowFontColor"].get()); 347 | profile.opacity = profile_json["opacity"].get(); 348 | profile.invert_click_type = profile_json["invertClickType"].get(); 349 | profile.only_forewindow = profile_json["onlyForeWindow"].get(); 350 | profile.enable_window_switching = profile_json["enableWindowSwitching"].get(); 351 | profile.enable_cache = profile_json["enableCache"].get(); 352 | 353 | return profile; 354 | } 355 | 356 | //------------------------------------------------------------------------ 357 | Context::Context() : 358 | wndProc_handler_(WndProcHandler()), 359 | current_tag_(string(TEXT(""))), 360 | tag_map_(PTagMap()), 361 | enable_state_(true), 362 | on_fast_select_mode_(false), 363 | mode_(NORMAL_MODE), 364 | is_cache_expired_(false), 365 | cache_expired_count_(0), 366 | cache_expired_struct_changed_(false), 367 | last_input_tick_(0), 368 | last_cache_tick_(0), 369 | cache_window_mtx_(), 370 | cache_window_cv_(), 371 | doing_caching_(false) { 372 | 373 | app_directory_ = AppDir(); 374 | SetCurrentDirectory(app_directory_.c_str()); 375 | 376 | json_name_ = std::wstring(L"./config.json"); 377 | config_ = Config(json_name_); 378 | keybinding_map_ = config_.ExtractKeyBinding(); 379 | profile_ = config_.ExtractProfile(); 380 | ApplyProfile_(); 381 | 382 | // initalize uiautomation. 383 | CoCreateInstance(CLSID_CUIAutomation, nullptr, 384 | CLSCTX_INPROC_SERVER, IID_IUIAutomation, 385 | reinterpret_cast(&automation_)); 386 | 387 | event_handlers_.reset(new std::map>>); 388 | wndProc_handler_.InitialHKBinding(keybinding_map_); 389 | cache_window_.reset(new std::map>); 390 | if (profile_.invert_click_type) { 391 | click_type_ = RIGHT_CLICK; 392 | } 393 | else { 394 | click_type_ = LEFT_CLICK; 395 | } 396 | } 397 | 398 | Context::~Context() { 399 | } 400 | 401 | void Context::ApplyProfile_() { 402 | if (profile_.run_startup) { 403 | WriteRegistryRUN_(); 404 | } 405 | else { 406 | DeleteRegistryRUN_(); 407 | } 408 | } 409 | std::wstring Context::AppDir() { 410 | TCHAR buffer[MAX_PATH_LEN]; 411 | GetModuleFileName( NULL, buffer, MAX_PATH_LEN); 412 | std::wstring::size_type pos = std::wstring(buffer).find_last_of( L"\\/" ); 413 | return std::wstring(buffer).substr(0, pos); 414 | } 415 | bool Context::WriteRegistryRUN_() 416 | { 417 | HKEY hKey; 418 | TCHAR pPath [MAX_PATH_LEN]; 419 | 420 | GetModuleFileName(0, pPath, MAX_PATH_LEN); 421 | 422 | RegOpenKeyExA (HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_ALL_ACCESS, &hKey); 423 | if (RegSetValueEx(hKey, L"KeyMouse", 0, REG_SZ, (BYTE*)pPath, MAX_PATH_LEN)) { 424 | return false; 425 | } 426 | 427 | 428 | RegCloseKey(hKey); 429 | return true; 430 | } 431 | 432 | bool Context::DeleteRegistryRUN_() 433 | { 434 | HKEY hKey; 435 | 436 | RegOpenKeyExA (HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_ALL_ACCESS, &hKey); 437 | if (RegDeleteValue(hKey, L"KeyMouse")) { 438 | return false; 439 | } 440 | 441 | RegCloseKey(hKey); 442 | return true; 443 | } 444 | 445 | void Context::InitTimer(HWND hwnd) { 446 | timer_.reset(new TimedExecution(CacheThread, std::chrono::milliseconds(1), hwnd)); 447 | } 448 | 449 | void Context::WaitForCacheWindow() { 450 | std::unique_lock lck(cache_window_mtx_); 451 | cache_window_cv_.wait(lck, [this] { return !doing_caching_; }); 452 | } 453 | void Context::LockCacheWindow() { 454 | doing_caching_ = true; 455 | cache_window_mtx_.lock(); 456 | } 457 | 458 | void Context::UnlockCacheWindow() { 459 | cache_window_mtx_.unlock(); 460 | doing_caching_ = false; 461 | cache_window_cv_.notify_one(); 462 | } 463 | 464 | const WndProcHandler& Context::GetWndProcHandler() const { 465 | return wndProc_handler_; 466 | } 467 | 468 | const KeybindingMap& Context::GetKeybindingMap() const { 469 | return keybinding_map_; 470 | } 471 | 472 | const Profile& Context::GetProfile() const { 473 | return profile_; 474 | } 475 | 476 | IUIAutomation* Context::GetAutomation() const { 477 | return automation_; 478 | } 479 | 480 | void Context::SetEventHandlers(PEventHandlers& event_handlers) { 481 | event_handlers_ = std::move(event_handlers); 482 | } 483 | 484 | PEventHandlers Context::GetEventHandlers() const { 485 | return event_handlers_; 486 | } 487 | 488 | void Context::SetStructEventHandler(CComPtr &event_handler) { 489 | pStruct_event_handler_ = event_handler; 490 | } 491 | CComPtr Context::GetStructEventHandler() const { 492 | return pStruct_event_handler_; 493 | } 494 | 495 | void Context::SetElement(CComPtr& pElement) { 496 | pElement_ = pElement; 497 | } 498 | const CComPtr& Context::GetElement() const { 499 | return pElement_; 500 | } 501 | 502 | void Context::SetPrevProcessName(const std::string &name) { 503 | prev_process_name_ = name; 504 | } 505 | const std::string &Context::GetPrevProcessName() const { 506 | return prev_process_name_; 507 | } 508 | 509 | void Context::SetCurrentTag(const string &tag) { 510 | current_tag_ = tag; 511 | } 512 | 513 | const string &Context::GetCurrentTag() const { 514 | return current_tag_; 515 | } 516 | 517 | void Context::SetMaxTagLen(const size_t len) { 518 | max_tag_len_ = len; 519 | } 520 | 521 | const size_t &Context::GetMaxTagLen() const { 522 | return max_tag_len_; 523 | } 524 | 525 | void Context::SetTagMap(PTagMap& map) { 526 | tag_map_ = std::move(map); 527 | } 528 | 529 | void Context::ClearTagMap() { 530 | if (tag_map_ != nullptr) { 531 | tag_map_->clear(); 532 | } 533 | } 534 | 535 | void Context::MergeTagMap(PTagMap& src_map) { 536 | if (tag_map_ == nullptr) { 537 | tag_map_ = std::move(src_map); 538 | } 539 | else { 540 | tag_map_->insert(src_map->begin(), src_map->end()); 541 | } 542 | } 543 | 544 | const PTagMap& Context::GetTagMap() const { 545 | return tag_map_; 546 | } 547 | 548 | void Context::SetWindowMap(PTagMap& map) { 549 | window_map_ = std::move(map); 550 | } 551 | 552 | const PTagMap& Context::GetWindowMap() const { 553 | return window_map_; 554 | } 555 | 556 | void Context::SetEnableState(const bool flag) { 557 | enable_state_ = flag; 558 | } 559 | 560 | const bool &Context::GetEnableState() const { 561 | return enable_state_; 562 | } 563 | 564 | void Context::SetEnableCache(const bool stat) { 565 | profile_.enable_cache = stat; 566 | } 567 | const bool &Context::GetEnableCache() const { 568 | return profile_.enable_cache; 569 | } 570 | 571 | void Context::SetFastSelectState(const bool flag) { 572 | on_fast_select_mode_ = flag; 573 | } 574 | 575 | const bool& Context::GetFastSelectState() const { 576 | return on_fast_select_mode_; 577 | } 578 | 579 | void Context::SetClickType(const ClickType type) { 580 | click_type_ = type; 581 | } 582 | 583 | Context::ClickType Context::GetClickType() const { 584 | return click_type_; 585 | } 586 | 587 | void Context::SetTransWindow(const HWND hWnd) { 588 | transparent_window_ = hWnd; 589 | } 590 | 591 | const HWND &Context::GetTransWindow() const { 592 | return transparent_window_; 593 | } 594 | void Context::SetForeWindow(const HWND hWnd) { 595 | fore_window_ = hWnd; 596 | } 597 | 598 | const HWND &Context::GetForeWindow() const { 599 | return fore_window_; 600 | } 601 | void Context::SetMode(const Mode mode) { 602 | mode_ = mode; 603 | } 604 | 605 | const Context::Mode &Context::GetMode() const { 606 | return mode_; 607 | } 608 | 609 | void Context::SetCacheExpiredState(const bool stat) { 610 | if (!stat) { 611 | cache_expired_count_ = 0; 612 | last_input_tick_ = 0; 613 | } 614 | else { 615 | cache_expired_count_++; 616 | last_input_tick_ = GetTickCount(); 617 | } 618 | is_cache_expired_ = stat; 619 | } 620 | 621 | const bool &Context::GetCacheExpiredState() const { 622 | return is_cache_expired_; 623 | } 624 | 625 | void Context::SetCacheExpiredStructChanged(const bool stat) { 626 | cache_expired_struct_changed_ = stat; 627 | } 628 | 629 | const bool &Context::GetCacheExpiredStructChanged() const { 630 | return cache_expired_struct_changed_; 631 | } 632 | 633 | void Context::SetLastInputTick(const DWORD interval) { 634 | last_input_tick_ = interval; 635 | } 636 | 637 | const DWORD &Context::GetLastInputTick() const { 638 | return last_input_tick_; 639 | } 640 | 641 | void Context::SetLastcacheTick(const DWORD time) { 642 | last_cache_tick_ = time; 643 | } 644 | 645 | const DWORD &Context::GetLastCacheTick() const { 646 | return last_cache_tick_; 647 | } 648 | 649 | void Context::SetCacheWindow(PCacheWindow& cache_window) { 650 | cache_window_ = std::move(cache_window); 651 | } 652 | 653 | const PCacheWindow& Context::GetCacheWindow() const { 654 | return cache_window_; 655 | } 656 | const bool &Context::GetDoingCachingState() const { 657 | return doing_caching_; 658 | } 659 | void Context::SetDoingCachingState(const bool state) { 660 | doing_caching_ = state; 661 | } 662 | } 663 | -------------------------------------------------------------------------------- /KeyMouse/ctx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "stdafx.h" 4 | #include "def.h" 5 | #include "UIAHandler.h" 6 | 7 | #include "json/single_include/nlohmann/json.hpp" 8 | 9 | 10 | // user defined message. 11 | #define WM_TRAY WM_USER + 1 12 | 13 | 14 | using json = nlohmann::json; 15 | 16 | namespace KeyMouse { 17 | struct ElementInfo { 18 | UIA_HWND handle; 19 | RECT rect; 20 | CONTROLTYPEID control_type; 21 | }; 22 | struct IdlParam{ 23 | // id is used for RegisterHotKey. defined in command_id_map_. 24 | int id; 25 | LPARAM lParam; 26 | }; 27 | using KeybindingMap = std::map; 28 | 29 | typedef struct WindowsEventArguments { 30 | HWND hWnd; 31 | WPARAM wParam; 32 | LPARAM lParam; 33 | HINSTANCE hInst; 34 | }WndEventArgs, *lpWndEventArgs; 35 | 36 | 37 | struct EVENTHANDLER { 38 | unsigned int Code; 39 | LRESULT(*fnPtr)(const WndEventArgs&); 40 | }; 41 | 42 | struct HKBinding { 43 | LPARAM lParam; 44 | LRESULT(*fnPtr)(const WndEventArgs&); 45 | }; 46 | 47 | /** 48 | * @brief: handle all window messages. 49 | * 50 | */ 51 | class WndProcHandler 52 | { 53 | public: 54 | static constexpr int EVENTHANDLER_NUM = 5; 55 | static constexpr int SELECT_HKBINDING_NUM = 4; 56 | static constexpr int NORMAL_HKBINDING_NUM = 6; 57 | 58 | WndProcHandler(); 59 | ~WndProcHandler(); 60 | void InitialHKBinding(KeybindingMap& keybinding_map); 61 | LRESULT HandlerEntrance(UINT msg, const WndEventArgs& Wea); 62 | static LRESULT fnWndProc_Command_(const WndEventArgs& Wea); 63 | static LRESULT fnWndProc_Tray_(const WndEventArgs& Wea); 64 | static LRESULT fnWndProc_Hotkey_(const WndEventArgs& Wea); 65 | static LRESULT fnWndProc_Paint_(const WndEventArgs& Wea); 66 | static LRESULT fnWndProc_Destroy_(const WndEventArgs& Wea); 67 | static LRESULT fnHKProc_SelectMode_(const WndEventArgs& Wea); 68 | static LRESULT fnHKProc_SelectModeSingle_(const WndEventArgs& Wea); 69 | static LRESULT fnHKProc_FastSelectMode_(const WndEventArgs& Wea); 70 | static LRESULT fnHKProc_RightClickPrefix_(const WndEventArgs& Wea); 71 | static LRESULT fnHKProc_SingleClickPrefix_(const WndEventArgs& Wea); 72 | static LRESULT fnHKProc_Escape_(const WndEventArgs& Wea); 73 | static LRESULT fnHKProc_ToggleEnable_(const WndEventArgs& Wea); 74 | static LRESULT fnHKProc_Scroll_Up_(const WndEventArgs& Wea); 75 | static LRESULT fnHKProc_Scroll_Down_(const WndEventArgs& Wea); 76 | static LRESULT fnHKProc_ForceNotUseCache_(const WndEventArgs& Wea); 77 | 78 | static void EscSelectMode_(HWND hWnd); 79 | static void SelectModeHandler_(HWND hWnd, WORD VirtualKey); 80 | static void ScrollHandler_(HWND hWnd, bool Up); 81 | static void LeftClick_(int x, int y, int time); 82 | static void RightClick_(int x, int y, int time); 83 | static void InvokeElement_(KeyMouse::ElementInfo &pElement, HWND hWnd); 84 | static void EditInputForward_(HWND hWnd, WORD VirtualKey); 85 | static bool CompareBlackList_(); 86 | static INT_PTR CALLBACK About_(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); 87 | 88 | private: 89 | EVENTHANDLER event_handler_[EVENTHANDLER_NUM]; 90 | static HKBinding select_hkbinding_[SELECT_HKBINDING_NUM]; 91 | static HKBinding normal_hkbinding_[NORMAL_HKBINDING_NUM]; 92 | 93 | }; 94 | 95 | // a snippet from https://stackoverflow.com/questions/25104305/how-to-make-a-function-execute-at-the-desired-periods-using-c-11 96 | struct TimedExecution { 97 | typedef void (*func_type)(HWND); 98 | TimedExecution() {}; 99 | TimedExecution (func_type func, const std::chrono::milliseconds period, HWND hwnd) 100 | : func_(func) 101 | , period_(period) 102 | , thread_(std::bind(&TimedExecution::threadFunc,this)) 103 | , hwnd_(hwnd) 104 | , kill_(false) 105 | { 106 | } 107 | ~TimedExecution() { 108 | kill_ = true; 109 | thread_.join(); 110 | }; 111 | private: 112 | void threadFunc() { 113 | HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); 114 | while(true) { 115 | std::this_thread::sleep_for(period_); 116 | func_(hwnd_); 117 | if (kill_) { 118 | CoUninitialize(); 119 | break; 120 | } 121 | } 122 | } 123 | func_type func_; 124 | std::chrono::milliseconds period_; 125 | std::thread thread_; 126 | HWND hwnd_; 127 | bool kill_; 128 | }; 129 | 130 | 131 | using PTagMap = std::shared_ptr>; 132 | using PElementVec = std::shared_ptr>; 133 | using PCacheWindow = std::unique_ptr>>; 134 | using PEventHandlers = std::shared_ptr>>>; 135 | using KeybindingMap = std::map; 136 | 137 | struct Font { 138 | std::wstring font_name; 139 | COLORREF font_color; 140 | COLORREF background_color; 141 | int font_size; 142 | }; 143 | struct Profile { 144 | bool run_startup; 145 | Font font; 146 | Font window_tag_font; 147 | int opacity; 148 | bool invert_click_type; 149 | bool only_forewindow; 150 | bool enable_window_switching; 151 | bool enable_cache; 152 | }; 153 | 154 | /** 155 | * @brief: extract configuration from json format file. 156 | */ 157 | class Config { 158 | public: 159 | Config(); 160 | Config(const std::wstring& json_name); 161 | ~Config(); 162 | bool LoadJson(const std::wstring& json_name); 163 | bool WriteJson(const std::wstring& json_name); 164 | // fill the custom json with default json. 165 | void PatchCustomJson_(); 166 | // extract keybinding map from json. 167 | LPARAM ExtractSingleKeyBinding_(std::string key, std::string binding); 168 | KeybindingMap ExtractKeyBinding(); 169 | Profile ExtractProfile(); 170 | 171 | 172 | private: 173 | static std::map lo_map_; 174 | static std::map hi_map_; 175 | static std::map command_id_map_; 176 | bool has_error_; 177 | json default_json_; 178 | json config_json_; 179 | 180 | }; 181 | 182 | /** 183 | * @brief: Context will include most of global context of this app. 184 | */ 185 | class Context 186 | { 187 | public: 188 | enum Mode { 189 | NORMAL_MODE, 190 | SELECT_MODE, 191 | INSERT_MODE 192 | }; 193 | enum ClickType { 194 | RIGHT_CLICK, 195 | LEFT_CLICK, 196 | SINGLE_RIGHT_CLICK, 197 | SINGLE_LEFT_CLICK 198 | }; 199 | Context (); 200 | ~Context (); 201 | std::wstring AppDir(); 202 | bool WriteRegistryRUN_(); 203 | bool DeleteRegistryRUN_(); 204 | void ApplyProfile_(); 205 | void InitTimer(HWND hwnd); 206 | void WaitForCacheWindow(); 207 | void LockCacheWindow(); 208 | void UnlockCacheWindow(); 209 | const WndProcHandler& GetWndProcHandler () const; 210 | const KeybindingMap& GetKeybindingMap () const; 211 | const Profile& GetProfile() const; 212 | 213 | IUIAutomation* GetAutomation() const; 214 | void SetEventHandlers(PEventHandlers& event_handlers); 215 | PEventHandlers GetEventHandlers() const; 216 | void SetStructEventHandler(CComPtr& event_handler); 217 | CComPtr GetStructEventHandler() const; 218 | void SetElement(CComPtr& pElement); 219 | const CComPtr& GetElement() const; 220 | void SetPrevProcessName(const std::string &tag); 221 | const std::string &GetPrevProcessName() const; 222 | 223 | void SetCurrentTag(const string &tag); 224 | const string &GetCurrentTag() const; 225 | void SetMaxTagLen(const size_t len); 226 | const size_t &GetMaxTagLen() const; 227 | void SetTagMap(PTagMap& map); 228 | void ClearTagMap(); 229 | void MergeTagMap(PTagMap& src_map); 230 | const PTagMap& GetTagMap() const; 231 | void SetWindowMap(PTagMap& map); 232 | const PTagMap& GetWindowMap() const; 233 | void SetEnableState(const bool flag); 234 | const bool &GetEnableState() const; 235 | void SetEnableCache(const bool stat); 236 | const bool &GetEnableCache() const; 237 | void SetFastSelectState(const bool flag); 238 | const bool &GetFastSelectState() const; 239 | void SetClickType(const ClickType type); 240 | ClickType GetClickType() const; 241 | void SetTransWindow(const HWND hWnd); 242 | const HWND &GetTransWindow() const; 243 | void SetForeWindow(const HWND hWnd); 244 | const HWND &GetForeWindow() const; 245 | void SetMode(const Mode mode); 246 | const Mode &GetMode() const; 247 | void SetCacheExpiredState(const bool stat); 248 | const bool &GetCacheExpiredState() const; 249 | void SetCacheExpiredStructChanged(const bool stat); 250 | const bool &GetCacheExpiredStructChanged() const; 251 | void SetLastInputTick(const DWORD interval); 252 | const DWORD &GetLastInputTick() const; 253 | void SetLastcacheTick(const DWORD time); 254 | const DWORD &GetLastCacheTick() const; 255 | void SetCacheWindow(PCacheWindow& cache_window); 256 | const PCacheWindow& GetCacheWindow() const; 257 | const bool &GetDoingCachingState() const; 258 | void SetDoingCachingState(const bool state); 259 | 260 | private: 261 | std::wstring app_directory_; 262 | // the configure file name. 263 | std::wstring json_name_; 264 | Config config_; 265 | // the (command: IdlParam)map of hotkey binding. 266 | KeybindingMap keybinding_map_; 267 | Profile profile_; 268 | 269 | IUIAutomation* automation_; 270 | // window process handler which will handle all message from windows. 271 | WndProcHandler wndProc_handler_; 272 | PEventHandlers event_handlers_; 273 | CComPtr pStruct_event_handler_; 274 | CComPtr pElement_; 275 | std::string prev_process_name_; 276 | 277 | string current_tag_; 278 | size_t max_tag_len_; 279 | // (hints: UIAutomationElement) map. 280 | PTagMap tag_map_; 281 | PTagMap window_map_; 282 | 283 | PCacheWindow cache_window_; 284 | std::mutex cache_window_mtx_; 285 | std::condition_variable cache_window_cv_; 286 | bool doing_caching_; 287 | 288 | 289 | 290 | // current status. 291 | bool enable_state_; 292 | bool on_fast_select_mode_; 293 | ClickType click_type_; 294 | 295 | HWND transparent_window_; 296 | HWND fore_window_; 297 | Mode mode_; 298 | 299 | // cache will expires when keyboard strokes are detected 300 | // and target window's struct and property changing. 301 | bool is_cache_expired_; 302 | int cache_expired_count_; 303 | bool cache_expired_struct_changed_; 304 | 305 | DWORD last_input_tick_; 306 | DWORD last_cache_tick_; 307 | 308 | std::unique_ptr timer_; 309 | }; 310 | } 311 | -------------------------------------------------------------------------------- /KeyMouse/def.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #ifdef UNICODE 5 | typedef std::wstring string; 6 | #define cout std::wcout 7 | #else 8 | typedef std::string string; 9 | #define cout std::cout 10 | #endif 11 | -------------------------------------------------------------------------------- /KeyMouse/hotkey_handler.cpp: -------------------------------------------------------------------------------- 1 | #include"stdafx.h" 2 | #include "hotkey_handler.h" 3 | #include"KeyMouse.h" 4 | #include "utils.h" 5 | namespace KeyMouse { 6 | BOOL RegCustomHotKey(HWND hWnd, std::string key) { 7 | Context *pCtx = GetContext(hWnd); 8 | KeybindingMap keybinding_map = pCtx->GetKeybindingMap(); 9 | int id = keybinding_map[key].id; 10 | // don't register when hotkey is disabled. 11 | if (id == DISABLED) 12 | return TRUE; 13 | UINT fsModifiers = LOWORD(keybinding_map[key].lParam); 14 | UINT vk = HIWORD(keybinding_map[key].lParam); 15 | return RegisterHotKey(hWnd, id, fsModifiers, vk); 16 | 17 | } 18 | BOOL UnregCustomHotKey(HWND hWnd, std::string key) { 19 | Context *pCtx = GetContext(hWnd); 20 | KeybindingMap keybinding_map = pCtx->GetKeybindingMap(); 21 | int id = keybinding_map[key].id; 22 | return UnregisterHotKey(hWnd, id); 23 | } 24 | 25 | BOOL RegisterAllHotKey(HWND hWnd, bool exclude_toggle) { 26 | BOOL flag = TRUE; 27 | if (!exclude_toggle) { 28 | flag = RegCustomHotKey(hWnd, "toggleEnable"); 29 | auto str = GetLastErrorAsString(); 30 | } 31 | 32 | return ( 33 | flag && 34 | RegCustomHotKey(hWnd, "selectMode") && 35 | RegCustomHotKey(hWnd, "scrollUp") && 36 | RegCustomHotKey(hWnd, "scrollDown") && 37 | RegCustomHotKey(hWnd, "fastSelectMode") && 38 | RegCustomHotKey(hWnd, "selectModeSingle") 39 | ); 40 | } 41 | BOOL RegisterTagHotKey(HWND hWnd) { 42 | return ( 43 | // unregister the conflict hotkeys first. 44 | UnregCustomHotKey(hWnd, "scrollDown") && 45 | UnregCustomHotKey(hWnd, "scrollUp") && 46 | // register hotkey for tags input. 47 | RegCustomHotKey(hWnd, "rightClickPrefix") && 48 | RegCustomHotKey(hWnd, "singleClickPrefix") && 49 | RegisterHotKey(hWnd, HOTKEY_A, 0, 0x41 /* A */) && 50 | RegisterHotKey(hWnd, HOTKEY_B, 0, 0x42 /* B */) && 51 | RegisterHotKey(hWnd, HOTKEY_C, 0, 0x43 /* C */) && 52 | RegisterHotKey(hWnd, HOTKEY_D, 0, 0x44 /* D */) && 53 | RegisterHotKey(hWnd, HOTKEY_E, 0, 0x45 /* E */) && 54 | RegisterHotKey(hWnd, HOTKEY_F, 0, 0x46 /* F */) && 55 | RegisterHotKey(hWnd, HOTKEY_G, 0, 0x47 /* G */) && 56 | RegisterHotKey(hWnd, HOTKEY_H, 0, 0x48 /* H */) && 57 | RegisterHotKey(hWnd, HOTKEY_I, 0, 0x49 /* I */) && 58 | RegisterHotKey(hWnd, HOTKEY_J, 0, 0x4A /* J */) && 59 | RegisterHotKey(hWnd, HOTKEY_K, 0, 0x4B /* K */) && 60 | RegisterHotKey(hWnd, HOTKEY_L, 0, 0x4C /* L */) && 61 | RegisterHotKey(hWnd, HOTKEY_M, 0, 0x4D /* M */) && 62 | RegisterHotKey(hWnd, HOTKEY_N, 0, 0x4E /* N */) && 63 | RegisterHotKey(hWnd, HOTKEY_O, 0, 0x4F /* O */) && 64 | RegisterHotKey(hWnd, HOTKEY_P, 0, 0x50 /* P */) && 65 | RegisterHotKey(hWnd, HOTKEY_Q, 0, 0x51 /* Q */) && 66 | RegisterHotKey(hWnd, HOTKEY_R, 0, 0x52 /* R */) && 67 | RegisterHotKey(hWnd, HOTKEY_S, 0, 0x53 /* S */) && 68 | RegisterHotKey(hWnd, HOTKEY_T, 0, 0x54 /* T */) && 69 | RegisterHotKey(hWnd, HOTKEY_U, 0, 0x55 /* U */) && 70 | RegisterHotKey(hWnd, HOTKEY_V, 0, 0x56 /* V */) && 71 | RegisterHotKey(hWnd, HOTKEY_W, 0, 0x57 /* W */) && 72 | RegisterHotKey(hWnd, HOTKEY_X, 0, 0x58 /* X */) && 73 | RegisterHotKey(hWnd, HOTKEY_Y, 0, 0x59 /* Y */) && 74 | RegisterHotKey(hWnd, HOTKEY_Z, 0, 0x5A /* Z */) 75 | ); 76 | } 77 | BOOL UnregisterAllHotKey(HWND hWnd, bool exclude_toggle) { 78 | BOOL flag = TRUE; 79 | if (!exclude_toggle) { 80 | flag = UnregCustomHotKey(hWnd, "toggleEnable"); 81 | } 82 | return ( 83 | flag && 84 | UnregCustomHotKey(hWnd, "selectMode") && 85 | UnregCustomHotKey(hWnd, "scrollUp") && 86 | UnregCustomHotKey(hWnd, "scrollDown") && 87 | UnregCustomHotKey(hWnd, "fastSelectMode") && 88 | UnregCustomHotKey(hWnd, "selectModeSingle") 89 | ); 90 | } 91 | BOOL UnregisterTagHotKey(HWND hWnd) { 92 | return ( 93 | UnregCustomHotKey(hWnd, "rightClickPrefix") && 94 | UnregCustomHotKey(hWnd, "singleClickPrefix") && 95 | UnregisterHotKey(hWnd, HOTKEY_A) && 96 | UnregisterHotKey(hWnd, HOTKEY_B) && 97 | UnregisterHotKey(hWnd, HOTKEY_C) && 98 | UnregisterHotKey(hWnd, HOTKEY_D) && 99 | UnregisterHotKey(hWnd, HOTKEY_E) && 100 | UnregisterHotKey(hWnd, HOTKEY_F) && 101 | UnregisterHotKey(hWnd, HOTKEY_G) && 102 | UnregisterHotKey(hWnd, HOTKEY_H) && 103 | UnregisterHotKey(hWnd, HOTKEY_I) && 104 | UnregisterHotKey(hWnd, HOTKEY_J) && 105 | UnregisterHotKey(hWnd, HOTKEY_K) && 106 | UnregisterHotKey(hWnd, HOTKEY_L) && 107 | UnregisterHotKey(hWnd, HOTKEY_M) && 108 | UnregisterHotKey(hWnd, HOTKEY_N) && 109 | UnregisterHotKey(hWnd, HOTKEY_O) && 110 | UnregisterHotKey(hWnd, HOTKEY_P) && 111 | UnregisterHotKey(hWnd, HOTKEY_Q) && 112 | UnregisterHotKey(hWnd, HOTKEY_R) && 113 | UnregisterHotKey(hWnd, HOTKEY_S) && 114 | UnregisterHotKey(hWnd, HOTKEY_T) && 115 | UnregisterHotKey(hWnd, HOTKEY_U) && 116 | UnregisterHotKey(hWnd, HOTKEY_V) && 117 | UnregisterHotKey(hWnd, HOTKEY_W) && 118 | UnregisterHotKey(hWnd, HOTKEY_X) && 119 | UnregisterHotKey(hWnd, HOTKEY_Y) && 120 | UnregisterHotKey(hWnd, HOTKEY_Z) && 121 | // register the previous hotkeys. 122 | RegCustomHotKey(hWnd, "scrollDown") && 123 | RegCustomHotKey(hWnd, "scrollUp") 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /KeyMouse/hotkey_handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include"stdafx.h" 3 | 4 | #define DISABLED 0 5 | // hotkeys' ID. 6 | #define HOTKEY_A 1 7 | #define HOTKEY_B 2 8 | #define HOTKEY_C 3 9 | #define HOTKEY_D 4 10 | #define HOTKEY_E 5 11 | #define HOTKEY_F 6 12 | #define HOTKEY_G 7 13 | #define HOTKEY_H 8 14 | #define HOTKEY_I 9 15 | #define HOTKEY_J 10 16 | #define HOTKEY_K 11 17 | #define HOTKEY_L 12 18 | #define HOTKEY_M 13 19 | #define HOTKEY_N 14 20 | #define HOTKEY_O 15 21 | #define HOTKEY_P 16 22 | #define HOTKEY_Q 17 23 | #define HOTKEY_R 18 24 | #define HOTKEY_S 19 25 | #define HOTKEY_T 20 26 | #define HOTKEY_U 21 27 | #define HOTKEY_V 22 28 | #define HOTKEY_W 23 29 | #define HOTKEY_X 24 30 | #define HOTKEY_Y 25 31 | #define HOTKEY_Z 26 32 | 33 | #define SHOWTAG 27 34 | #define CLEANTAG 28 35 | #define TOGGLEENABLE 29 36 | #define SCROLLUP 30 37 | #define SCROLLDOWN 31 38 | #define FASTSELECTMODE 32 39 | #define RIGHTCLICKPREFIX 33 40 | #define SINGLELEFTCLICKPREFIX 34 41 | #define FORCENOTUSECACHE 35 42 | #define SELECTMODESINGLE 36 43 | 44 | namespace KeyMouse { 45 | BOOL RegCustomHotKey(HWND hWnd, std::string key); 46 | BOOL UnregCustomHotKey(HWND hWnd, std::string key); 47 | BOOL RegisterAllHotKey(HWND hWnd, bool exclude_toggle = false); 48 | BOOL RegisterTagHotKey(HWND hWnd); 49 | BOOL UnregisterAllHotKey(HWND hWnd, bool exclude_toggle = false); 50 | BOOL UnregisterTagHotKey(HWND hWnd); 51 | } 52 | -------------------------------------------------------------------------------- /KeyMouse/small.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iscooool/KeyMouse/433ab713645ac98033fca61c2ce5dfe68a169c3d/KeyMouse/small.ico -------------------------------------------------------------------------------- /KeyMouse/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // KeyMouse.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /KeyMouse/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | //#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | #include 13 | #include 14 | // C RunTime Header Files 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | // TODO: reference additional headers your program requires here 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | 41 | #define _CRTDBG_MAP_ALLOC 42 | #include 43 | #ifdef _DEBUG 44 | #ifndef DBG_NEW 45 | #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) 46 | #define new DBG_NEW 47 | #endif 48 | #endif // _DEBUG 49 | 50 | -------------------------------------------------------------------------------- /KeyMouse/tag.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "tag.h" 3 | 4 | namespace KeyMouse { 5 | 6 | TagCreator::TagCreator() { 7 | count_ = 0; 8 | zero_ = 'A'; 9 | period_ = 26; 10 | } 11 | TagCreator::~TagCreator() { 12 | } 13 | 14 | 15 | std::queue TagCreator::AllocTag(int iCount) { 16 | count_ = iCount; 17 | string szPrefix(TEXT("")); 18 | // Clear the queue. 19 | std::queue empty; 20 | std::swap(tag_queue_, empty); 21 | tag_queue_.push(szPrefix); 22 | for(int i = 0; i < iCount; ++i) { 23 | int remainer = i % period_; 24 | if(remainer == 0) { 25 | szPrefix.clear(); 26 | szPrefix = string(TEXT("")) + tag_queue_.front(); 27 | tag_queue_.pop(); 28 | // Something is dequeued form queue. 29 | iCount += 1; 30 | } 31 | tag_queue_.push(szPrefix + string(1, zero_ + remainer)); 32 | 33 | } 34 | return tag_queue_; 35 | 36 | } 37 | 38 | void TagCreator::Clear() { 39 | count_ = 0; 40 | } 41 | 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /KeyMouse/tag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdafx.h" 3 | #include "def.h" 4 | #include 5 | #include 6 | namespace KeyMouse { 7 | 8 | class TagCreator 9 | { 10 | private: 11 | int count_; // The tags' count. 12 | TCHAR zero_; // The start tag in each period. 13 | int period_; // Use 26 english character. 14 | std::queue tag_queue_; 15 | 16 | 17 | 18 | public: 19 | TagCreator(); 20 | ~TagCreator(); 21 | std::queue AllocTag(int iCount); 22 | void Clear(); 23 | 24 | 25 | }; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /KeyMouse/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /KeyMouse/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "KeyMouse.h" 4 | #include "UIAHandler.h" 5 | #include 6 | inline void throw_if_fail(HRESULT hr) { 7 | if (FAILED(hr)) 8 | throw _com_error(hr); 9 | } 10 | 11 | //IUIAutomation *pAutomation; 12 | 13 | HRESULT InitializeUIAutomation(IUIAutomation **ppAutomation) { 14 | return CoCreateInstance(CLSID_CUIAutomation, nullptr, 15 | CLSCTX_INPROC_SERVER, IID_IUIAutomation, 16 | reinterpret_cast(ppAutomation)); 17 | } 18 | 19 | KeyMouse::Context *GetContext(HWND hMainWnd) { 20 | return reinterpret_cast(GetClassLongPtr(hMainWnd, 0)); 21 | } 22 | 23 | /** 24 | * @brief: cache all elments of handles from TopWindowVec and return the cached 25 | * elements. 26 | * 27 | * @param: HWND hMainWnd: the handle of main window. 28 | * std::vector TopWindowVec: vector of handles to enumerate. 29 | * lpEnumFunc: function pointer of enumerate strategy. 30 | * @return: void 31 | */ 32 | KeyMouse::PElementVec CachingElementsFromWindow(HWND hMainWnd, std::vector TopWindowVec,KeyMouse::PElementVec (*lpEnumFunc)(HWND, HWND)){ 33 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 34 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 35 | 36 | KeyMouse::PElementVec pElementVec(new std::vector); 37 | KeyMouse::PElementVec pTempElementVec; 38 | 39 | 40 | auto& pCacheWindow = pCtx->GetCacheWindow(); 41 | 42 | // for (auto it = TopWindowVec.begin(); it != TopWindowVec.end(); ++it) { 43 | // if (pCacheWindow->count(*it) == 0) { 44 | // CComPtr pEHTemp(new KeyMouse::CacheEventHandler(hMainWnd, *it)); 45 | // CComPtr pElement; 46 | // HRESULT hr = pAutomation->ElementFromHandle(*it, &pElement); 47 | // hr = pAutomation->AddStructureChangedEventHandler( 48 | // pElement, 49 | // TreeScope_Children, 50 | // NULL, 51 | // (IUIAutomationStructureChangedEventHandler*)pEHTemp.p 52 | // ); 53 | // 54 | // CComPtr pPropertyChangedEHTemp(new KeyMouse::PropertyChangedEventHandler(hMainWnd)); 55 | // PROPERTYID pPIDProperties[] = { 56 | // UIA_IsOffscreenPropertyId, 57 | // UIA_BoundingRectanglePropertyId 58 | // }; 59 | // hr = pAutomation->AddPropertyChangedEventHandlerNativeArray( 60 | // pElement, 61 | // TreeScope_Subtree, 62 | // NULL, 63 | // (IUIAutomationPropertyChangedEventHandler*) pPropertyChangedEHTemp.p, 64 | // pPIDProperties, 65 | // sizeof(pPIDProperties) / sizeof(pPIDProperties[0]) 66 | // ); 67 | // auto pEventHandlers = pCtx->GetEventHandlers(); 68 | // (*pEventHandlers)[*it].push_back(pEHTemp); 69 | // (*pEventHandlers)[*it].push_back(pPropertyChangedEHTemp); 70 | // pCtx->SetEventHandlers(pEventHandlers); 71 | // } 72 | // } 73 | 74 | auto Profile = pCtx->GetProfile(); 75 | if (!Profile.only_forewindow) { 76 | #ifdef _DEBUG 77 | DWORD start_time = GetTickCount(); 78 | #endif 79 | size_t nThread = TopWindowVec.size(); 80 | std::vector> Futures(nThread); 81 | // Task assignment for multi-threads. 82 | for(size_t i = 0; i < nThread; ++i) { 83 | HWND hTopWindow = TopWindowVec[i]; 84 | Futures[i] = std::async( 85 | lpEnumFunc, 86 | hMainWnd, 87 | hTopWindow 88 | ); 89 | } 90 | for(size_t i = 0; i < nThread; ++i) { 91 | auto FutureGet = Futures[i].get(); 92 | HWND hTopWindow = TopWindowVec[i]; 93 | // cache the elements for hTopWindow. 94 | (*pCacheWindow)[hTopWindow]["AllCache"] = FutureGet; 95 | 96 | if (FutureGet) { 97 | pElementVec->insert(pElementVec->end(), 98 | FutureGet->begin(), FutureGet->end()); 99 | } 100 | } 101 | #ifdef _DEBUG 102 | DWORD end_time = GetTickCount(); 103 | DWORD total_time = end_time - start_time; 104 | cout<insert(pElementVec->end(), 114 | pTempElementVec->begin(), pTempElementVec->end()); 115 | } 116 | } 117 | } 118 | 119 | return pElementVec; 120 | } 121 | 122 | struct ParamForEnum 123 | { 124 | HMONITOR *pMonitor; 125 | std::vector *pTopWindowVec; 126 | }; 127 | 128 | BOOL CALLBACK lpEnumTopWindowFunc(_In_ HWND hwnd, _In_ LPARAM lParam) { 129 | ParamForEnum *pParam = reinterpret_cast(lParam); 130 | HMONITOR *pTargetMonitor = pParam->pMonitor; 131 | std::vector *pTopWindowVec = pParam->pTopWindowVec; 132 | 133 | RECT Rect; 134 | GetWindowRect(hwnd, &Rect); 135 | HMONITOR hMonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); 136 | if (hMonitor != *pTargetMonitor) 137 | return TRUE; 138 | MONITORINFO MonitorInfo; 139 | MonitorInfo.cbSize = sizeof(MONITORINFO); 140 | GetMonitorInfo(hMonitor, &MonitorInfo); 141 | RECT Intersect; 142 | if (IntersectRect(&Intersect, &Rect, &MonitorInfo.rcWork)) { 143 | if (MonitorInfo.dwFlags != MONITORINFOF_PRIMARY) { 144 | pTopWindowVec->push_back(hwnd); 145 | return FALSE; 146 | 147 | } 148 | } 149 | 150 | return TRUE; 151 | }; 152 | 153 | BOOL CALLBACK lpEnumMonitor(HMONITOR hMonitor, HDC hDC, LPRECT lpRect, LPARAM lParam) { 154 | ParamForEnum Param; 155 | Param.pMonitor = &hMonitor; 156 | Param.pTopWindowVec = reinterpret_cast*>(lParam); 157 | EnumWindows(lpEnumTopWindowFunc, reinterpret_cast(&Param)); 158 | return TRUE; 159 | } 160 | 161 | std::thread t_tagmap; 162 | 163 | /** 164 | * @brief: a thread function for enumerating target window elements and 165 | * paint on the transparent window. 166 | * 167 | * @param: HWND hWnd: the handle of main window. 168 | * bool bUseCache: whether use cache. 169 | * @return: void 170 | */ 171 | void SetTagMapThread(HWND hWnd, bool bUseCache) { 172 | HWND *phMainWnd = reinterpret_cast(GetClassLongPtr(hWnd, 0)); 173 | KeyMouse::Context *pCtx = GetContext(*phMainWnd); 174 | if (pCtx == nullptr) 175 | return; 176 | 177 | 178 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 179 | pCtx->SetTransWindow(hWnd); 180 | KeyMouse::PElementVec pElementVec(new std::vector); 181 | KeyMouse::PElementVec pTempElementVec; 182 | 183 | HWND hForeWnd = GetForegroundWindow(); 184 | 185 | 186 | std::vector TopWindowVec; 187 | 188 | auto Profile = pCtx->GetProfile(); 189 | if (!Profile.only_forewindow) { 190 | HMONITOR hMonitor = MonitorFromWindow(hForeWnd, MONITOR_DEFAULTTONEAREST); 191 | MONITORINFO MonitorInfo; 192 | MonitorInfo.cbSize = sizeof(MONITORINFO); 193 | GetMonitorInfo(hMonitor, &MonitorInfo); 194 | 195 | // due to the hardness of identifing real top visible winodow on primary monitor. 196 | // when foreground window is not on primary monitor, we use hwnd previously stored 197 | // in ctx as the top visible window on primary monitor. 198 | if (MonitorInfo.dwFlags != MONITORINFOF_PRIMARY) { 199 | hForeWnd = pCtx->GetForeWindow(); 200 | } 201 | else { 202 | pCtx->SetForeWindow(hForeWnd); 203 | } 204 | 205 | TopWindowVec.push_back(hForeWnd); 206 | EnumDisplayMonitors(NULL, NULL, lpEnumMonitor, reinterpret_cast(&TopWindowVec)); 207 | 208 | 209 | } 210 | else { 211 | pCtx->SetForeWindow(hForeWnd); 212 | TopWindowVec.push_back(hForeWnd); 213 | } 214 | 215 | if (bUseCache && 216 | !pCtx->GetCacheExpiredState() && 217 | !pCtx->GetCacheExpiredStructChanged() 218 | ) { 219 | pCtx->WaitForCacheWindow(); 220 | 221 | for(auto it = TopWindowVec.begin(); it != TopWindowVec.end();) { 222 | auto& pCacheWindow = pCtx->GetCacheWindow(); 223 | if (pCacheWindow->count(*it) > 0) { 224 | auto& pCache = (*pCacheWindow)[*it]["AllCache"]; 225 | if (pCache) { 226 | pElementVec->insert(pElementVec->end(), 227 | pCache->begin(), pCache->end()); 228 | } 229 | it = TopWindowVec.erase(it); 230 | } 231 | else { 232 | CComPtr pEHTemp(new KeyMouse::CacheEventHandler(*phMainWnd, *it)); 233 | IUIAutomationElement* pElement; 234 | HRESULT hr = pAutomation->ElementFromHandle(*it, &pElement); 235 | hr = pAutomation->AddStructureChangedEventHandler( 236 | pElement, 237 | TreeScope_Children, 238 | NULL, 239 | (IUIAutomationStructureChangedEventHandler*)pEHTemp.p 240 | ); 241 | pElement->Release(); 242 | //CComPtr pPropertyChangedEHTemp(new KeyMouse::PropertyChangedEventHandler(*phMainWnd)); 243 | //PROPERTYID pPIDProperties[] = { 244 | // UIA_IsOffscreenPropertyId, 245 | // UIA_BoundingRectanglePropertyId 246 | //}; 247 | //hr = pAutomation->AddPropertyChangedEventHandlerNativeArray(pElement, 248 | // TreeScope_Subtree, 249 | // NULL, 250 | // (IUIAutomationPropertyChangedEventHandler*) pPropertyChangedEHTemp.p, 251 | // pPIDProperties, 252 | // sizeof(pPIDProperties) / sizeof(pPIDProperties[0]) 253 | //); 254 | auto pEventHandlers = pCtx->GetEventHandlers(); 255 | (*pEventHandlers)[*it].push_back(pEHTemp); 256 | //(*pEventHandlers)[*it].push_back(pPropertyChangedEHTemp); 257 | pCtx->SetEventHandlers(pEventHandlers); 258 | ++it; 259 | } 260 | } 261 | } 262 | else { 263 | // if don't use cache, the cache will be updated. 264 | pCtx->SetCacheExpiredState(false); 265 | pCtx->SetCacheExpiredStructChanged(false); 266 | } 267 | 268 | KeyMouse::PElementVec pElementsFromUncache; 269 | pElementsFromUncache = CachingElementsFromWindow(*phMainWnd, TopWindowVec, EnumConditionedElement); 270 | if (pElementsFromUncache) { 271 | pElementVec->insert(pElementVec->end(), 272 | pElementsFromUncache->begin(), pElementsFromUncache->end()); 273 | } 274 | 275 | //pElementVec = EnumTargetWindow(*phMainWnd, hForeWnd); 276 | 277 | // find the elements of taskbar. 278 | CComPtr pDesktop; 279 | HRESULT hr = pAutomation->GetRootElement(&pDesktop); 280 | throw_if_fail(hr); 281 | 282 | CComPtr pCondition; 283 | VARIANT Val; 284 | Val.vt = VT_BSTR; 285 | Val.bstrVal = SysAllocString(TEXT("Shell_TrayWnd")); 286 | hr = pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, 287 | Val, 288 | &pCondition); 289 | throw_if_fail(hr); 290 | 291 | CComPtr pTaskBar; 292 | pDesktop->FindFirst(TreeScope_Children, pCondition, &pTaskBar); 293 | if (pTaskBar) { 294 | UIA_HWND hTaskBar; 295 | pTaskBar->get_CurrentNativeWindowHandle(&hTaskBar); 296 | if (hForeWnd != static_cast(hTaskBar)) { 297 | pTempElementVec = EnumConditionedElement(*phMainWnd, static_cast(hTaskBar)); 298 | if (pTempElementVec) { 299 | pElementVec->insert(pElementVec->end(), 300 | pTempElementVec->begin(), pTempElementVec->end()); 301 | } 302 | } 303 | } 304 | // find the elements of other windows. 305 | KeyMouse::PElementVec pWindowVec(new std::vector); 306 | if (Profile.enable_window_switching) { 307 | CComPtr pIsOffScreenCondition; 308 | Val.vt = VT_BOOL; 309 | Val.boolVal = VARIANT_FALSE; 310 | hr = pAutomation->CreatePropertyCondition(UIA_IsOffscreenPropertyId, 311 | Val, 312 | &pIsOffScreenCondition); 313 | throw_if_fail(hr); 314 | CComPtr pCacheRequest; 315 | hr = pAutomation->CreateCacheRequest(&pCacheRequest); 316 | throw_if_fail(hr); 317 | hr = pCacheRequest->AddProperty(UIA_BoundingRectanglePropertyId); 318 | throw_if_fail(hr); 319 | CComPtr pWindowElementArray; 320 | hr = pDesktop->FindAllBuildCache(TreeScope_Children, 321 | pIsOffScreenCondition, 322 | pCacheRequest, 323 | &pWindowElementArray); 324 | throw_if_fail(hr); 325 | int nWindowNum = 0; 326 | if(pWindowElementArray != nullptr) { 327 | hr = pWindowElementArray->get_Length(&nWindowNum); 328 | throw_if_fail(hr); 329 | } 330 | 331 | for (int i = 0; i < nWindowNum; ++i) { 332 | IUIAutomationElement *pTempElement; 333 | KeyMouse::ElementInfo Temp; 334 | pWindowElementArray->GetElement(i, &pTempElement); 335 | pTempElement->get_CachedBoundingRectangle(&Temp.rect); 336 | pTempElement->get_CachedNativeWindowHandle(&Temp.handle); 337 | pWindowVec->push_back(Temp); 338 | } 339 | 340 | } 341 | 342 | //------------------------------------------------------------------- 343 | KeyMouse::PTagMap pTagMap(new std::map); 344 | KeyMouse::PTagMap pWindowMap(new std::map); 345 | KeyMouse::TagCreator TC; 346 | std::queue TagQueue = TC.AllocTag(pElementVec->size() + pWindowVec->size()); 347 | // the last one of the queue must be the longest one. 348 | pCtx->SetMaxTagLen(TagQueue.back().length()); 349 | 350 | // Traverse the items of ElementArray and paint all hints on the screen. 351 | for(auto& pTempElement : *pElementVec) { 352 | string szTemp = TagQueue.front(); 353 | TagQueue.pop(); 354 | 355 | // insert the tag and Element into the keymap. 356 | pTagMap->insert(std::pair( 357 | szTemp, pTempElement)); 358 | } 359 | for(auto& pTempElement : *pWindowVec) { 360 | string szTemp = TagQueue.front(); 361 | TagQueue.pop(); 362 | 363 | // insert the tag and Element into the keymap. 364 | pWindowMap->insert(std::pair( 365 | szTemp, pTempElement)); 366 | } 367 | 368 | HDC hDC = GetDC(hWnd); 369 | if (pWindowMap != nullptr) { 370 | for (auto& item : *pWindowMap) { 371 | RECT Rect = item.second.rect; 372 | // item.second->get_CachedBoundingRectangle(&Rect); 373 | 374 | // print the hint on the screen. 375 | const TCHAR *psText = item.first.c_str(); 376 | POINT point; 377 | point.x = (Rect.left + Rect.right) / 2; 378 | point.y = Rect.top; 379 | KeyMouse::Font font = Profile.window_tag_font; 380 | DrawTag(*phMainWnd, hWnd, hDC, point, psText, font); 381 | 382 | } 383 | } 384 | if (pTagMap != nullptr) { 385 | for (auto& item : *pTagMap) { 386 | RECT Rect = item.second.rect; 387 | // item.second->get_CachedBoundingRectangle(&Rect); 388 | 389 | // print the hint on the screen. 390 | const TCHAR *psText = item.first.c_str(); 391 | POINT point; 392 | point.x = Rect.left; 393 | point.y = Rect.top; 394 | DrawTag(*phMainWnd, hWnd, hDC, point, psText, Profile.font); 395 | 396 | } 397 | } 398 | ReleaseDC(hWnd, hDC); 399 | pCtx->SetTagMap(pTagMap); 400 | pCtx->SetWindowMap(pWindowMap); 401 | 402 | VariantClear(&Val); 403 | RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); 404 | 405 | } 406 | 407 | 408 | /** 409 | * @brief: a thread function for caching elements of target window. 410 | * 411 | * @param: HWND hWnd: the handle of main window. 412 | * @return: void 413 | */ 414 | void CacheThread(HWND hWnd) { 415 | HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); 416 | 417 | KeyMouse::Context *pCtx = GetContext(hWnd); 418 | if (pCtx == nullptr) 419 | return; 420 | 421 | bool bIsCacheExpired = pCtx->GetCacheExpiredState(); 422 | bool bCacheExpiredStructChanged = pCtx->GetCacheExpiredStructChanged(); 423 | LASTINPUTINFO Lii; 424 | Lii.cbSize = sizeof(LASTINPUTINFO); 425 | GetLastInputInfo(&Lii); 426 | DWORD CurrentTime = GetTickCount(); 427 | DWORD LastTime = pCtx->GetLastInputTick(); 428 | //pCtx->SetLastInputTick(Lii.dwTime); 429 | if ((LastTime > 0 && CurrentTime - LastTime > 200 && bIsCacheExpired) || 430 | (CurrentTime - LastTime > 1 && bCacheExpiredStructChanged)) { 431 | if (pCtx->GetEnableState()) { 432 | 433 | HWND hForeWnd = GetForegroundWindow(); 434 | std::vector TopWindowVec; 435 | 436 | auto Profile = pCtx->GetProfile(); 437 | // TODO: set to false to comment out the code. need to be clean up. 438 | if (false) { 439 | HMONITOR hMonitor = MonitorFromWindow(hForeWnd, MONITOR_DEFAULTTONEAREST); 440 | MONITORINFO MonitorInfo; 441 | MonitorInfo.cbSize = sizeof(MONITORINFO); 442 | GetMonitorInfo(hMonitor, &MonitorInfo); 443 | 444 | // due to the hardness of identifing real top visible winodow on primary monitor. 445 | // when foreground window is not on primary monitor, we use hwnd previously stored 446 | // in ctx as the top visible window on primary monitor. 447 | if (MonitorInfo.dwFlags != MONITORINFOF_PRIMARY) { 448 | hForeWnd = pCtx->GetForeWindow(); 449 | } 450 | else { 451 | pCtx->SetForeWindow(hForeWnd); 452 | } 453 | 454 | TopWindowVec.push_back(hForeWnd); 455 | EnumDisplayMonitors(NULL, NULL, lpEnumMonitor, reinterpret_cast(&TopWindowVec)); 456 | 457 | 458 | } 459 | else { 460 | pCtx->SetForeWindow(hForeWnd); 461 | TopWindowVec.push_back(hForeWnd); 462 | } 463 | 464 | // only cache the windows which have been selected. 465 | for(auto it = TopWindowVec.begin(); it != TopWindowVec.end();) { 466 | auto& pCacheWindow = pCtx->GetCacheWindow(); 467 | if (pCacheWindow->count(*it) == 0) { 468 | it = TopWindowVec.erase(it); 469 | } 470 | else { 471 | ++it; 472 | } 473 | } 474 | 475 | pCtx->SetLastcacheTick(CurrentTime); 476 | pCtx->SetCacheExpiredState(false); 477 | pCtx->SetCacheExpiredStructChanged(false); 478 | pCtx->LockCacheWindow(); 479 | CachingElementsFromWindow(hWnd, TopWindowVec, EnumConditionedElementForCaching); 480 | pCtx->UnlockCacheWindow(); 481 | } 482 | } 483 | 484 | CoUninitialize(); 485 | } 486 | 487 | /** 488 | * @brief: TransparentWndProc is used by a transparent window which is used to paint hints 489 | * on. 490 | * 491 | * @param: HWND hWnd: the handle of transparent window. And arguments below is 492 | * similar with common win32 WndProc. 493 | * : UINT message 494 | * : WPARAM wParam 495 | * : LPARAM lParam 496 | * 497 | * @return: LRESULT 498 | */ 499 | LRESULT CALLBACK TransparentWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 500 | { 501 | 502 | switch (message) 503 | { 504 | case WM_PAINT: 505 | { 506 | PAINTSTRUCT ps = { 0 }; 507 | HDC hDC = BeginPaint(hWnd, &ps); 508 | EndPaint(hWnd, &ps); 509 | SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 510 | 511 | } 512 | break; 513 | case WM_NCHITTEST: 514 | return HTCAPTION; 515 | case WM_ERASEBKGND: { 516 | RECT rect; 517 | GetClientRect(hWnd, &rect); 518 | FillRect((HDC)wParam, &rect, CreateSolidBrush(RGB(255, 0, 255))); 519 | HWND *phMainWnd = reinterpret_cast(GetClassLongPtr(hWnd, 0)); 520 | KeyMouse::Context *pCtx = 521 | reinterpret_cast( 522 | GetClassLongPtr(*phMainWnd, 0) 523 | ); 524 | if (pCtx) { 525 | bool bEnableCache = pCtx->GetProfile().enable_cache; 526 | t_tagmap = std::thread(SetTagMapThread, hWnd, bEnableCache); 527 | t_tagmap.detach(); 528 | } 529 | } 530 | break; 531 | case WM_DESTROY: { 532 | HWND *phMainWnd = reinterpret_cast(GetClassLongPtr(hWnd, 0)); 533 | delete phMainWnd; 534 | } 535 | break; 536 | default: 537 | return DefWindowProc(hWnd, message, wParam, lParam); 538 | } 539 | return 0; 540 | } 541 | 542 | 543 | /** 544 | * @brief: create a transparent window to paint hints on. 545 | * 546 | * @param: HINSTANCE hInstance: the global HINSTANCE. 547 | * : HWND hMainWnd: the original window which is created when app starts 548 | * up. we store global context in it. 549 | * 550 | * @return: HWND: the handle of transparent window. 551 | */ 552 | HWND CreateTransparentWindow(HINSTANCE hInstance, HWND hMainWnd) 553 | { 554 | HWND hWnd; 555 | HINSTANCE hInst = hInstance; 556 | 557 | DWORD Flags1 = WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOPMOST | WS_EX_TRANSPARENT; 558 | DWORD Flags2 = WS_VISIBLE | WS_POPUP; 559 | 560 | WCHAR szWindowClass[] = TEXT("Transparent Window"); 561 | WCHAR szTitle[] = TEXT("Transparent Window"); 562 | 563 | WNDCLASSEXW wcex; 564 | 565 | wcex.cbSize = sizeof(WNDCLASSEX); 566 | 567 | wcex.style = CS_HREDRAW | CS_VREDRAW; 568 | wcex.lpfnWndProc = TransparentWndProc; 569 | wcex.cbClsExtra = sizeof(HWND *); // Extra space for main window. 570 | wcex.cbWndExtra = 0; 571 | wcex.hInstance = hInstance; 572 | wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KEYMOUSE)); 573 | wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); 574 | wcex.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH); 575 | wcex.lpszMenuName = NULL; 576 | wcex.lpszClassName = szWindowClass; 577 | wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 578 | 579 | RegisterClassExW(&wcex); 580 | // Get the resolution of the virtual screen. 581 | int X = GetSystemMetrics(SM_XVIRTUALSCREEN); 582 | int Y = GetSystemMetrics(SM_YVIRTUALSCREEN); 583 | int Width = GetSystemMetrics(SM_CXVIRTUALSCREEN); 584 | int Height = GetSystemMetrics(SM_CYVIRTUALSCREEN); 585 | hWnd = CreateWindowEx(Flags1, szWindowClass, szTitle, Flags2, 586 | X, Y, 587 | Width, Height, 588 | 0, 0, hInstance, 0); 589 | 590 | if (!hWnd) 591 | return NULL; 592 | 593 | // Set the main window's hWnd for getting the context. 594 | HWND* phMainWnd = new HWND(hMainWnd); 595 | SetClassLongPtr(hWnd, 0, reinterpret_cast(phMainWnd)); 596 | 597 | HRGN GGG = CreateRectRgn(X, Y, 598 | Width, Height); 599 | InvertRgn(GetDC(hWnd), GGG); 600 | SetWindowRgn(hWnd, GGG, false); 601 | 602 | KeyMouse::Context *pCtx = reinterpret_cast( 603 | GetClassLongPtr(hMainWnd, 0) 604 | ); 605 | int opacity = pCtx->GetProfile().opacity; 606 | COLORREF RRR = RGB(255, 0, 255); 607 | SetLayeredWindowAttributes(hWnd, RRR, (BYTE)255 * opacity / 100, LWA_ALPHA | LWA_COLORKEY); 608 | 609 | ShowWindow(hWnd, SW_SHOWNORMAL); 610 | UpdateWindow(hWnd); 611 | 612 | 613 | return hWnd; 614 | } 615 | 616 | 617 | BOOL CALLBACK lpGetControlCoordinate(_In_ HWND hwnd, _In_ LPARAM lParam) { 618 | std::vector *phWndVec = reinterpret_cast *>(lParam); 619 | 620 | HWND hChild = GetWindow(hwnd, GW_CHILD); 621 | //if(hChild == NULL && IsWindowVisible(hwnd)) 622 | phWndVec->push_back(hwnd); 623 | return TRUE; 624 | }; 625 | 626 | // TODO: never be used. 627 | KeyMouse::PElementVec EnumTargetWindow(HWND hMainWnd, HWND hForeWnd) { 628 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 629 | std::unique_ptr> phWndVec(new std::vector); 630 | EnumChildWindows(hForeWnd, lpGetControlCoordinate, reinterpret_cast(phWndVec.get())); 631 | 632 | KeyMouse::PElementVec pElementVec(new std::vector); 633 | for (auto hTargetWnd : *phWndVec) { 634 | } 635 | KeyMouse::PElementVec pTempVec = EnumConditionedElement(hMainWnd, hForeWnd); 636 | pElementVec->insert(pElementVec->end(), 637 | pTempVec->begin(), pTempVec->end()); 638 | return pElementVec; 639 | } 640 | 641 | IUIAutomationCondition* GetDesiredCondition(HWND hMainWnd) { 642 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 643 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 644 | HRESULT hr = S_OK; 645 | //----------------------create element type condition ---------------------------- 646 | // Define the condition by pTotalCondition to find all desired items. 647 | std::vector vPropertyId = { 648 | UIA_ListItemControlTypeId, 649 | UIA_ButtonControlTypeId, 650 | UIA_TreeItemControlTypeId, 651 | UIA_TabItemControlTypeId, 652 | UIA_HyperlinkControlTypeId, 653 | UIA_SplitButtonControlTypeId, 654 | UIA_ScrollBarControlTypeId, 655 | UIA_MenuItemControlTypeId 656 | }; 657 | SAFEARRAY *pConditionVector = SafeArrayCreateVector( 658 | VT_UNKNOWN, 659 | 0, 660 | vPropertyId.size() 661 | ); 662 | 663 | LONG i = 0; 664 | 665 | // construct an condition array and use it for creating a big 666 | // OrCondition. 667 | for (PROPERTYID PropertyId : vPropertyId) { 668 | IUIAutomationCondition *pCondition; 669 | VARIANT Val; 670 | Val.vt = VT_I4; 671 | Val.lVal = PropertyId; 672 | hr = pAutomation->CreatePropertyCondition(UIA_ControlTypePropertyId, 673 | Val, 674 | &pCondition); 675 | throw_if_fail(hr); 676 | 677 | hr = SafeArrayPutElement(pConditionVector, &i, pCondition); 678 | throw_if_fail(hr); 679 | 680 | ++i; 681 | } 682 | IUIAutomationCondition* pTotalOrCondition; 683 | 684 | hr = pAutomation->CreateOrConditionFromArray( 685 | pConditionVector, 686 | &pTotalOrCondition 687 | ); 688 | throw_if_fail(hr); 689 | hr = SafeArrayDestroy(pConditionVector); 690 | //----------------------create bool condition ---------------------------- 691 | std::vector> vPropertyTuple = { 692 | {UIA_IsEnabledPropertyId, VARIANT_TRUE}, 693 | {UIA_IsOffscreenPropertyId, VARIANT_FALSE} 694 | }; 695 | 696 | pConditionVector = SafeArrayCreateVector( 697 | VT_UNKNOWN, 698 | 0, 699 | vPropertyTuple.size() 700 | ); 701 | 702 | i = 0; 703 | 704 | for (std::tuple PropertyTuple : vPropertyTuple) { 705 | IUIAutomationCondition *pCondition; 706 | VARIANT Val; 707 | Val.vt = VT_BOOL; 708 | Val.lVal = std::get<1>(PropertyTuple); 709 | hr = pAutomation->CreatePropertyCondition(std::get<0>(PropertyTuple), 710 | Val, 711 | &pCondition); 712 | 713 | hr = SafeArrayPutElement(pConditionVector, &i, pCondition); 714 | throw_if_fail(hr); 715 | 716 | ++i; 717 | } 718 | IUIAutomationCondition* pTotalAndCondition; 719 | 720 | hr = pAutomation->CreateAndConditionFromArray( 721 | pConditionVector, 722 | &pTotalAndCondition 723 | ); 724 | throw_if_fail(hr); 725 | hr = SafeArrayDestroy(pConditionVector); 726 | throw_if_fail(hr); 727 | //---------------------------------------------------------------------------- 728 | 729 | IUIAutomationCondition* pTotalCondition; 730 | hr = pAutomation->CreateAndCondition(pTotalAndCondition, 731 | pTotalOrCondition, 732 | &pTotalCondition 733 | ); 734 | 735 | pTotalAndCondition->Release(); 736 | pTotalOrCondition->Release(); 737 | return pTotalCondition; 738 | } 739 | 740 | IUIAutomationCacheRequest* GetDesiredCacheRequest(HWND hMainWnd) { 741 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 742 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 743 | 744 | IUIAutomationCacheRequest* pCacheRequest; 745 | HRESULT hr = pAutomation->CreateCacheRequest(&pCacheRequest); 746 | throw_if_fail(hr); 747 | 748 | //hr = pCacheRequest->AddPattern(UIA_ScrollPatternId); 749 | throw_if_fail(hr); 750 | //hr = pCacheRequest->AddPattern(UIA_InvokePatternId); 751 | throw_if_fail(hr); 752 | hr = pCacheRequest->AddProperty(UIA_BoundingRectanglePropertyId); 753 | throw_if_fail(hr); 754 | hr = pCacheRequest->AddProperty(UIA_ControlTypePropertyId); 755 | throw_if_fail(hr); 756 | hr = pCacheRequest->AddProperty(UIA_IsOffscreenPropertyId); 757 | throw_if_fail(hr); 758 | hr = pCacheRequest->AddProperty(UIA_NativeWindowHandlePropertyId); 759 | throw_if_fail(hr); 760 | 761 | return pCacheRequest; 762 | } 763 | 764 | /** 765 | * @brief: the much slow version of Findall subtree. but it doesn't 766 | * freeze the target window. It's used for caching. 767 | * 768 | * @param: HWND hMainWnd: the main window handle. 769 | * : int restric_depth : the search depth. 770 | * : IUIAutomationElement *element : the root node to walk. 771 | * : IUIAutomationCacheRequest * cacheRequest 772 | * : IUIAutomationTreeWalker *walker 773 | * : std::vector> *found 774 | * 775 | * @return: HRESULT 776 | */ 777 | HRESULT WalkDesiredElementBuildCache( 778 | HWND hMainWnd, 779 | int restrict_depth, 780 | IUIAutomationElement *element, 781 | IUIAutomationCondition *condition, 782 | IUIAutomationCacheRequest *cacheRequest, 783 | IUIAutomationTreeWalker *walker, 784 | std::vector *found) 785 | { 786 | HRESULT hr = S_OK; 787 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 788 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 789 | CComPtr pTrueCondition; 790 | pAutomation->CreateTrueCondition(&pTrueCondition); 791 | 792 | CComPtr pWalker; 793 | pAutomation->get_RawViewWalker(&pWalker); 794 | 795 | int nDepth = 0; 796 | std::queue queue; 797 | queue.push(element); 798 | IUIAutomationElement* pCurrent; 799 | IUIAutomationElement* pExist; 800 | do { 801 | pCurrent = queue.front(); 802 | queue.pop(); 803 | //CComPtr pFirstChild; 804 | 805 | // pWalker->GetFirstChildElementBuildCache( 806 | // pCurrent, cacheRequest, &pFirstChild); 807 | 808 | //if (pFirstChild == NULL) { // when current has no child. 809 | //} 810 | //else { //pCurrent is not a leaf element. 811 | // queue.push(pFirstChild); 812 | // IUIAutomationElement* pPrev = NULL; 813 | // CComPtr pSecond; 814 | // pWalker->GetNextSiblingElementBuildCache(pFirstChild, cacheRequest, &pSecond); 815 | // pPrev = pSecond; 816 | // while (pPrev != NULL) { 817 | // CComPtr pNext; 818 | // BOOL IsOffScreen = FALSE; 819 | // pPrev->get_CachedIsOffscreen(&IsOffScreen); 820 | // if (IsOffScreen == FALSE) { 821 | // queue.push(pPrev); 822 | // } 823 | // pPrev = pNext; 824 | // pWalker->GetNextSiblingElementBuildCache(pPrev, cacheRequest, &pNext); 825 | // } 826 | //} 827 | 828 | if (pCurrent != nullptr) { 829 | HRESULT hr; 830 | 831 | IUIAutomationElementArray* pChildrenElementArray; 832 | hr = pCurrent->FindAllBuildCache(TreeScope_Children, 833 | pTrueCondition, 834 | cacheRequest, 835 | &pChildrenElementArray); 836 | throw_if_fail(hr); 837 | 838 | int nChildrenNum = 0; 839 | if(pChildrenElementArray != nullptr) { 840 | hr = pChildrenElementArray->get_Length(&nChildrenNum); 841 | throw_if_fail(hr); 842 | } 843 | for (int i = 0; i < nChildrenNum; ++i) { 844 | IUIAutomationElement* pChild; 845 | pChildrenElementArray->GetElement(i, &pChild); 846 | if (pChild != nullptr) { 847 | BOOL IsOffScreen = FALSE; 848 | pChild->get_CachedIsOffscreen(&IsOffScreen); 849 | if (IsOffScreen == FALSE) { 850 | queue.push(pChild); 851 | } 852 | else { 853 | pChild->Release(); 854 | } 855 | } 856 | } 857 | if (pChildrenElementArray != nullptr) { 858 | pChildrenElementArray->Release(); 859 | } 860 | pCurrent->FindFirst(TreeScope_Element, condition, &pExist); 861 | if (pExist != nullptr) { 862 | KeyMouse::ElementInfo Temp; 863 | pCurrent->get_CachedBoundingRectangle(&Temp.rect); 864 | pCurrent->get_CachedControlType(&Temp.control_type); 865 | pCurrent->get_CachedNativeWindowHandle(&Temp.handle); 866 | found->push_back(Temp); 867 | pExist->Release(); 868 | } 869 | } 870 | 871 | pCurrent->Release(); 872 | 873 | } while (!queue.empty()); 874 | return S_OK; 875 | } 876 | /** 877 | * @brief: TODO: need to be refactored. 878 | * enumerate all element of foreground window which satisfies specific 879 | * condition. it's used for select mode. 880 | * 881 | * @param: HWND hMainWnd: the handle of window stores global context. 882 | * : HWND hForeWnd: the target(or foreground) window handle. 883 | * 884 | * @return: PElementVec: a vector of all elements found in foreground window. 885 | */ 886 | KeyMouse::PElementVec EnumConditionedElement(HWND hMainWnd, HWND hForeWnd) { 887 | try { 888 | 889 | // Get current context. 890 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 891 | if (!pCtx) { 892 | return nullptr; 893 | } 894 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 895 | HWND hTransWnd = pCtx->GetTransWindow(); 896 | CComPtr pElement; 897 | HRESULT hr = pAutomation->ElementFromHandle(hForeWnd, &pElement); 898 | throw_if_fail(hr); 899 | 900 | // this is used for fastSelectMode. add a event handler to detect 901 | // element tree change. 902 | if (pCtx->GetFastSelectState()) { 903 | // add event handler to detect changing when in consercutive mode. 904 | CComPtr pEHTemp(new KeyMouse::EventHandler(hMainWnd)); 905 | hr = pAutomation->AddStructureChangedEventHandler(pElement, 906 | TreeScope_Subtree, NULL, 907 | (IUIAutomationStructureChangedEventHandler*)pEHTemp); 908 | 909 | pCtx->SetStructEventHandler(pEHTemp); 910 | pCtx->SetElement(pElement); 911 | // get the current foreground window's file name. 912 | DWORD pid; 913 | GetWindowThreadProcessId(hForeWnd, &pid); 914 | HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); 915 | CHAR lpFileName[200] = { 0 }; 916 | GetModuleFileNameExA(hProc, NULL, lpFileName, 200); 917 | pCtx->SetPrevProcessName(std::string(lpFileName)); 918 | CloseHandle(hProc); 919 | } 920 | CComPtr pElementArray; 921 | 922 | 923 | IUIAutomationCondition* pTotalCondition = GetDesiredCondition(hMainWnd); 924 | // add cache request for desired patterns and properties. 925 | IUIAutomationCacheRequest* pCacheRequest = GetDesiredCacheRequest(hMainWnd); 926 | 927 | throw_if_fail(hr); 928 | CComPtr pChildrenElementArray; 929 | hr = pElement->FindAllBuildCache(TreeScope_Descendants, 930 | pTotalCondition, 931 | pCacheRequest, 932 | &pChildrenElementArray); 933 | throw_if_fail(hr); 934 | 935 | int nChildrenNum = 0; 936 | if(pChildrenElementArray != nullptr) { 937 | hr = pChildrenElementArray->get_Length(&nChildrenNum); 938 | throw_if_fail(hr); 939 | } 940 | 941 | 942 | KeyMouse::PElementVec pElementVec(new std::vector); 943 | for (int i = 0; i < nChildrenNum; ++i) { 944 | IUIAutomationElement* pTempElement; 945 | KeyMouse::ElementInfo Temp; 946 | pChildrenElementArray->GetElement(i, &pTempElement); 947 | pTempElement->get_CachedBoundingRectangle(&Temp.rect); 948 | pTempElement->get_CachedControlType(&Temp.control_type); 949 | pTempElement->get_CachedNativeWindowHandle(&Temp.handle); 950 | pElementVec->push_back(Temp); 951 | pTempElement->Release(); 952 | 953 | } 954 | pCacheRequest->Release(); 955 | pTotalCondition->Release(); 956 | return pElementVec; 957 | } 958 | catch (_com_error err) { 959 | } 960 | return nullptr; 961 | } 962 | 963 | /** 964 | * @brief: enumerate all element of foreground window which satisfies specific 965 | * condition, it's used for caching all elements on target window. 966 | * 967 | * @param: HWND hMainWnd: the handle of window stores global context. 968 | * : HWND hForeWnd: the target(or foreground) window handle. 969 | * 970 | * @return: PElementVec: a vector of all elements found in targer(or foreground) window. 971 | */ 972 | KeyMouse::PElementVec EnumConditionedElementForCaching(HWND hMainWnd, HWND hForeWnd) { 973 | try { 974 | 975 | // Get current context. 976 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 977 | if (!pCtx) { 978 | return nullptr; 979 | } 980 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 981 | HWND hTransWnd = pCtx->GetTransWindow(); 982 | IUIAutomationElement* pElement; 983 | HRESULT hr = pAutomation->ElementFromHandle(hForeWnd, &pElement); 984 | throw_if_fail(hr); 985 | 986 | IUIAutomationCondition* pTotalCondition = GetDesiredCondition(hMainWnd); 987 | // add cache request for desired patterns and properties. 988 | IUIAutomationCacheRequest* pCacheRequest = GetDesiredCacheRequest(hMainWnd); 989 | 990 | throw_if_fail(hr); 991 | KeyMouse::PElementVec pElementVec(new std::vector); 992 | hr = WalkDesiredElementBuildCache( 993 | hMainWnd, 994 | 20, 995 | pElement, 996 | pTotalCondition, 997 | pCacheRequest, 998 | NULL, 999 | pElementVec.get() 1000 | ); 1001 | 1002 | pCacheRequest->Release(); 1003 | pTotalCondition->Release(); 1004 | return pElementVec; 1005 | } 1006 | catch (_com_error err) { 1007 | } 1008 | return nullptr; 1009 | } 1010 | /** 1011 | * @brief: check whether focus on edit control.(not always works.) 1012 | * 1013 | * @param: 1014 | * 1015 | * @return: bool: if focus on edit control, return true. else false. 1016 | */ 1017 | bool isFocusOnEdit(HWND hMainWnd) { 1018 | KeyMouse::Context *pCtx = GetContext(hMainWnd); 1019 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 1020 | if(pAutomation == nullptr) 1021 | return false; 1022 | try { 1023 | CComPtr pTempElement; 1024 | HRESULT hr = pAutomation->GetFocusedElement(&pTempElement); 1025 | throw_if_fail(hr); 1026 | CONTROLTYPEID iControlType; 1027 | if(pTempElement) 1028 | pTempElement->get_CurrentControlType(&iControlType); 1029 | if(iControlType == UIA_EditControlTypeId) { 1030 | 1031 | return true; 1032 | } 1033 | else 1034 | return false; 1035 | } 1036 | catch (_com_error err) { 1037 | } 1038 | return false; 1039 | } 1040 | //Returns the last Win32 error, in string format. Returns an empty string if there is no error. 1041 | std::string GetLastErrorAsString() 1042 | { 1043 | //Get the error message, if any. 1044 | DWORD errorMessageID = ::GetLastError(); 1045 | if(errorMessageID == 0) 1046 | return std::string(); //No error message has been recorded 1047 | 1048 | LPSTR messageBuffer = nullptr; 1049 | size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 1050 | NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); 1051 | 1052 | std::string message(messageBuffer, size); 1053 | 1054 | //Free the buffer. 1055 | LocalFree(messageBuffer); 1056 | 1057 | return message; 1058 | } 1059 | 1060 | void DrawTag(HWND hMainWnd, HWND hTransWnd, HDC hdc, POINT point, const TCHAR* psText, KeyMouse::Font font) { 1061 | KeyMouse::Context *pCtx = 1062 | reinterpret_cast( 1063 | GetClassLongPtr(hMainWnd, 0) 1064 | ); 1065 | 1066 | ScreenToClient(hTransWnd, &point); 1067 | 1068 | HPEN hpenOld = static_cast(SelectObject(hdc, GetStockObject(DC_PEN))); 1069 | HBRUSH hbrushOld = static_cast(SelectObject(hdc, GetStockObject(DC_BRUSH))); 1070 | // background color. 1071 | SetDCBrushColor(hdc, font.background_color); 1072 | 1073 | int nHight = MulDiv(font.font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72); 1074 | int PrimaryHeight = GetSystemMetrics(SM_CYSCREEN); 1075 | // use different height on different monitors. 1076 | 1077 | HFONT hFont = CreateFont(nHight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 1078 | ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 1079 | DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, font.font_name.c_str()); 1080 | 1081 | HFONT hfontOld = static_cast(SelectObject(hdc, hFont)); 1082 | SIZE TextSize; 1083 | 1084 | GetTextExtentPoint(hdc, psText, _tcslen(psText), &TextSize); 1085 | 1086 | int WidthMax = GetSystemMetrics(SM_CXVIRTUALSCREEN); 1087 | int HeightMax = GetSystemMetrics(SM_CYVIRTUALSCREEN); 1088 | point.x = point.x > WidthMax ? WidthMax : point.x; 1089 | point.y = point.y > HeightMax ? HeightMax : point.y; 1090 | LogicalToPhysicalPointForPerMonitorDPI(hTransWnd, &point); 1091 | double PaddingRatio = 1.4; 1092 | RECT Rect; 1093 | Rect.left = point.x; 1094 | Rect.top = point.y; 1095 | Rect.right = static_cast(point.x + TextSize.cx * PaddingRatio); 1096 | Rect.bottom = static_cast(point.y + TextSize.cy * PaddingRatio); 1097 | RoundRect(hdc, Rect.left, Rect.top, Rect.right, Rect.bottom, TextSize.cx / 4, TextSize.cy / 4); 1098 | SetBkMode(hdc, TRANSPARENT); 1099 | SetBkColor(hdc, RGB(0, 0, 0)); // black 1100 | SetTextColor(hdc, font.font_color); 1101 | 1102 | DrawText(hdc, psText, -1, &Rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); 1103 | 1104 | SelectObject(hdc, hpenOld); 1105 | SelectObject(hdc, hbrushOld); 1106 | SelectObject(hdc, hfontOld); 1107 | 1108 | DeleteObject(hFont); 1109 | 1110 | } 1111 | 1112 | /** 1113 | * @brief: return the right physical rect for multiple monitors. 1114 | * 1115 | * @param: HWND hWnd: the transparent window. 1116 | : RECT Rect: the logical source rect. 1117 | * 1118 | * @return: Rect 1119 | */ 1120 | RECT RectForPerMonitorDPI(HWND hWnd, RECT Rect) { 1121 | POINT LeftTop; 1122 | LeftTop.x = Rect.left; 1123 | LeftTop.y = Rect.top; 1124 | LogicalToPhysicalPointForPerMonitorDPI(hWnd, &LeftTop); 1125 | Rect.left = LeftTop.x; 1126 | Rect.top = LeftTop.y; 1127 | 1128 | int WidthMax = GetSystemMetrics(SM_CXVIRTUALSCREEN); 1129 | int HeightMax = GetSystemMetrics(SM_CYVIRTUALSCREEN); 1130 | POINT RightBottom; 1131 | // the RightBottom point might be over the max value. 1132 | RightBottom.x = Rect.right > WidthMax ? WidthMax : Rect.right; 1133 | RightBottom.y = Rect.bottom > HeightMax ? HeightMax : Rect.bottom; 1134 | LogicalToPhysicalPointForPerMonitorDPI(hWnd, &RightBottom); 1135 | Rect.right = RightBottom.x; 1136 | Rect.bottom = RightBottom.y; 1137 | 1138 | return Rect; 1139 | } 1140 | -------------------------------------------------------------------------------- /KeyMouse/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "tag.h" 3 | #include "ctx.h" 4 | 5 | inline void throw_if_fail(HRESULT hr); 6 | HRESULT InitializeUIAutomation(IUIAutomation **ppAutomation); 7 | KeyMouse::Context *GetContext(HWND hMainWnd); 8 | KeyMouse::PElementVec CachingElementsFromWindow(HWND hMainWnd, std::vector TopWindowVec, KeyMouse::PElementVec(*lpEnumFunc)(HWND, HWND)); 9 | void CacheThread(HWND hWnd); 10 | HWND CreateTransparentWindow(HINSTANCE hInstance, HWND hMainWnd); 11 | KeyMouse::PElementVec EnumConditionedElement(HWND hMainWnd, HWND hForeWnd); 12 | KeyMouse::PElementVec EnumConditionedElementForCaching(HWND hMainWnd, HWND hForeWnd); 13 | bool isFocusOnEdit(HWND hMainWnd); 14 | std::string GetLastErrorAsString(); 15 | void DrawTag(HWND hMainWnd, HWND hTransWnd, HDC hdc, POINT point, const TCHAR* psText, KeyMouse::Font font); 16 | RECT RectForPerMonitorDPI(HWND hWnd, RECT Rect); 17 | 18 | KeyMouse::PElementVec EnumTargetWindow(HWND hMainWnd, HWND hForeWnd); 19 | -------------------------------------------------------------------------------- /KeyMouse/wndproc_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "ctx.h" 3 | #include "Resource.h" 4 | #include "utils.h" 5 | #include "hotkey_handler.h" 6 | #include "def.h" 7 | namespace KeyMouse { 8 | HKBinding WndProcHandler::select_hkbinding_[WndProcHandler::SELECT_HKBINDING_NUM] = {}; 9 | HKBinding WndProcHandler::normal_hkbinding_[WndProcHandler::NORMAL_HKBINDING_NUM] = {}; 10 | 11 | WndProcHandler::WndProcHandler() { 12 | // message handlers initializaion. 13 | event_handler_[0].Code = WM_COMMAND; 14 | event_handler_[0].fnPtr = fnWndProc_Command_; 15 | event_handler_[1].Code = WM_TRAY; 16 | event_handler_[1].fnPtr = fnWndProc_Tray_; 17 | event_handler_[2].Code = WM_HOTKEY; 18 | event_handler_[2].fnPtr = fnWndProc_Hotkey_; 19 | event_handler_[3].Code = WM_PAINT; 20 | event_handler_[3].fnPtr = fnWndProc_Paint_; 21 | event_handler_[4].Code = WM_DESTROY; 22 | event_handler_[4].fnPtr = fnWndProc_Destroy_; 23 | 24 | } 25 | 26 | WndProcHandler::~WndProcHandler() { 27 | } 28 | 29 | /** 30 | * @brief: initial struct array of WM_HOTKEY massages' lParam and corresponding 31 | * process functions. 32 | * 33 | * @param: KeybindingMap& keybinding_map 34 | * 35 | * @return: void 36 | */ 37 | void WndProcHandler::InitialHKBinding(KeybindingMap& keybinding_map) { 38 | select_hkbinding_[0].lParam = keybinding_map["escape"].lParam; 39 | select_hkbinding_[0].fnPtr = fnHKProc_Escape_; 40 | select_hkbinding_[1].lParam = keybinding_map["rightClickPrefix"].lParam; 41 | select_hkbinding_[1].fnPtr = fnHKProc_RightClickPrefix_; 42 | select_hkbinding_[2].lParam = keybinding_map["singleClickPrefix"].lParam; 43 | select_hkbinding_[2].fnPtr = fnHKProc_SingleClickPrefix_; 44 | select_hkbinding_[3].lParam = keybinding_map["forceNotUseCache"].lParam; 45 | select_hkbinding_[3].fnPtr = fnHKProc_ForceNotUseCache_; 46 | 47 | normal_hkbinding_[0].lParam = keybinding_map["selectMode"].lParam; 48 | normal_hkbinding_[0].fnPtr = fnHKProc_SelectMode_; 49 | normal_hkbinding_[1].lParam = keybinding_map["toggleEnable"].lParam; 50 | normal_hkbinding_[1].fnPtr = fnHKProc_ToggleEnable_; 51 | normal_hkbinding_[2].lParam = keybinding_map["scrollDown"].lParam; 52 | normal_hkbinding_[2].fnPtr = fnHKProc_Scroll_Down_; 53 | normal_hkbinding_[3].lParam = keybinding_map["scrollUp"].lParam; 54 | normal_hkbinding_[3].fnPtr = fnHKProc_Scroll_Up_; 55 | normal_hkbinding_[4].lParam = keybinding_map["fastSelectMode"].lParam; 56 | normal_hkbinding_[4].fnPtr = fnHKProc_FastSelectMode_; 57 | normal_hkbinding_[5].lParam = keybinding_map["selectModeSingle"].lParam; 58 | normal_hkbinding_[5].fnPtr = fnHKProc_SelectModeSingle_; 59 | 60 | } 61 | 62 | /** 63 | * @brief: the entrance of all windows process message. 64 | * 65 | * @param: UINT msg: the message from main window's WndProc. 66 | * : WndEventArgs& Wea: struct of window event arguments. 67 | * 68 | * @return: LRESULT 69 | */ 70 | LRESULT WndProcHandler::HandlerEntrance(UINT msg, const WndEventArgs& Wea) { 71 | 72 | for(int i = 0; i < EVENTHANDLER_NUM; i++) { 73 | if(event_handler_[i].Code==msg) 74 | { 75 | return (*event_handler_[i].fnPtr)(Wea); 76 | } 77 | } 78 | 79 | return (DefWindowProc(Wea.hWnd, msg, Wea.wParam, Wea.lParam)); 80 | 81 | 82 | } 83 | 84 | LRESULT WndProcHandler::fnWndProc_Command_(const WndEventArgs& Wea) { 85 | int wmId = LOWORD(Wea.wParam); 86 | // Parse the menu selections: 87 | switch (wmId) 88 | { 89 | case IDM_ABOUT: 90 | DialogBox(Wea.hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), Wea.hWnd, About_); 91 | break; 92 | case IDM_EXIT: 93 | DestroyWindow(Wea.hWnd); 94 | break; 95 | default: 96 | return DefWindowProc(Wea.hWnd, WM_COMMAND, Wea.wParam, Wea.lParam); 97 | } 98 | 99 | return 0; 100 | } 101 | LRESULT WndProcHandler::fnWndProc_Tray_(const WndEventArgs& Wea) { 102 | Context *pCtx = GetContext(Wea.hWnd); 103 | 104 | int lmId = LOWORD(Wea.lParam); 105 | switch(lmId) 106 | { 107 | case WM_RBUTTONDOWN: 108 | { 109 | bool EnableState = pCtx->GetEnableState(); 110 | UINT fState; 111 | if(EnableState) 112 | fState = MFS_UNCHECKED; 113 | else 114 | fState = MFS_CHECKED; 115 | MENUITEMINFO MenuItemInfo = { 116 | sizeof(MENUITEMINFO), 117 | MIIM_STATE, 118 | 0, 119 | fState, 120 | 0, 121 | NULL, 122 | NULL, 123 | NULL, 124 | NULL, 125 | NULL, 126 | 0, 127 | 0 128 | }; 129 | HMENU hMenu; 130 | HMENU hSubMenu; 131 | POINT pt; 132 | // Get the current point of cursor. 133 | GetCursorPos(&pt); 134 | hMenu = LoadMenu(Wea.hInst, MAKEINTRESOURCE(IDR_POPUP_MENU)); 135 | 136 | if(!hMenu) 137 | return -1; 138 | 139 | hSubMenu = GetSubMenu(hMenu, 0); 140 | // show check state. 141 | SetMenuItemInfo(hSubMenu, ID_TRAYMENU_DISABLE, false, 142 | &MenuItemInfo); 143 | 144 | SetForegroundWindow(Wea.hWnd); 145 | int cmd = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, 146 | pt.x, pt.y, NULL, Wea.hWnd, NULL); 147 | if(cmd == ID_TRAYMENU_EXIT) 148 | PostMessage(Wea.hWnd, WM_DESTROY, NULL, NULL); 149 | if(cmd == ID_TRAYMENU_ABOUT) 150 | DialogBox(Wea.hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), 151 | Wea.hWnd, About_); 152 | 153 | if(cmd == ID_TRAYMENU_DISABLE) { 154 | if(EnableState) { 155 | pCtx->SetEnableState(false); 156 | UnregCustomHotKey(Wea.hWnd, "selectMode"); 157 | } else { 158 | pCtx->SetEnableState(true); 159 | RegCustomHotKey(Wea.hWnd, "selectMode"); 160 | } 161 | 162 | } 163 | DestroyMenu(hMenu); 164 | } 165 | break; 166 | } 167 | return 0; 168 | } 169 | 170 | LRESULT WndProcHandler::fnWndProc_Hotkey_(const WndEventArgs& Wea) { 171 | Context *pCtx = GetContext(Wea.hWnd); 172 | 173 | auto Mode = pCtx->GetMode(); 174 | if (Mode == Context::NORMAL_MODE) { 175 | for (int i = 0; i < NORMAL_HKBINDING_NUM; i++) { 176 | if (normal_hkbinding_[i].lParam == Wea.lParam) { 177 | return (*normal_hkbinding_[i].fnPtr)(Wea); 178 | } 179 | } 180 | } 181 | else if (Mode == Context::SELECT_MODE) { 182 | for (int i = 0; i < SELECT_HKBINDING_NUM; i++) { 183 | if (select_hkbinding_[i].lParam == Wea.lParam) { 184 | 185 | return (*select_hkbinding_[i].fnPtr)(Wea); 186 | } 187 | } 188 | 189 | // default operation which will consume all hotkeys about tags on the screen. when A <= virtualkey <= Z 190 | WORD VirtualKey = HIWORD(Wea.lParam); 191 | SelectModeHandler_(Wea.hWnd, VirtualKey); 192 | 193 | 194 | } 195 | 196 | return 0; 197 | } 198 | 199 | LRESULT WndProcHandler::fnWndProc_Paint_(const WndEventArgs& Wea) { 200 | PAINTSTRUCT ps; 201 | HDC hdc = BeginPaint(Wea.hWnd, &ps); 202 | // TODO: Add any drawing code that uses hdc here... 203 | EndPaint(Wea.hWnd, &ps); 204 | return 0; 205 | } 206 | 207 | LRESULT WndProcHandler::fnWndProc_Destroy_(const WndEventArgs& Wea) { 208 | PostQuitMessage(0); 209 | return 0; 210 | } 211 | 212 | LRESULT WndProcHandler::fnHKProc_SelectMode_(const WndEventArgs& Wea) { 213 | Context *pCtx = GetContext(Wea.hWnd); 214 | 215 | if (!pCtx->GetEnableState()) 216 | return 0; 217 | auto Mode = pCtx->GetMode(); 218 | // prevent from multiple selecting. 219 | if (Mode == Context::SELECT_MODE) { 220 | EscSelectMode_(Wea.hWnd); 221 | pCtx->SetMode(Context::NORMAL_MODE); 222 | } 223 | CompareBlackList_(); 224 | 225 | pCtx->SetMode(Context::SELECT_MODE); 226 | // this will show all tags on the screen. 227 | CreateTransparentWindow(Wea.hInst, Wea.hWnd); 228 | 229 | if (pCtx->GetProfile().enable_cache) { 230 | RegCustomHotKey(Wea.hWnd, "forceNotUseCache"); 231 | } 232 | RegCustomHotKey(Wea.hWnd, "escape"); 233 | RegisterTagHotKey(Wea.hWnd); 234 | return 0; 235 | } 236 | 237 | LRESULT WndProcHandler::fnHKProc_SelectModeSingle_(const WndEventArgs& Wea) { 238 | fnHKProc_SelectMode_(Wea); 239 | fnHKProc_SingleClickPrefix_(Wea); 240 | return 0; 241 | } 242 | 243 | LRESULT WndProcHandler::fnHKProc_FastSelectMode_(const WndEventArgs& Wea) { 244 | Context *pCtx = GetContext(Wea.hWnd); 245 | KeybindingMap keybinding_map = pCtx->GetKeybindingMap(); 246 | pCtx->SetFastSelectState(true); 247 | PostMessage(Wea.hWnd, WM_HOTKEY, 0, keybinding_map["selectMode"].lParam); 248 | return 0; 249 | } 250 | 251 | LRESULT WndProcHandler::fnHKProc_RightClickPrefix_(const WndEventArgs& Wea) { 252 | Context *pCtx = GetContext(Wea.hWnd); 253 | auto profile = pCtx->GetProfile(); 254 | if (profile.invert_click_type) { 255 | pCtx->SetClickType(Context::SINGLE_LEFT_CLICK); 256 | } 257 | else { 258 | pCtx->SetClickType(Context::SINGLE_RIGHT_CLICK); 259 | } 260 | 261 | if (pCtx->GetFastSelectState()) { 262 | HWND hForeWnd = pCtx->GetForeWindow(); 263 | CComPtr pElement; 264 | pElement = pCtx->GetElement(); 265 | CComPtr pEHTemp = pCtx->GetStructEventHandler(); 266 | if (pElement != nullptr && pEHTemp != nullptr) { 267 | 268 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 269 | HRESULT hr = pAutomation->RemoveAllEventHandlers(); 270 | //HRESULT hr = pAutomation->RemoveStructureChangedEventHandler(pElement, (IUIAutomationStructureChangedEventHandler*)pEHTemp.p); 271 | pCtx->SetFastSelectState(false); 272 | } 273 | } 274 | return 0; 275 | } 276 | LRESULT WndProcHandler::fnHKProc_SingleClickPrefix_(const WndEventArgs& Wea) { 277 | Context *pCtx = GetContext(Wea.hWnd); 278 | auto profile = pCtx->GetProfile(); 279 | if (profile.invert_click_type) { 280 | pCtx->SetClickType(Context::SINGLE_RIGHT_CLICK); 281 | } 282 | else { 283 | pCtx->SetClickType(Context::SINGLE_LEFT_CLICK); 284 | } 285 | return 0; 286 | } 287 | LRESULT WndProcHandler::fnHKProc_Escape_(const WndEventArgs& Wea) { 288 | Context *pCtx = GetContext(Wea.hWnd); 289 | 290 | EscSelectMode_(Wea.hWnd); 291 | if (pCtx->GetFastSelectState()) { 292 | HWND hForeWnd = pCtx->GetForeWindow(); 293 | CComPtr pElement; 294 | pElement = pCtx->GetElement(); 295 | CComPtr pEHTemp = pCtx->GetStructEventHandler(); 296 | if (pElement != nullptr && pEHTemp != nullptr) { 297 | 298 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 299 | HRESULT hr = pAutomation->RemoveAllEventHandlers(); 300 | //HRESULT hr = pAutomation->RemoveStructureChangedEventHandler(pElement, (IUIAutomationStructureChangedEventHandler*)pEHTemp.p); 301 | pCtx->SetFastSelectState(false); 302 | } 303 | } 304 | pCtx->SetMode(Context::NORMAL_MODE); 305 | 306 | return 0; 307 | } 308 | LRESULT WndProcHandler::fnHKProc_ToggleEnable_(const WndEventArgs& Wea) { 309 | Context *pCtx = GetContext(Wea.hWnd); 310 | bool EnableState = pCtx->GetEnableState(); 311 | if(EnableState) { 312 | pCtx->SetEnableState(false); 313 | UnregisterAllHotKey(Wea.hWnd, true); 314 | } else { 315 | pCtx->SetEnableState(true); 316 | RegisterAllHotKey(Wea.hWnd, true); 317 | } 318 | 319 | return 0; 320 | } 321 | 322 | 323 | LRESULT WndProcHandler::fnHKProc_Scroll_Up_(const WndEventArgs& Wea) { 324 | WORD VirtualKey = HIWORD(Wea.lParam); 325 | if (isFocusOnEdit(Wea.hWnd)) { 326 | EditInputForward_(Wea.hWnd, VirtualKey); 327 | } 328 | else { 329 | HWND handle = GetForegroundWindow(); 330 | ScrollHandler_(handle, true); 331 | } 332 | return 0; 333 | } 334 | 335 | LRESULT WndProcHandler::fnHKProc_Scroll_Down_(const WndEventArgs& Wea) { 336 | WORD VirtualKey = HIWORD(Wea.lParam); 337 | if (isFocusOnEdit(Wea.hWnd)) { 338 | EditInputForward_(Wea.hWnd, VirtualKey); 339 | } 340 | else { 341 | HWND handle = GetForegroundWindow(); 342 | ScrollHandler_(handle, false); 343 | } 344 | return 0; 345 | } 346 | 347 | LRESULT WndProcHandler::fnHKProc_ForceNotUseCache_(const WndEventArgs& Wea) { 348 | Context *pCtx = GetContext(Wea.hWnd); 349 | EscSelectMode_(Wea.hWnd); 350 | pCtx->SetEnableCache(false); 351 | fnHKProc_SelectMode_(Wea); 352 | pCtx->SetEnableCache(true); 353 | return 0; 354 | } 355 | 356 | void WndProcHandler::EscSelectMode_(HWND hWnd) { 357 | Context *pCtx = GetContext(hWnd); 358 | 359 | HWND handle = pCtx->GetTransWindow(); 360 | // Enable window drawing. 361 | LockWindowUpdate(NULL); 362 | RedrawWindow(handle, NULL, NULL, 363 | RDW_INVALIDATE | RDW_ALLCHILDREN); 364 | DestroyWindow(handle); 365 | 366 | 367 | if (pCtx->GetProfile().enable_cache) { 368 | UnregCustomHotKey(hWnd, "forceNotUseCache"); 369 | } 370 | UnregCustomHotKey(hWnd, "escape"); 371 | UnregisterTagHotKey(hWnd); 372 | 373 | pCtx->SetCurrentTag(string(TEXT(""))); 374 | pCtx->SetMaxTagLen(0); 375 | pCtx->SetMode(Context::NORMAL_MODE); 376 | auto profile = pCtx->GetProfile(); 377 | // clear click type. 378 | if (profile.invert_click_type) { 379 | pCtx->SetClickType(Context::RIGHT_CLICK); 380 | } 381 | else { 382 | pCtx->SetClickType(Context::LEFT_CLICK); 383 | } 384 | } 385 | 386 | void WndProcHandler::SelectModeHandler_(HWND hWnd, WORD VirtualKey) { 387 | Context *pCtx = GetContext(hWnd); 388 | KeybindingMap keybinding_map = pCtx->GetKeybindingMap(); 389 | // If the VIrtualKey is out of our expectation. 390 | // i.e. VirtualKey is not in A - Z. 391 | if(VirtualKey < 0x41 || VirtualKey > 0x5A) 392 | return; 393 | 394 | TCHAR cInputChar = MapVirtualKey(VirtualKey, MAPVK_VK_TO_CHAR); 395 | size_t iMaxTagLen = pCtx->GetMaxTagLen(); 396 | string szTag = pCtx->GetCurrentTag(); 397 | 398 | const KeyMouse::PTagMap& pTagMap = pCtx->GetTagMap(); 399 | const KeyMouse::PTagMap& pWindowMap = pCtx->GetWindowMap(); 400 | 401 | szTag.append(string(1, cInputChar)); 402 | pCtx->SetCurrentTag(szTag); 403 | HWND TransWindow = pCtx->GetTransWindow(); 404 | 405 | if(pTagMap->find(szTag) != pTagMap->end()) { 406 | KeyMouse::ElementInfo pElement = (*pTagMap)[szTag]; 407 | 408 | RECT Rect = pElement.rect; 409 | // pElement->get_CachedBoundingRectangle(&Rect); 410 | 411 | Rect = RectForPerMonitorDPI(TransWindow, Rect); 412 | if (pCtx->GetClickType() == Context::SINGLE_RIGHT_CLICK) { 413 | RightClick_((Rect.left + Rect.right) / 2, 414 | (Rect.top + Rect.bottom) /2, 1); 415 | } 416 | else if (pCtx->GetClickType() == Context::SINGLE_LEFT_CLICK) { 417 | LeftClick_((Rect.left + Rect.right) / 2, 418 | (Rect.top + Rect.bottom) / 2, 1); 419 | 420 | } 421 | else { 422 | InvokeElement_(pElement, hWnd); 423 | } 424 | 425 | EscSelectMode_(hWnd); 426 | } 427 | else if (pWindowMap->find(szTag) != pWindowMap->end()) { // switch window. 428 | IUIAutomation* pAutomation = pCtx->GetAutomation(); 429 | KeyMouse::ElementInfo pElement = (*pWindowMap)[szTag]; 430 | IUIAutomationElement* pTempElement; 431 | pAutomation->ElementFromHandle(pElement.handle, &pTempElement); 432 | pTempElement->SetFocus(); 433 | pTempElement->Release(); 434 | } 435 | if(iMaxTagLen <= szTag.length()) 436 | EscSelectMode_(hWnd); 437 | } 438 | 439 | void WndProcHandler::ScrollHandler_(HWND hWnd, bool Up) { 440 | 441 | GUITHREADINFO gti; 442 | gti.cbSize = sizeof(GUITHREADINFO); 443 | GetGUIThreadInfo(NULL, >i); 444 | HWND hWindowToScroll = gti.hwndFocus; 445 | RECT r; 446 | GetClientRect(hWnd, &r); 447 | int iDirection = 0; 448 | if(Up) 449 | iDirection = 1; 450 | else 451 | iDirection = -1; 452 | 453 | SendMessage(hWindowToScroll, WM_MOUSEWHEEL, 454 | MAKEWPARAM(0, WHEEL_DELTA * iDirection), 455 | MAKELPARAM(r.right / 2, r.bottom / 2)); 456 | 457 | 458 | } 459 | void WndProcHandler::LeftClick_(int x, int y, int time) 460 | { 461 | const double XSCALEFACTOR = 65535.0 / (GetSystemMetrics(SM_CXSCREEN) - 1); 462 | const double YSCALEFACTOR = 65535.0 / (GetSystemMetrics(SM_CYSCREEN) - 1); 463 | 464 | POINT cursorPos; 465 | GetCursorPos(&cursorPos); 466 | 467 | double cx = cursorPos.x * XSCALEFACTOR; 468 | double cy = cursorPos.y * YSCALEFACTOR; 469 | 470 | double nx = x * XSCALEFACTOR; 471 | double ny = y * YSCALEFACTOR; 472 | 473 | INPUT Input = { 0 }; 474 | Input.type = INPUT_MOUSE; 475 | 476 | Input.mi.dx = (LONG)nx; 477 | Input.mi.dy = (LONG)ny; 478 | 479 | Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP; 480 | 481 | for (int i = 0; i < time; i++) { 482 | SendInput(1, &Input, sizeof(INPUT)); 483 | } 484 | 485 | 486 | Input.mi.dx = (LONG)cx; 487 | Input.mi.dy = (LONG)cy; 488 | 489 | Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; 490 | 491 | SendInput(1, &Input, sizeof(INPUT)); 492 | } 493 | 494 | void WndProcHandler::RightClick_(int x, int y, int time) 495 | { 496 | const double XSCALEFACTOR = 65535.0 / (GetSystemMetrics(SM_CXSCREEN) - 1); 497 | const double YSCALEFACTOR = 65535.0 / (GetSystemMetrics(SM_CYSCREEN) - 1); 498 | 499 | POINT cursorPos; 500 | GetCursorPos(&cursorPos); 501 | 502 | double cx = cursorPos.x * XSCALEFACTOR; 503 | double cy = cursorPos.y * YSCALEFACTOR; 504 | 505 | double nx = x * XSCALEFACTOR; 506 | double ny = y * YSCALEFACTOR; 507 | 508 | INPUT Input = { 0 }; 509 | Input.type = INPUT_MOUSE; 510 | 511 | Input.mi.dx = (LONG)nx; 512 | Input.mi.dy = (LONG)ny; 513 | 514 | Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP; 515 | 516 | for (int i = 0; i < time; i++) { 517 | SendInput(1, &Input, sizeof(INPUT)); 518 | } 519 | 520 | 521 | Input.mi.dx = (LONG)cx; 522 | Input.mi.dy = (LONG)cy; 523 | 524 | Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; 525 | 526 | SendInput(1, &Input, sizeof(INPUT)); 527 | } 528 | void WndProcHandler::InvokeElement_( 529 | KeyMouse::ElementInfo &pElement, 530 | HWND hWnd) { 531 | try { 532 | CONTROLTYPEID iControlType = pElement.control_type; 533 | Context *pCtx = GetContext(hWnd); 534 | HWND TransWindow = pCtx->GetTransWindow(); 535 | 536 | if(iControlType == UIA_ButtonControlTypeId || 537 | iControlType == UIA_HyperlinkControlTypeId) { 538 | 539 | // Sometimes the ClickablePoint is not actually clickable, but 540 | // bClickable equals 1. 541 | 542 | RECT Rect = pElement.rect; 543 | 544 | Rect = RectForPerMonitorDPI(TransWindow, Rect); 545 | if (pCtx->GetClickType() == Context::LEFT_CLICK) { 546 | LeftClick_((Rect.left + Rect.right) / 2, 547 | (Rect.top + Rect.bottom) / 2, 1); 548 | } 549 | else if (pCtx->GetClickType() == Context::RIGHT_CLICK) { 550 | RightClick_((Rect.left + Rect.right) / 2, 551 | (Rect.top + Rect.bottom) / 2, 1); 552 | } 553 | } else { 554 | RECT Rect = pElement.rect; 555 | Rect = RectForPerMonitorDPI(TransWindow, Rect); 556 | if (pCtx->GetClickType() == Context::LEFT_CLICK) { 557 | LeftClick_((Rect.left + Rect.right) / 2, 558 | (Rect.top + Rect.bottom) / 2, 2); 559 | } 560 | else if (pCtx->GetClickType() == Context::RIGHT_CLICK) { 561 | RightClick_((Rect.left + Rect.right) / 2, 562 | (Rect.top + Rect.bottom) / 2, 2); 563 | } 564 | } 565 | } 566 | catch (_com_error err) { 567 | } 568 | 569 | } 570 | // to avoid conflict when facus on text edit field. 571 | void WndProcHandler::EditInputForward_(HWND hWnd, WORD VirtualKey) { 572 | UnregCustomHotKey(hWnd, "scrollDown"); 573 | UnregCustomHotKey(hWnd, "scrollUp"); 574 | INPUT ip; 575 | ip.type = INPUT_KEYBOARD; 576 | ip.ki.wScan = 0; 577 | ip.ki.time = 0; 578 | ip.ki.dwExtraInfo = 0; 579 | ip.ki.wVk = VirtualKey; 580 | ip.ki.dwFlags = 0; //Prepares key down 581 | SendInput(1, &ip, sizeof(INPUT)); //Key down 582 | ip.ki.dwFlags = KEYEVENTF_KEYUP; //Prepares key up 583 | SendInput(1, &ip, sizeof(INPUT)); //Key up 584 | RegCustomHotKey(hWnd, "scrollDown"); 585 | RegCustomHotKey(hWnd, "scrollUp"); 586 | } 587 | 588 | bool WndProcHandler::CompareBlackList_() { 589 | HWND handle = GetForegroundWindow(); 590 | DWORD pid; 591 | GetWindowThreadProcessId(handle, &pid); 592 | HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ , FALSE, pid); 593 | TCHAR lpFileName[100] = {0}; 594 | GetModuleFileNameEx( 595 | hProc, 596 | NULL, 597 | lpFileName, 598 | 100 599 | ); 600 | 601 | CloseHandle(hProc); 602 | return true; 603 | } 604 | 605 | // Message handler for about box. 606 | INT_PTR CALLBACK WndProcHandler::About_( 607 | HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam 608 | ) 609 | { 610 | UNREFERENCED_PARAMETER(lParam); 611 | switch (message) 612 | { 613 | case WM_INITDIALOG: 614 | return (INT_PTR)TRUE; 615 | 616 | case WM_COMMAND: 617 | if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 618 | { 619 | EndDialog(hDlg, LOWORD(wParam)); 620 | return (INT_PTR)TRUE; 621 | } 622 | break; 623 | } 624 | return (INT_PTR)FALSE; 625 | } 626 | } 627 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 iscooool 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KeyMouse 2 | **KeyMouse** is a program which works like the chrome plugin **vimium**, but is designed for other programs(like windows file browser). 3 | 4 | ![demo](https://i.imgur.com/HxaxNYu.gif) 5 | 6 | ## Installation 7 | Download the zip file in release page and unzip it. 8 | 9 | ## Usage 10 | 11 | - Select mode: Press Alt + ; to enter select mode, then press tag to select item. If you hit `rightClickPrefix` before hitting tag, KeyMouse will simulate right click instead. 12 | - Fast Select mode: Alt + J to enter fast select mode. This mode may fail in some situations. 13 | - Esc Select mode: Press Esc to esc. 14 | - Disable: Press Alt + [ to enable/disable the program. 15 | - Scroll down: Press Alt + J. 16 | - Scroll up: Press Alt + K. 17 | - Force not use cache: When `enableCache` is `true` and you're in `select mode`. Press space to force not use cache. 18 | 19 | > if KeyMouse doesn't work, try to run as Administrator. 20 | 21 | ## Configuration 22 | **KeyMouse** supports configuration for hot keys and profile.you can put your configuration in `config.json`(put in the path the same as `KeyMouse.exe`). there is a typical configuration: 23 | ``` 24 | { 25 | "profile": 26 | { 27 | "runOnStartUp": true, 28 | "backgroundColor": "#CCFFCC", 29 | "fontColor": "#000000", 30 | "fontSize": 9, 31 | "font": "Arial Rounded MT Bold", 32 | "windowBkgdColor": "#CCBBAA", 33 | "windowFontSize": 11, 34 | "opacity": 70, 35 | "invertClickType": false, 36 | "onlyForeWindow": true, 37 | "enableWindowSwitching": true, 38 | "enableCache": false 39 | }, 40 | "keybindings": 41 | { 42 | "toggleEnable": "alt+[", 43 | "scrollUp": "alt+k", 44 | "scrollDown": "alt+j", 45 | "selectMode": "alt+;", 46 | "escape": "esc", 47 | "fastSelectMode": "alt+i", 48 | "rightClickPrefix": "shift+a", 49 | "singleClickPrefix": "shift+s", 50 | "forceNotUseCache": "space", 51 | "selectModeSingle": "disabled" 52 | } 53 | } 54 | ``` 55 | ### options 56 | The options of profile is listed below: 57 | 58 | | Options | Type | Default | Description| 59 | | ------------- | ------------- | ----------- |----------| 60 | | runOnStartUp | bool | `false` |When this option is `true`, the app will be launched when windows starts up.| 61 | | backgroundColor| string| `#CCFFCC` |The background color of hints. Use RGB format| 62 | | fontColor| string| `#000000` |The font color of hint. Use RGB format| 63 | | fontSize| int| `10`| The font size of hint. | 64 | | font| string| `Arial Rounded MT Bold`| The font type of hint. You can find the font name supported by your system through **Settings->Personalization->fonts**. Make sure to use the full name of font.| 65 | | windowBkgdColor| string| `#CCBBAA`| This option is for switch window tag. Indicate the background color of switch window tag.| 66 | | windowFontColor| string| `#000000`| This option is for switch window tag. Indecate tor font color of switch window tag.| 67 | | windowFontSize| int| `12`| This option is for switch window tag.| 68 | | windowFont| string| `Arial Rounded MT Bold`| this option is for switch window tag. Indecate the font type of switch window tag.| 69 | | opacity| int| `100`| The opacity of tags. value is from 0-100.| 70 | | invertClickType| bool| `false`| When it's true, KeyMouse will use right click as main click.| 71 | | onlyForeWindow| bool| `true`| When using multiple monitors, only enumerate hints on foreground window. If setting to false, It will try to enumerate windows on other monitors(might be slow.)| 72 | | enableWindowSwitching| bool| `true`| Window switching feature may cause a high delay on some Windows versions.| 73 | | enableCache| bool| `false`| (experimental) Try to cache the target window's hints when it's possible. (target window may be frozen if it's hint-intensive.)| 74 | 75 | The hot keys support `alt`, `shift`, `ctrl`, `win` and most keys on the keyboard. Please use lowercase and use `+` to connect different keys. some typical keybings: `alt+j`, `shift+alt+j`, `f11`. You can also use `disabled` to disable hot key. 76 | 77 | The option of hot keys is listed below: 78 | 79 | | Options | Type | Default | Description| 80 | | ------------- | ------------- | ----------- |----------| 81 | | toggleEnable| string| `alt+[`| Enable/Disable this app.| 82 | | scrollUp| string| `k`| Scroll up.| 83 | | scrollDown| string| `j`| Scroll down.| 84 | | selectMode| string| `alt+;`| Enter select mode.| 85 | | fastSelectMode| string| `alt+j`| Enter fast select mode. the difference between select mode and fast select mode is that FSM will continually enter select mode. may fail in some situations.| 86 | | escape| string| `esc`| Leave select mode.| 87 | | rightClickPrefix| string| `shift+a`| When you're in select mode. Hitting `rightClickPrefix` before hitting tag will simulate right click instead.| 88 | | singleClickPrefix| string| `shift+s`| When you're in select mode. Hitting `singleClickPrefix` before hitting tag will simulate single click instead.| 89 | | forceNotUseCache| string| `space`| When `enableCache` is `true` and you're in select mode. Hitting `forceNotUseCache` will retrieve hints directly instead of using cache.| 90 | | selectModeSingle| string| `disabled`| Select Mode using single left click.| 91 | 92 | 93 | ## Build 94 | ### Prerequisities 95 | 1. require json lib from [nlohmann/json](https://github.com/nlohmann/json). 96 | 97 | you can either add `--recurse-submodules` option when cloning this repo or get a copy from `nlohmann/json/single_include/nlohmann/json.hpp` to `/KeyMouse/KeyMouse/json/single_include/nlohmann/json.hpp`. 98 | ### Step 99 | 1. run `git clone --recurse-submodules https://github.com/iscooool/KeyMouse.git`. 100 | 2. Install Visual Studio and Import this project. 101 | 3. Ensure to build this project in x64. 102 | --------------------------------------------------------------------------------