-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.cpp
More file actions
267 lines (230 loc) · 7.83 KB
/
Copy pathmain.cpp
File metadata and controls
267 lines (230 loc) · 7.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#pragma comment(linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")
// #include "MainWindowW32Wrap.hpp"
// run_mainwindow_use_win32_wrap(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
// #include "MainWindowImguiDx9.hpp"
// #include "MainWindowImguiDx11.hpp"
#define TRAY_WINAPI 1
#include "CapsHotkey.hpp"
#include "HkeyUser.hpp"
#include "res/resource.h"
#include "str.hpp"
#include "tray.hpp"
#include "use_win32.hpp"
#include <algorithm>
#include <filesystem>
#include <fstream>
std::wstring appid_{ L"Capslock Hotkey" };
std::wstring subkey_{ L"Software\\Microsoft\\Windows\\CurrentVersion\\Run" };
static auto simulate_mouse_down()
{
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, -WHEEL_DELTA * 3, 0);
}
static auto simulate_mouse_up()
{
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, WHEEL_DELTA * 3, 0);
}
std::wstring get_app_path()
{
std::wstring path(MAX_PATH, 0);
GetModuleFileName(NULL, path.data(), (DWORD)path.size());
return path.c_str();
};
std::filesystem::path get_app_dir()
{
return std::filesystem::path(get_app_path()).parent_path();
}
std::filesystem::path get_config_path()
{
return get_app_dir() / L"caps.cfg";
}
void register_hotkeys_from_text(CapsHotkey &hotkey, std::string text)
{
if (text.size() >= 3 && (uint8_t)text[0] == 0xEF && (uint8_t)text[1] == 0xBB && (uint8_t)text[2] == 0xBF)
{
text.erase(0, 3);
}
std::replace(text.begin(), text.end(), '\r', '\n');
auto parts = str::split(text, "\n", false);
for (auto &&it : parts)
{
hotkey.register_hook(str::trim_copy(it));
}
}
void load_hotkey_from_cfg(CapsHotkey &hotkey, const std::filesystem::path &config_path)
{
// load default config
if (auto res = FindResource(NULL, MAKEINTRESOURCE(IDR_CFG2), TEXT("CFG")); res != NULL)
{
if (auto rr = LoadResource(NULL, res); rr != NULL)
{
if (auto locked = (char *)LockResource(rr); locked != nullptr)
{
std::string text(locked, SizeofResource(NULL, res));
register_hotkeys_from_text(hotkey, text);
}
}
}
// load user config
{
std::ifstream in(config_path, std::ios::binary);
std::string text({ std::istreambuf_iterator<char>(in), {} });
register_hotkeys_from_text(hotkey, text);
}
}
void register_builtin_hotkeys(CapsHotkey &hotkey, const KeyHookFunc &quit)
{
hotkey.register_hook(VK_OEM_7, simulate_mouse_up, "Mouse up");
hotkey.register_hook(VK_OEM_1, simulate_mouse_down, "Mouse down");
hotkey.register_hook(char2key('q'), quit, "Quit CapsHotkey");
}
void reload_hotkeys(CapsHotkey &hotkey, const KeyHookFunc &quit, const std::filesystem::path &config_path)
{
hotkey.clear_hooks();
register_builtin_hotkeys(hotkey, quit);
load_hotkey_from_cfg(hotkey, config_path);
}
struct ConfigWatchContext
{
CapsHotkey *hotkey{ nullptr };
KeyHookFunc quit;
std::filesystem::path config_path;
HANDLE stop_event{ NULL };
};
DWORD WINAPI watch_config_file(LPVOID param)
{
auto *context = (ConfigWatchContext *)param;
auto dir = context->config_path.parent_path().wstring();
HANDLE hDir = CreateFile(
dir.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hDir == INVALID_HANDLE_VALUE)
return 0;
char buffer[1024];
OVERLAPPED overlapped{};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlapped.hEvent == NULL)
{
CloseHandle(hDir);
return 0;
}
while (true)
{
ResetEvent(overlapped.hEvent);
DWORD bytesReturned = 0;
if (!ReadDirectoryChangesW(hDir, buffer, sizeof(buffer), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, NULL, &overlapped, NULL))
{
break;
}
HANDLE handles[]{ overlapped.hEvent, context->stop_event };
auto wait_result = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
if (wait_result == WAIT_OBJECT_0 + 1)
{
CancelIo(hDir);
break;
}
if (wait_result != WAIT_OBJECT_0 || !GetOverlappedResult(hDir, &overlapped, &bytesReturned, FALSE) || bytesReturned == 0)
{
break;
}
auto *info = (FILE_NOTIFY_INFORMATION *)buffer;
auto watched_filename = context->config_path.filename().wstring();
bool should_reload = false;
while (true)
{
std::wstring filename(info->FileName, info->FileNameLength / sizeof(WCHAR));
if (CompareStringOrdinal(filename.c_str(), -1, watched_filename.c_str(), -1, TRUE) == CSTR_EQUAL)
{
should_reload = true;
}
if (info->NextEntryOffset == 0)
{
break;
}
info = (FILE_NOTIFY_INFORMATION *)((BYTE *)info + info->NextEntryOffset);
}
if (should_reload)
{
Sleep(100); // Wait for file write to complete
reload_hotkeys(*context->hotkey, context->quit, context->config_path);
}
}
CloseHandle(overlapped.hEvent);
CloseHandle(hDir);
return 0;
}
bool is_auto_run()
{
HkeyUser hku(subkey_.c_str(), KEY_READ);
auto left = str::trim_copy(hku.read(appid_));
auto right = str::trim_copy(get_app_path());
if (left.empty())
return false;
return left.find(right) != std::wstring::npos || right.find(left) != std::wstring::npos;
}
void toggle_autorun_enabled()
{
HkeyUser hku(subkey_.c_str(), KEY_WRITE);
auto need_set = !is_auto_run();
hku.remove(std::wstring_view{ appid_ });
if (need_set)
{
hku.write(appid_, get_app_path());
}
}
void run_tray_loop(HICON icon)
{
tray_icon tray;
auto quit = [&tray] { tray.quit(); };
tray.initialize(icon, {
tray_menu{ L"v2.11" },
tray_menu{ L"-" },
tray_menu{ L"Autorun",
[&tray](auto &item) {
toggle_autorun_enabled();
item.checked = is_auto_run();
tray.update_tray();
},
is_auto_run() },
tray_menu{ L"Quit", [&quit](auto &) { quit(); } },
});
CapsHotkey app;
auto config_path = get_config_path();
reload_hotkeys(app, quit, config_path);
// Show invalid config warnings
if (!app.get_invalid_configs().empty())
{
std::wstring msg = L"Invalid hotkey configurations found:\n\n";
for (auto &&cfg : app.get_invalid_configs())
{
msg += str::wide(cfg) + L"\n";
}
MessageBox(NULL, msg.c_str(), L"CapsHotkey Configuration Warning", MB_OK | MB_ICONWARNING);
}
// Start config file watcher thread
ConfigWatchContext watch_context{ &app, quit, config_path, CreateEvent(NULL, TRUE, FALSE, NULL) };
HANDLE watcher = NULL;
if (watch_context.stop_event != NULL)
{
watcher = CreateThread(NULL, 0, watch_config_file, &watch_context, 0, NULL);
}
exec_main_loop();
if (watch_context.stop_event != NULL)
{
SetEvent(watch_context.stop_event);
if (watcher != NULL)
{
WaitForSingleObject(watcher, INFINITE);
CloseHandle(watcher);
}
CloseHandle(watch_context.stop_event);
}
}
int WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
// run_imgui_loop(hInstance);
auto icon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON2));
run_tray_loop(icon);
return EXIT_SUCCESS;
}