Skip to content

Commit a883d2e

Browse files
feat(js): impl current_process_name, current_process_path, and keyboard/mouse simulation
1 parent 92fd283 commit a883d2e

File tree

5 files changed

+291
-62
lines changed

5 files changed

+291
-62
lines changed

src/shell/script/binding_qjs.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,8 @@ template<> struct js_bind<mb_shell::js::breeze> {
11411141
.static_fun<&mb_shell::js::breeze::user_language>("user_language")
11421142
.static_fun<&mb_shell::js::breeze::set_can_reload_js>("set_can_reload_js")
11431143
.static_fun<&mb_shell::js::breeze::should_show_settings_button>("should_show_settings_button")
1144+
.static_fun<&mb_shell::js::breeze::current_process_name>("current_process_name")
1145+
.static_fun<&mb_shell::js::breeze::current_process_path>("current_process_path")
11441146
;
11451147
}
11461148
};
@@ -1175,6 +1177,15 @@ template<> struct js_bind<mb_shell::js::win32> {
11751177
.static_fun<&mb_shell::js::win32::reg_set_string>("reg_set_string")
11761178
.static_fun<&mb_shell::js::win32::reg_set_qword>("reg_set_qword")
11771179
.static_fun<&mb_shell::js::win32::is_key_down>("is_key_down")
1180+
.static_fun<&mb_shell::js::win32::simulate_hotkeys>("simulate_hotkeys")
1181+
.static_fun<&mb_shell::js::win32::simulate_key_press>("simulate_key_press")
1182+
.static_fun<&mb_shell::js::win32::simulate_key_down>("simulate_key_down")
1183+
.static_fun<&mb_shell::js::win32::simulate_key_up>("simulate_key_up")
1184+
.static_fun<&mb_shell::js::win32::simulate_text_input>("simulate_text_input")
1185+
.static_fun<&mb_shell::js::win32::simulate_mouse_move>("simulate_mouse_move")
1186+
.static_fun<&mb_shell::js::win32::simulate_mouse_click>("simulate_mouse_click")
1187+
.static_fun<&mb_shell::js::win32::simulate_mouse_down>("simulate_mouse_down")
1188+
.static_fun<&mb_shell::js::win32::simulate_mouse_up>("simulate_mouse_up")
11781189
;
11791190
}
11801191
};

src/shell/script/binding_types.cc

Lines changed: 194 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "shell/contextmenu/hooks.h"
2525

2626
#include "script.h"
27+
#include "shell/utils.h"
2728
#include "winhttp.h"
2829

2930
#include <shellapi.h>
@@ -1235,74 +1236,185 @@ void win32::reg_set_qword(std::string key, std::string name, int64_t value) {
12351236

12361237
RegCloseKey(hKey);
12371238
}
1239+
1240+
1241+
static WORD get_scancode(std::string key) {
1242+
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
1243+
1244+
static const std::unordered_map<std::string, WORD> scancodes = {
1245+
{"escape", 0x01}, {"1", 0x02}, {"2", 0x03}, {"3", 0x04}, {"4", 0x05},
1246+
{"5", 0x06}, {"6", 0x07}, {"7", 0x08}, {"8", 0x09}, {"9", 0x0A},
1247+
{"0", 0x0B}, {"minus", 0x0C}, {"equal", 0x0D}, {"backspace", 0x0E},
1248+
{"tab", 0x0F}, {"q", 0x10}, {"w", 0x11}, {"e", 0x12}, {"r", 0x13},
1249+
{"t", 0x14}, {"y", 0x15}, {"u", 0x16}, {"i", 0x17}, {"o", 0x18},
1250+
{"p", 0x19}, {"bracket_left", 0x1A}, {"bracket_right", 0x1B}, {"enter", 0x1C},
1251+
{"ctrl", 0x1D}, {"a", 0x1E}, {"s", 0x1F}, {"d", 0x20}, {"f", 0x21},
1252+
{"g", 0x22}, {"h", 0x23}, {"j", 0x24}, {"k", 0x25}, {"l", 0x26},
1253+
{"semicolon", 0x27}, {"quote", 0x28}, {"backtick", 0x29}, {"shift", 0x2A},
1254+
{"backslash", 0x2B}, {"z", 0x2C}, {"x", 0x2D}, {"c", 0x2E}, {"v", 0x2F},
1255+
{"b", 0x30}, {"n", 0x31}, {"m", 0x32}, {"comma", 0x33}, {"period", 0x34},
1256+
{"slash", 0x35}, {"alt", 0x38}, {"space", 0x39}, {"capslock", 0x3A},
1257+
{"f1", 0x3B}, {"f2", 0x3C}, {"f3", 0x3D}, {"f4", 0x3E}, {"f5", 0x3F},
1258+
{"f6", 0x40}, {"f7", 0x41}, {"f8", 0x42}, {"f9", 0x43}, {"f10", 0x44},
1259+
{"numlock", 0x45}, {"scrolllock", 0x46}, {"home", 0x47}, {"up", 0x48},
1260+
{"pageup", 0x49}, {"minus_pad", 0x4A}, {"left", 0x4B}, {"center", 0x4C},
1261+
{"right", 0x4D}, {"plus_pad", 0x4E}, {"end", 0x4F}, {"down", 0x50},
1262+
{"pagedown", 0x51}, {"insert", 0x52}, {"delete", 0x53}, {"f11", 0x57},
1263+
{"f12", 0x58}, {"win", 0xE05B}, {"context", 0xE05D}, {"printscreen", 0xE037},
1264+
{"pause", 0xE11D45}
1265+
};
1266+
1267+
auto it = scancodes.find(key);
1268+
if (it != scancodes.end()) {
1269+
return it->second;
1270+
}
1271+
return 0;
1272+
}
1273+
12381274
bool win32::is_key_down(std::string key) {
12391275
auto key_lower =
12401276
key | std::views::transform(::tolower) | std::ranges::to<std::string>();
12411277

1242-
constexpr auto key_map =
1243-
std::to_array<std::pair<const char *, int>>({{"ctrl", VK_CONTROL},
1244-
{"shift", VK_SHIFT},
1245-
{"alt", VK_MENU},
1246-
{"space", VK_SPACE},
1247-
{"enter", VK_RETURN},
1248-
{"esc", VK_ESCAPE},
1249-
{"tab", VK_TAB},
1250-
{"backspace", VK_BACK},
1251-
{"delete", VK_DELETE},
1252-
{"left", VK_LEFT},
1253-
{"right", VK_RIGHT},
1254-
{"up", VK_UP},
1255-
{"down", VK_DOWN},
1256-
{"f1", VK_F1},
1257-
{"f2", VK_F2},
1258-
{"f3", VK_F3},
1259-
{"f4", VK_F4},
1260-
{"f5", VK_F5},
1261-
{"f6", VK_F6},
1262-
{"f7", VK_F7},
1263-
{"f8", VK_F8},
1264-
{"f9", VK_F9},
1265-
{"f10", VK_F10},
1266-
{"f11", VK_F11},
1267-
{"f12", VK_F12},
1268-
{"a", 'A'},
1269-
{"b", 'B'},
1270-
{"c", 'C'},
1271-
{"d", 'D'},
1272-
{"e", 'E'},
1273-
{"f", 'F'},
1274-
{"g", 'G'},
1275-
{"h", 'H'},
1276-
{"i", 'I'},
1277-
{"j", 'J'},
1278-
{"k", 'K'},
1279-
{"l", 'L'},
1280-
{"m", 'M'},
1281-
{"n", 'N'},
1282-
{"o", 'O'},
1283-
{"p", 'P'},
1284-
{"q", 'Q'},
1285-
{"r", 'R'},
1286-
{"s", 'S'},
1287-
{"t", 'T'},
1288-
{"u", 'U'},
1289-
{"v", 'V'},
1290-
{"w", 'W'},
1291-
{"x", 'X'},
1292-
{"y", 'Y'},
1293-
{"z", 'Z'}});
1294-
1295-
auto keycode = std::ranges::find_if(key_map, [key_lower](const auto &pair) {
1296-
return key_lower == pair.first;
1297-
});
1298-
1299-
if (keycode != key_map.end()) {
1300-
return GetAsyncKeyState(keycode->second) & 0x8000;
1278+
WORD scancode = get_scancode(key_lower);
1279+
if (scancode != 0) {
1280+
SHORT state = GetAsyncKeyState(MapVirtualKeyW(scancode & 0xFF, MAPVK_VSC_TO_VK));
1281+
return (state & 0x8000) != 0;
13011282
}
13021283

13031284
return false;
13041285
}
13051286

1287+
1288+
static bool is_extended_key(WORD scancode) {
1289+
return (scancode & 0xFF00) == 0xE000 || (scancode & 0xFF0000) == 0xE10000;
1290+
}
1291+
1292+
void win32::simulate_hotkeys(std::vector<std::string> keys) {
1293+
if (keys.empty()) return;
1294+
1295+
for (const auto& key : keys) {
1296+
simulate_key_down(key);
1297+
}
1298+
for (auto it = keys.rbegin(); it != keys.rend(); ++it) {
1299+
simulate_key_up(*it);
1300+
}
1301+
}
1302+
1303+
void win32::simulate_key_press(std::string key) {
1304+
simulate_key_down(key);
1305+
simulate_key_up(key);
1306+
}
1307+
1308+
void win32::simulate_key_down(std::string key) {
1309+
WORD sc = get_scancode(key);
1310+
if (sc == 0) return;
1311+
1312+
INPUT input = {0};
1313+
input.type = INPUT_KEYBOARD;
1314+
input.ki.wScan = sc & 0xFF;
1315+
input.ki.dwFlags = KEYEVENTF_SCANCODE;
1316+
1317+
if (is_extended_key(sc)) {
1318+
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
1319+
}
1320+
1321+
SendInput(1, &input, sizeof(INPUT));
1322+
}
1323+
1324+
void win32::simulate_key_up(std::string key) {
1325+
WORD sc = get_scancode(key);
1326+
if (sc == 0) return;
1327+
1328+
INPUT input = {0};
1329+
input.type = INPUT_KEYBOARD;
1330+
input.ki.wScan = sc & 0xFF;
1331+
input.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
1332+
1333+
if (is_extended_key(sc)) {
1334+
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
1335+
}
1336+
1337+
SendInput(1, &input, sizeof(INPUT));
1338+
}
1339+
1340+
void win32::simulate_text_input(std::string text) {
1341+
std::wstring wtext = utf8_to_wstring(text);
1342+
1343+
for (wchar_t c : wtext) {
1344+
INPUT input[2] = {0};
1345+
1346+
input[0].type = INPUT_KEYBOARD;
1347+
input[0].ki.wScan = c;
1348+
input[0].ki.dwFlags = KEYEVENTF_UNICODE;
1349+
1350+
input[1].type = INPUT_KEYBOARD;
1351+
input[1].ki.wScan = c;
1352+
input[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
1353+
1354+
SendInput(2, input, sizeof(INPUT));
1355+
}
1356+
}
1357+
1358+
void win32::simulate_mouse_move(int x, int y) {
1359+
INPUT input = {0};
1360+
input.type = INPUT_MOUSE;
1361+
input.mi.dx = (x * 65535) / (GetSystemMetrics(SM_CXSCREEN) - 1);
1362+
input.mi.dy = (y * 65535) / (GetSystemMetrics(SM_CYSCREEN) - 1);
1363+
input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
1364+
SendInput(1, &input, sizeof(INPUT));
1365+
}
1366+
1367+
static void get_mouse_flags(std::string button, bool down, DWORD& flags, DWORD& data) {
1368+
std::transform(button.begin(), button.end(), button.begin(), ::tolower);
1369+
flags = 0;
1370+
data = 0;
1371+
1372+
if (button == "left") {
1373+
flags = down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
1374+
} else if (button == "right") {
1375+
flags = down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
1376+
} else if (button == "middle") {
1377+
flags = down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
1378+
} else if (button == "x1") {
1379+
flags = down ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
1380+
data = XBUTTON1;
1381+
} else if (button == "x2") {
1382+
flags = down ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
1383+
data = XBUTTON2;
1384+
}
1385+
}
1386+
1387+
void win32::simulate_mouse_click(std::string button) {
1388+
simulate_mouse_down(button);
1389+
simulate_mouse_up(button);
1390+
}
1391+
1392+
void win32::simulate_mouse_down(std::string button) {
1393+
DWORD flags, data;
1394+
get_mouse_flags(button, true, flags, data);
1395+
1396+
if (flags == 0) return;
1397+
1398+
INPUT input = {0};
1399+
input.type = INPUT_MOUSE;
1400+
input.mi.dwFlags = flags;
1401+
input.mi.mouseData = data;
1402+
SendInput(1, &input, sizeof(INPUT));
1403+
}
1404+
1405+
void win32::simulate_mouse_up(std::string button) {
1406+
DWORD flags, data;
1407+
get_mouse_flags(button, false, flags, data);
1408+
1409+
if (flags == 0) return;
1410+
1411+
INPUT input = {0};
1412+
input.type = INPUT_MOUSE;
1413+
input.mi.dwFlags = flags;
1414+
input.mi.mouseData = data;
1415+
SendInput(1, &input, sizeof(INPUT));
1416+
}
1417+
13061418
struct WinToastEventHandler : public IWinToastHandler {
13071419
std::function<void(int)> on_activate = [](int) {};
13081420
std::function<void(WinToastDismissalReason)> on_dismiss =
@@ -1459,11 +1571,32 @@ void screenside_button_controller::add_button(std::string icon_svg,
14591571
if ($menu.expired())
14601572
return;
14611573
auto menu = $menu.lock();
1462-
auto button = std::make_shared<screenside_button_group_widget::button_widget>(icon_svg);
1574+
auto button =
1575+
std::make_shared<screenside_button_group_widget::button_widget>(
1576+
icon_svg);
14631577
button->on_click = on_click;
1464-
if(auto group = menu->get_child<screenside_button_group_widget>()) {
1578+
if (auto group = menu->get_child<screenside_button_group_widget>()) {
14651579
group->children.push_back(button);
14661580
group->children_dirty = true;
14671581
}
14681582
}
1583+
std::string breeze::current_process_name() {
1584+
static std::string process_name = []() {
1585+
wchar_t buffer[MAX_PATH];
1586+
GetModuleFileNameW(NULL, buffer, MAX_PATH);
1587+
return wstring_to_utf8(
1588+
std::filesystem::path(buffer).filename().wstring());
1589+
}();
1590+
1591+
return process_name;
1592+
}
1593+
std::string breeze::current_process_path() {
1594+
static std::string process_path = []() {
1595+
wchar_t buffer[MAX_PATH];
1596+
GetModuleFileNameW(NULL, buffer, MAX_PATH);
1597+
return wstring_to_utf8(std::filesystem::path(buffer).wstring());
1598+
}();
1599+
1600+
return process_path;
1601+
}
14691602
} // namespace mb_shell::js

src/shell/script/binding_types.d.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,8 @@ export class breeze {
10761076
*/
10771077
static set_can_reload_js(can: boolean): void
10781078
static should_show_settings_button(): boolean
1079+
static current_process_name(): string
1080+
static current_process_path(): string
10791081
}
10801082
export class win32 {
10811083
/**
@@ -1165,6 +1167,67 @@ export class win32 {
11651167
* @returns boolean
11661168
*/
11671169
static is_key_down(key: string): boolean
1170+
/**
1171+
* Support keys:
1172+
* "ctrl", "shift", "alt", "win", "A" - "Z", "0" - "9", "f1" - "f12",
1173+
* "up", "down", "left", "right", "enter", "space", "tab", "esc", "backspace",
1174+
* "delete", "home", "end", "pageup", "pagedown", "insert", "capslock", "numlock",
1175+
* "scrolllock", "printscreen", "pause", "minus", "equal", "comma", "period",
1176+
* "slash", "backslash", "semicolon", "quote", "bracket_left", "bracket_right"
1177+
* case insensitive
1178+
* @param keys: Array<string>
1179+
* @returns void
1180+
*/
1181+
static simulate_hotkeys(keys: Array<string>): void
1182+
/**
1183+
*
1184+
* @param key: string
1185+
* @returns void
1186+
*/
1187+
static simulate_key_press(key: string): void
1188+
/**
1189+
*
1190+
* @param key: string
1191+
* @returns void
1192+
*/
1193+
static simulate_key_down(key: string): void
1194+
/**
1195+
*
1196+
* @param key: string
1197+
* @returns void
1198+
*/
1199+
static simulate_key_up(key: string): void
1200+
/**
1201+
*
1202+
* @param text: string
1203+
* @returns void
1204+
*/
1205+
static simulate_text_input(text: string): void
1206+
/**
1207+
*
1208+
* @param x: number
1209+
* @param y: number
1210+
* @returns void
1211+
*/
1212+
static simulate_mouse_move(x: number, y: number): void
1213+
/**
1214+
* Buttons: "left", "right", "middle", "x1", "x2"
1215+
* @param button: string
1216+
* @returns void
1217+
*/
1218+
static simulate_mouse_click(button: string): void
1219+
/**
1220+
* Buttons: "left", "right", "middle", "x1", "x2"
1221+
* @param button: string
1222+
* @returns void
1223+
*/
1224+
static simulate_mouse_down(button: string): void
1225+
/**
1226+
* Buttons: "left", "right", "middle", "x1", "x2"
1227+
* @param button: string
1228+
* @returns void
1229+
*/
1230+
static simulate_mouse_up(button: string): void
11681231
}
11691232
export class notification {
11701233
/**

0 commit comments

Comments
 (0)