@velor/remote-mouse 6.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.artifacts/screenshots/remote-mobile-config.png +0 -0
- package/.artifacts/screenshots/remote-mobile-home.png +0 -0
- package/.artifacts/screenshots/remote-mobile-preferences.png +0 -0
- package/.artifacts/screenshots/remote-mobile-server-info.png +0 -0
- package/.artifacts/screenshots/remote-mobile-vlc.png +0 -0
- package/.env.example +93 -0
- package/README.md +370 -0
- package/bin/remote-mouse.js +19 -0
- package/client/core/dom.js +148 -0
- package/client/core/init-socket-io.js +117 -0
- package/client/core/socket-emit.js +6 -0
- package/client/core/window-events.js +23 -0
- package/client/i18n/apply-page-translations.js +18 -0
- package/client/i18n/constants.js +3 -0
- package/client/i18n/core.js +40 -0
- package/client/i18n/locale-registry.js +77 -0
- package/client/i18n/locales/de.js +227 -0
- package/client/i18n/locales/en.js +389 -0
- package/client/i18n/locales/es.js +228 -0
- package/client/i18n/locales/fr.js +389 -0
- package/client/i18n/locales/it.js +227 -0
- package/client/i18n/locales/ja.js +227 -0
- package/client/i18n/locales/pt.js +227 -0
- package/client/i18n/locales/ru.js +240 -0
- package/client/i18n/locales/zh.js +227 -0
- package/client/preferences/constants.js +15 -0
- package/client/preferences/effects.js +24 -0
- package/client/preferences/load.js +19 -0
- package/client/preferences/render.js +26 -0
- package/client/preferences/rows.js +78 -0
- package/client/preferences/state.js +29 -0
- package/client/preview/create-preview-frame-receiver.js +13 -0
- package/client/preview/draw.js +77 -0
- package/client/preview/parse-preview-frame.js +39 -0
- package/client/preview/preview-stream.js +152 -0
- package/client/services/app-state/appStateKeys.js +30 -0
- package/client/services/app-state/createAppStateService.js +216 -0
- package/client/services/app-state/createPersistStoreService.js +122 -0
- package/client/services/app-state/createStateStoreService.js +64 -0
- package/client/services/app-state/localStorageBinding.js +26 -0
- package/client/services/app-state/normalizers.js +74 -0
- package/client/services/app-state/resolveEffectiveStateValue.js +58 -0
- package/client/services/app-state/stateDefinitions.js +107 -0
- package/client/services/backend/createBackendService.js +55 -0
- package/client/services/config/ConfigView.js +45 -0
- package/client/services/config/createClientConfigService.js +122 -0
- package/client/services/config/defaultClientConfig.js +22 -0
- package/client/services/createServicesContainer.js +48 -0
- package/client/services/createServicesRegistry.js +26 -0
- package/client/services/i18n/createI18nService.js +86 -0
- package/client/services/notifications/createNotificationService.js +56 -0
- package/client/services/pubsub/createPubSubService.js +20 -0
- package/client/services/remotes/createRemotesService.js +20 -0
- package/client/services/transport/createSocketIoTransportService.js +50 -0
- package/client/touch/bindTouchPassthrough.js +94 -0
- package/client/touch/createTouchList.js +18 -0
- package/client/touch/dispatchSyntheticTouch.js +20 -0
- package/client/touch/gesture-zone.js +28 -0
- package/client/touch/throttle.js +46 -0
- package/client/touch/touch-handlers.js +193 -0
- package/client/touch/touchpad.js +231 -0
- package/client/ui/config/page.js +840 -0
- package/client/ui/main/applyRemoteVisibilityState.js +60 -0
- package/client/ui/main/bind-client-notifications.js +3 -0
- package/client/ui/main/bindings/bindAccordion.js +121 -0
- package/client/ui/main/bindings/bindActionButtons.js +13 -0
- package/client/ui/main/bindings/bindAdminDrawer.js +137 -0
- package/client/ui/main/bindings/bindAdminRemoteButtons.js +77 -0
- package/client/ui/main/bindings/bindAdminVersion.js +27 -0
- package/client/ui/main/bindings/bindBrowserRemoteButtons.js +119 -0
- package/client/ui/main/bindings/bindRemoteAccordion.js +23 -0
- package/client/ui/main/bindings/bindSamsungRemoteButtons.js +320 -0
- package/client/ui/main/bindings/bindSystemRemoteButtons.js +41 -0
- package/client/ui/main/bindings/bindVlcRemoteButtons.js +64 -0
- package/client/ui/main/canvas-ui.js +33 -0
- package/client/ui/main/connection-overlay.js +57 -0
- package/client/ui/main/init-ui.js +54 -0
- package/client/ui/main/keyboard-panel.js +271 -0
- package/client/ui/main/mouse-buttons.js +94 -0
- package/client/ui/main/page.js +21 -0
- package/client/ui/notifications/show-toast.js +48 -0
- package/client/ui/preferences/bindPreferenceSwitchers.js +49 -0
- package/client/ui/preferences/page.js +61 -0
- package/client/ui/preferences/switchers.js +200 -0
- package/index.js +9 -0
- package/package.json +41 -0
- package/public/admin-config.css +447 -0
- package/public/admin-config.html +63 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/brave-icon.svg +13 -0
- package/public/favicon.svg +30 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/index.html +246 -0
- package/public/manifest.webmanifest +45 -0
- package/public/preferences.html +55 -0
- package/public/server-info.html +574 -0
- package/public/styles.css +1042 -0
- package/public/sw.js +103 -0
- package/scripts/codex-commit.sh +112 -0
- package/scripts/generate-mobile-screenshots.mjs +244 -0
- package/scripts/install-linux.sh +633 -0
- package/scripts/install-windows.ps1 +369 -0
- package/server/application/createApplicationLifecycle.js +15 -0
- package/server/application/logger.js +216 -0
- package/server/application/shutdown.js +81 -0
- package/server/application/start.js +95 -0
- package/server/application/state.js +19 -0
- package/server/connection/api/admin-actions.router.js +65 -0
- package/server/connection/api/admin-configs.router.js +165 -0
- package/server/connection/api/admin-remotes.router.js +61 -0
- package/server/connection/api/admin-subs.router.js +49 -0
- package/server/connection/api/client.router.js +15 -0
- package/server/connection/api/configs.js +96 -0
- package/server/connection/api/qr-page.handler.js +17 -0
- package/server/connection/api/remotes.router.js +30 -0
- package/server/connection/api/server-info.router.js +149 -0
- package/server/connection/api/session.middleware.js +93 -0
- package/server/connection/api/unauthorized-response.js +73 -0
- package/server/connection/socket/createConnectionRegistrar.js +23 -0
- package/server/connection/socket/createSocketSessionAuthMiddleware.js +49 -0
- package/server/connection/socket/socket-action-responder.js +12 -0
- package/server/connection/socket/socket.timestamp-middleware.js +51 -0
- package/server/index.js +12 -0
- package/server/init/bootstrapApi.js +87 -0
- package/server/init/bootstrapSocket.js +79 -0
- package/server/init/createAdminApiRouter.js +22 -0
- package/server/init/createAdminUiRouter.js +21 -0
- package/server/init/createSocketActionRegistrars.js +52 -0
- package/server/init/observers/startConfigObserver.js +51 -0
- package/server/init/observers/startDisplaySizeObserver.js +55 -0
- package/server/init/observers/startNotificationObserver.js +49 -0
- package/server/init/observers/startQrOverlayHoverObserver.js +89 -0
- package/server/init/observers/startQrOverlayRefreshObserver.js +53 -0
- package/server/os/darwin/app.js +45 -0
- package/server/os/darwin/applescript.js +51 -0
- package/server/os/darwin/cliSocket.js +53 -0
- package/server/os/darwin/url.js +9 -0
- package/server/os/darwin/window.js +10 -0
- package/server/os/linux/app.js +120 -0
- package/server/os/linux/cliSocket.js +53 -0
- package/server/os/linux/process.js +69 -0
- package/server/os/linux/screen.js +58 -0
- package/server/os/linux/systemd.js +139 -0
- package/server/os/linux/url.js +17 -0
- package/server/os/linux/windows.js +144 -0
- package/server/os/win32/app.js +66 -0
- package/server/os/win32/cliSocket.js +15 -0
- package/server/os/win32/powershell.js +70 -0
- package/server/os/win32/process.js +20 -0
- package/server/os/win32/service.js +95 -0
- package/server/os/win32/url.js +9 -0
- package/server/os/win32/window.js +10 -0
- package/server/remotes/admin/createAdminEventGuardMiddleware.js +29 -0
- package/server/remotes/admin/createForceUpdateCheckAction.js +39 -0
- package/server/remotes/admin/createInstallUpdateAction.js +64 -0
- package/server/remotes/admin/createOpenQrBrowserAction.js +63 -0
- package/server/remotes/admin/createOpenServerInfoBrowserAction.js +58 -0
- package/server/remotes/admin/createRestartServiceAction.js +20 -0
- package/server/remotes/admin/createRotateEntryTokenAction.js +43 -0
- package/server/remotes/admin/createToggleQrOverlayAction.js +38 -0
- package/server/remotes/admin/index.js +32 -0
- package/server/remotes/admin/notifyIfRestarted.js +102 -0
- package/server/remotes/admin/registrar.js +94 -0
- package/server/remotes/browser/actions/createFocusOrLaunchBrowserAction.js +22 -0
- package/server/remotes/browser/actions/openUrlOnHost.js +3 -0
- package/server/remotes/browser/browserCatalog.js +70 -0
- package/server/remotes/browser/index.js +15 -0
- package/server/remotes/browser/listBrowsersOnHost.js +23 -0
- package/server/remotes/browser/registrar.js +29 -0
- package/server/remotes/browser/spec.js +18 -0
- package/server/remotes/browser/state.js +5 -0
- package/server/remotes/input/registrar.js +39 -0
- package/server/remotes/preview/bgraToRgbaBuffer.js +20 -0
- package/server/remotes/preview/captureAroundCursor.js +19 -0
- package/server/remotes/preview/createPreviewStreamer.js +85 -0
- package/server/remotes/preview/registrar.js +34 -0
- package/server/remotes/samsung/createSamsungCommandService.js +203 -0
- package/server/remotes/samsung/createSamsungRemote.js +26 -0
- package/server/remotes/samsung/createSamsungRemoteGetter.js +46 -0
- package/server/remotes/samsung/createSamsungTvGetter.js +102 -0
- package/server/remotes/samsung/device-config.js +122 -0
- package/server/remotes/samsung/registrar.js +59 -0
- package/server/remotes/samsung/utils.js +56 -0
- package/server/remotes/vlc/index.js +39 -0
- package/server/remotes/vlc/registrar.js +94 -0
- package/server/remotes/vlc/spec.js +15 -0
- package/server/remotes/vlc/state.js +5 -0
- package/server/remotes/window/index.js +6 -0
- package/server/remotes/window/registrar.js +28 -0
- package/server/services/application/createApplicationDaemonService.js +144 -0
- package/server/services/application/getDaemonServiceAdapter.js +56 -0
- package/server/services/application/utils.js +29 -0
- package/server/services/config/bootstrapConfig.js +38 -0
- package/server/services/config/configPaths.js +42 -0
- package/server/services/config/configSchema.js +297 -0
- package/server/services/config/configService.js +89 -0
- package/server/services/config/defaultConfig.js +137 -0
- package/server/services/config/envConfig.js +49 -0
- package/server/services/config/index.js +9 -0
- package/server/services/config/logConfig.js +54 -0
- package/server/services/config/system-config.js +44 -0
- package/server/services/createServicesContainer.js +94 -0
- package/server/services/createServicesRegistry.js +48 -0
- package/server/services/input/createInputController.js +17 -0
- package/server/services/input/createKeyboardController.js +146 -0
- package/server/services/input/createMouseController.js +182 -0
- package/server/services/notifier/createClientNotifier.js +23 -0
- package/server/services/notifier/createHostNotifier.js +41 -0
- package/server/services/notifier/createNotifier.js +13 -0
- package/server/services/notifier/createNotifierComposite.js +122 -0
- package/server/services/notifier/handleOpenQrBrowserEvent.js +40 -0
- package/server/services/notifier/handleOpenServerInfoBrowserEvent.js +40 -0
- package/server/services/notifier/handleRestartServiceEvent.js +37 -0
- package/server/services/notifier/handleRotateEntryTokenEvent.js +42 -0
- package/server/services/notifier/handleSessionEvent.js +24 -0
- package/server/services/notifier/handleToggleQrOverlayEvent.js +21 -0
- package/server/services/notifier/host-notifier/createLinuxHostNotifier.js +23 -0
- package/server/services/notifier/host-notifier/createNodeNotifierHostNotifier.js +24 -0
- package/server/services/notifier/host-notifier/index.js +11 -0
- package/server/services/notifier/mapper/handleForceUpdateCheckEvent.js +24 -0
- package/server/services/notifier/mapper/handleInstallUpdateEvent.js +53 -0
- package/server/services/notifier/mapper/handleSocketEvent.js +40 -0
- package/server/services/notifier/mapper/handleUpdateManagerEvent.js +24 -0
- package/server/services/notifier/mapper/notify.js +7 -0
- package/server/services/os/index.js +23 -0
- package/server/services/os/platforms/createFallbackAdapter.js +31 -0
- package/server/services/os/platforms/darwin/app.js +6 -0
- package/server/services/os/platforms/darwin/index.js +21 -0
- package/server/services/os/platforms/darwin/scripts.js +4 -0
- package/server/services/os/platforms/darwin/url.js +1 -0
- package/server/services/os/platforms/darwin/window.js +4 -0
- package/server/services/os/platforms/linux/app.js +6 -0
- package/server/services/os/platforms/linux/index.js +33 -0
- package/server/services/os/platforms/linux/url.js +1 -0
- package/server/services/os/platforms/linux/windows.js +11 -0
- package/server/services/os/platforms/win32/app.js +6 -0
- package/server/services/os/platforms/win32/index.js +21 -0
- package/server/services/os/platforms/win32/overlay.js +6 -0
- package/server/services/os/platforms/win32/powershell.js +4 -0
- package/server/services/os/platforms/win32/url.js +1 -0
- package/server/services/os/platforms/win32/window.js +4 -0
- package/server/services/os/shared.js +2 -0
- package/server/services/overlay/createNoopOverlay.js +13 -0
- package/server/services/overlay/createQrOverlay.js +27 -0
- package/server/services/overlay/createQrOverlayWin32.js +158 -0
- package/server/services/overlay/createQrOverlayYad.js +178 -0
- package/server/services/overlay/renderQrPage.js +82 -0
- package/server/services/persistence/createConfigDao.js +106 -0
- package/server/services/persistence/createDatabaseProvider.js +23 -0
- package/server/services/persistence/createEntryTokenDao.js +140 -0
- package/server/services/persistence/createRestartLogDao.js +210 -0
- package/server/services/persistence/index.js +19 -0
- package/server/services/pubsub/createEventStore.js +54 -0
- package/server/services/pubsub/createPubSub.js +89 -0
- package/server/services/pubsub/createServiceEvents.js +29 -0
- package/server/services/pubsub/serviceEventConstants.js +39 -0
- package/server/services/remotes/createRemotes.js +25 -0
- package/server/services/server/createHttpsServer.js +12 -0
- package/server/services/server/createServer.js +66 -0
- package/server/services/sse/createSseService.js +96 -0
- package/server/services/sse/utils.js +7 -0
- package/server/services/system/createSystemService.js +170 -0
- package/server/services/system/getNetworkInfo.js +18 -0
- package/server/services/system/getScreenInfo.js +32 -0
- package/server/services/system/listNetworkInterfaces.js +27 -0
- package/server/services/task-manager/createTaskManager.js +68 -0
- package/server/services/task-runner/createTaskRunner.js +114 -0
- package/server/services/token-manager/createTokenManager.js +244 -0
- package/server/services/token-manager/token-format.js +7 -0
- package/server/services/token-manager/token-store-utils.js +8 -0
- package/server/services/update-manager/CommandUpdateSource.js +70 -0
- package/server/services/update-manager/NpmUpdateSource.js +34 -0
- package/server/services/update-manager/buildNpmGlobalUpdateCommand.js +15 -0
- package/server/services/update-manager/chooseUpdateCheckSource.js +20 -0
- package/server/services/update-manager/chooseUpdateInstallSource.js +5 -0
- package/server/services/update-manager/createUpdateManager.js +109 -0
- package/server/services/update-manager/resolveInstallUpdateCommand.js +42 -0
- package/server/term/cli/executeLocalServiceCommand.js +42 -0
- package/server/term/cli/getCliSocketPath.js +5 -0
- package/server/term/cli/parseCliArgs.js +110 -0
- package/server/term/cli/printCliResult.js +18 -0
- package/server/term/cli/runCliCmd.js +50 -0
- package/server/term/cli/sendCliCommand.js +76 -0
- package/server/term/cliSocket.js +29 -0
- package/server/term/srv/commands/configCommand.js +84 -0
- package/server/term/srv/commands/helpCommand.js +6 -0
- package/server/term/srv/commands/infoCommand.js +7 -0
- package/server/term/srv/commands/openQrCommand.js +7 -0
- package/server/term/srv/commands/samsungDetectCommand.js +52 -0
- package/server/term/srv/commands/serviceCommand.js +25 -0
- package/server/term/srv/commands/systemConfigCommand.js +7 -0
- package/server/term/srv/commands/tasksCommand.js +24 -0
- package/server/term/srv/commands/tokensCommand.js +15 -0
- package/server/term/srv/executeCliCommand.js +44 -0
- package/server/term/srv/executeCliRequest.js +38 -0
- package/server/term/srv/startCliServer.js +140 -0
- package/server/utils/createRandomToken.js +11 -0
- package/server/utils/env.js +155 -0
- package/server/utils/git.js +13 -0
- package/server/utils/http.js +7 -0
- package/server/utils/network.js +45 -0
- package/server/utils/paths.js +10 -0
- package/server/utils/process.js +57 -0
- package/server/utils/robot.js +11 -0
- package/server/utils/semver.js +33 -0
- package/server/utils/truncateText.js +7 -0
- package/utils/createLazy.js +13 -0
- package/utils/functional.js +48 -0
- package/utils/math.js +26 -0
- package/utils/notificationSettings.js +61 -0
- package/utils/object.utils.js +79 -0
- package/utils/predicates.js +17 -0
- package/utils/remoteCommands.js +40 -0
- package/utils/sync.js +3 -0
- package/velor-remote-mouse-6.4.8.tgz +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
function getSocketName() {
|
|
6
|
+
const uid = typeof process.getuid === 'function' ? process.getuid() : 'default';
|
|
7
|
+
return `remote-mouse-${uid}.sock`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function removeStaleSocket(socketPath) {
|
|
11
|
+
try {
|
|
12
|
+
fs.unlinkSync(socketPath);
|
|
13
|
+
} catch (_error) {
|
|
14
|
+
// Ignore missing or stale socket cleanup issues.
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getCliSocketPath() {
|
|
19
|
+
return path.join(os.tmpdir(), getSocketName());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function prepareCliServerSocket(socketPath) {
|
|
23
|
+
removeStaleSocket(socketPath);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function cleanupCliServerSocket(socketPath) {
|
|
27
|
+
removeStaleSocket(socketPath);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function secureCliServerSocket(socketPath) {
|
|
31
|
+
try {
|
|
32
|
+
fs.chmodSync(socketPath, 0o600);
|
|
33
|
+
} catch (_error) {
|
|
34
|
+
// Best effort.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function cleanupCliClientConnectionError(socketPath, error) {
|
|
39
|
+
if (error?.code !== 'ECONNREFUSED') {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
if (!fs.existsSync(socketPath)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fs.unlinkSync(socketPath);
|
|
49
|
+
return true;
|
|
50
|
+
} catch (_cleanupError) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {createLogger} from '../../application/logger.js';
|
|
2
|
+
import {execFileAsync} from '../../utils/process.js';
|
|
3
|
+
|
|
4
|
+
const LINUX_COMMAND_EXTRA_PATHS = [
|
|
5
|
+
'/snap/bin',
|
|
6
|
+
'/var/lib/snapd/snap/bin',
|
|
7
|
+
'/usr/local/bin',
|
|
8
|
+
'/usr/bin',
|
|
9
|
+
'/bin',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
let log;
|
|
13
|
+
function getModuleLog() {
|
|
14
|
+
log ??= createLogger('os:linux:process');
|
|
15
|
+
return log;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getLinuxCommandLookupPath(basePath = process.env.PATH || '') {
|
|
19
|
+
const entries = String(basePath || '')
|
|
20
|
+
.split(':')
|
|
21
|
+
.map((entry) => entry.trim())
|
|
22
|
+
.filter(Boolean);
|
|
23
|
+
|
|
24
|
+
for (const extraPath of LINUX_COMMAND_EXTRA_PATHS) {
|
|
25
|
+
if (!entries.includes(extraPath)) {
|
|
26
|
+
entries.push(extraPath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return entries.join(':');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function resolveLinuxCommand(command) {
|
|
34
|
+
const log = getModuleLog();
|
|
35
|
+
const normalizedCommand = String(command || '').trim();
|
|
36
|
+
if (!normalizedCommand) {
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const lookupPath = getLinuxCommandLookupPath();
|
|
41
|
+
const result = await execFileAsync('which', [normalizedCommand], {
|
|
42
|
+
timeout: 2000,
|
|
43
|
+
env: {
|
|
44
|
+
...process.env,
|
|
45
|
+
PATH: lookupPath,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!result.ok) {
|
|
50
|
+
log.trace({command: normalizedCommand, lookupPath}, 'Commande Linux introuvable');
|
|
51
|
+
return '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const resolvedCommand = result.stdout
|
|
55
|
+
.split('\n')
|
|
56
|
+
.map((line) => line.trim())
|
|
57
|
+
.find(Boolean) || normalizedCommand;
|
|
58
|
+
|
|
59
|
+
log.debug({
|
|
60
|
+
command: normalizedCommand,
|
|
61
|
+
resolvedCommand,
|
|
62
|
+
lookupPath,
|
|
63
|
+
}, 'Commande Linux resolue');
|
|
64
|
+
return resolvedCommand;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function commandExists(command) {
|
|
68
|
+
return Boolean(await resolveLinuxCommand(command));
|
|
69
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {execFileAsync} from '../../utils/process.js';
|
|
2
|
+
|
|
3
|
+
function normalizeScreenSize(screen) {
|
|
4
|
+
const width = Number(screen?.width);
|
|
5
|
+
const height = Number(screen?.height);
|
|
6
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
width: Math.round(width),
|
|
12
|
+
height: Math.round(height),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function parseXrandrScreenSize(output) {
|
|
17
|
+
const match = String(output || '').match(/\bcurrent\s+(\d+)\s*x\s*(\d+)\b/i);
|
|
18
|
+
if (!match) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return normalizeScreenSize({
|
|
23
|
+
width: match[1],
|
|
24
|
+
height: match[2],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function parseXdpyinfoScreenSize(output) {
|
|
29
|
+
const match = String(output || '').match(/\bdimensions:\s*(\d+)x(\d+)\s+pixels\b/i);
|
|
30
|
+
if (!match) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return normalizeScreenSize({
|
|
35
|
+
width: match[1],
|
|
36
|
+
height: match[2],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function resolveLinuxScreenSize() {
|
|
41
|
+
const xrandr = await execFileAsync('xrandr', ['--current'], {timeout: 1500});
|
|
42
|
+
if (xrandr.ok) {
|
|
43
|
+
const screen = parseXrandrScreenSize(xrandr.stdout);
|
|
44
|
+
if (screen) {
|
|
45
|
+
return screen;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const xdpyinfo = await execFileAsync('xdpyinfo', [], {timeout: 1500});
|
|
50
|
+
if (xdpyinfo.ok) {
|
|
51
|
+
const screen = parseXdpyinfoScreenSize(xdpyinfo.stdout);
|
|
52
|
+
if (screen) {
|
|
53
|
+
return screen;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getEntrypoint,
|
|
3
|
+
getPlatformKind,
|
|
4
|
+
getSystemdUnitPath,
|
|
5
|
+
isDaemonProcess,
|
|
6
|
+
shellQuote,
|
|
7
|
+
} from "../../services/application/utils.js";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import {
|
|
11
|
+
execFileAsync,
|
|
12
|
+
spawnDetached
|
|
13
|
+
} from "../../utils/process.js";
|
|
14
|
+
|
|
15
|
+
export function buildSystemdUnit({entrypoint, nodePath}) {
|
|
16
|
+
const nodeBinDir = path.dirname(String(nodePath || process.execPath));
|
|
17
|
+
const environmentPath = [nodeBinDir, '/usr/local/bin', '/usr/bin', '/bin']
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.join(':');
|
|
20
|
+
|
|
21
|
+
return [
|
|
22
|
+
'[Unit]',
|
|
23
|
+
'Description=Remote Mouse Server',
|
|
24
|
+
'After=graphical-session.target network-online.target',
|
|
25
|
+
'Wants=network-online.target',
|
|
26
|
+
'',
|
|
27
|
+
'[Service]',
|
|
28
|
+
'Type=simple',
|
|
29
|
+
`ExecStart=${entrypoint}`,
|
|
30
|
+
'Restart=on-failure',
|
|
31
|
+
'RestartSec=2',
|
|
32
|
+
`Environment=PATH=${environmentPath}`,
|
|
33
|
+
'Environment=NODE_ENV=production',
|
|
34
|
+
'Environment=REMOTE_MOUSE_DAEMON=1',
|
|
35
|
+
'PassEnvironment=DISPLAY WAYLAND_DISPLAY XAUTHORITY DBUS_SESSION_BUS_ADDRESS',
|
|
36
|
+
'',
|
|
37
|
+
'[Install]',
|
|
38
|
+
'WantedBy=default.target',
|
|
39
|
+
'',
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function installLinuxDaemon(serviceName) {
|
|
44
|
+
const unitPath = getSystemdUnitPath(serviceName);
|
|
45
|
+
fs.mkdirSync(path.dirname(unitPath), {recursive: true});
|
|
46
|
+
fs.writeFileSync(unitPath, buildSystemdUnit({
|
|
47
|
+
entrypoint: getEntrypoint(),
|
|
48
|
+
nodePath: process.execPath,
|
|
49
|
+
}), 'utf8');
|
|
50
|
+
|
|
51
|
+
const reload = await execFileAsync('systemctl', ['--user', 'daemon-reload'], {timeout: 15000});
|
|
52
|
+
if (!reload.ok) {
|
|
53
|
+
return {ok: false, message: reload.stderr || 'systemctl --user daemon-reload a echoue.'};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const enable = await execFileAsync('systemctl', ['--user', 'enable', '--now', serviceName], {timeout: 30000});
|
|
57
|
+
if (!enable.ok) {
|
|
58
|
+
return {ok: false, message: enable.stderr || `Impossible d'installer ${serviceName}.`};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
message: `Service ${serviceName} installe via systemd utilisateur.`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function disableLinuxDaemon(serviceName) {
|
|
68
|
+
const result = await execFileAsync('systemctl', ['--user', 'disable', '--now', serviceName], {timeout: 30000});
|
|
69
|
+
if (!result.ok) {
|
|
70
|
+
return {ok: false, message: result.stderr || `Impossible de desactiver ${serviceName}.`};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
message: `Service ${serviceName} desactive.`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function uninstallLinuxDaemon(serviceName) {
|
|
80
|
+
const unitPath = getSystemdUnitPath(serviceName);
|
|
81
|
+
|
|
82
|
+
await execFileAsync('systemctl', ['--user', 'disable', '--now', serviceName], {timeout: 30000});
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
fs.unlinkSync(unitPath);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (error?.code !== 'ENOENT') {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
message: `Impossible de supprimer ${unitPath}.`,
|
|
91
|
+
details: {message: error.message},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const reload = await execFileAsync('systemctl', ['--user', 'daemon-reload'], {timeout: 15000});
|
|
97
|
+
if (!reload.ok) {
|
|
98
|
+
return {ok: false, message: reload.stderr || 'systemctl --user daemon-reload a echoue.'};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
await execFileAsync('systemctl', ['--user', 'reset-failed', serviceName], {timeout: 10000});
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
message: `Service ${serviceName} desinstalle.`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function restartLinuxDaemon(serviceName) {
|
|
110
|
+
const spawned = await spawnDetached(
|
|
111
|
+
'bash',
|
|
112
|
+
['-lc', `sleep 0.8; systemctl --user restart ${shellQuote(serviceName)}`],
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (!spawned) {
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
message: 'Impossible de lancer la commande de redemarrage.',
|
|
119
|
+
details: {message: 'spawn systemctl failed'},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
ok: true,
|
|
125
|
+
message: `Redemarrage demande pour ${serviceName}.`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function getLinuxDaemonInfo(serviceName) {
|
|
130
|
+
const result = await execFileAsync('systemctl', ['--user', 'status', serviceName], {timeout: 5000});
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
serviceName,
|
|
134
|
+
platform: process.platform,
|
|
135
|
+
platformKind: getPlatformKind(),
|
|
136
|
+
runningAsDaemon: isDaemonProcess(),
|
|
137
|
+
installed: result.ok,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {spawnDetached} from '../../utils/process.js';
|
|
2
|
+
import {commandExists} from './process.js';
|
|
3
|
+
|
|
4
|
+
export async function openLinuxUrl(url) {
|
|
5
|
+
const safeUrl = String(url || '').trim();
|
|
6
|
+
if (!safeUrl) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (await commandExists('xdg-open')) {
|
|
11
|
+
return spawnDetached('xdg-open', [safeUrl]);
|
|
12
|
+
}
|
|
13
|
+
if (await commandExists('gio')) {
|
|
14
|
+
return spawnDetached('gio', ['open', safeUrl]);
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {execFileAsync} from '../../utils/process.js';
|
|
2
|
+
import {commandExists} from './process.js';
|
|
3
|
+
|
|
4
|
+
export function parseWindowIdsFromWmctrl(output, {classes = [], names = []} = {}) {
|
|
5
|
+
const patterns = [...classes, ...names].map((value) => String(value).toLowerCase());
|
|
6
|
+
|
|
7
|
+
return output
|
|
8
|
+
.split('\n')
|
|
9
|
+
.map((line) => line.trim())
|
|
10
|
+
.filter(Boolean)
|
|
11
|
+
.filter((line) => patterns.some((pattern) => line.toLowerCase().includes(pattern)))
|
|
12
|
+
.map((line) => line.split(/\s+/)[0])
|
|
13
|
+
.filter((id) => /^0x[0-9a-f]+$/i.test(id));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function findWindows(query = {}) {
|
|
17
|
+
if (await commandExists('wmctrl')) {
|
|
18
|
+
const result = await execFileAsync('wmctrl', ['-lx']);
|
|
19
|
+
if (result.ok) {
|
|
20
|
+
const ids = parseWindowIdsFromWmctrl(result.stdout, query);
|
|
21
|
+
if (ids.length > 0) {
|
|
22
|
+
return ids;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!(await commandExists('xdotool'))) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const matches = [];
|
|
32
|
+
for (const className of query.classes ?? []) {
|
|
33
|
+
matches.push((await execFileAsync('xdotool', ['search', '--onlyvisible', '--class', className])).stdout);
|
|
34
|
+
matches.push((await execFileAsync('xdotool', ['search', '--class', className])).stdout);
|
|
35
|
+
}
|
|
36
|
+
for (const name of query.names ?? []) {
|
|
37
|
+
matches.push((await execFileAsync('xdotool', ['search', '--onlyvisible', '--name', name])).stdout);
|
|
38
|
+
matches.push((await execFileAsync('xdotool', ['search', '--name', name])).stdout);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return matches
|
|
42
|
+
.join('\n')
|
|
43
|
+
.split('\n')
|
|
44
|
+
.map((value) => value.trim())
|
|
45
|
+
.filter((value) => /^\d+$/.test(value));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function activateWindow(windowId, {maximize = false} = {}) {
|
|
49
|
+
if (await commandExists('wmctrl')) {
|
|
50
|
+
const activated = await execFileAsync('wmctrl', ['-ia', String(windowId)]);
|
|
51
|
+
if (!activated.ok) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (maximize) {
|
|
55
|
+
await execFileAsync('wmctrl', ['-ir', String(windowId), '-b', 'add,maximized_vert,maximized_horz']);
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!(await commandExists('xdotool'))) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const activated = await execFileAsync('xdotool', ['windowactivate', '--sync', String(windowId)]);
|
|
65
|
+
if (!activated.ok) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await execFileAsync('xdotool', ['windowraise', String(windowId)]);
|
|
70
|
+
if (maximize && await commandExists('wmctrl')) {
|
|
71
|
+
await execFileAsync('wmctrl', ['-ir', String(windowId), '-b', 'add,maximized_vert,maximized_horz']);
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function maximizeWindow(windowId) {
|
|
77
|
+
if (!(await commandExists('wmctrl'))) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (await execFileAsync('wmctrl', ['-ir', String(windowId), '-b', 'add,maximized_vert,maximized_horz'])).ok;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function restoreWindow(windowId) {
|
|
85
|
+
if (await commandExists('wmctrl')) {
|
|
86
|
+
return (await execFileAsync('wmctrl', ['-ir', String(windowId), '-b', 'remove,maximized_vert,maximized_horz'])).ok;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function isWindowMaximized(windowId) {
|
|
93
|
+
if (!(await commandExists('xprop'))) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const result = await execFileAsync('xprop', ['-id', String(windowId), '_NET_WM_STATE']);
|
|
98
|
+
return result.ok
|
|
99
|
+
&& result.stdout.includes('_NET_WM_STATE_MAXIMIZED_VERT')
|
|
100
|
+
&& result.stdout.includes('_NET_WM_STATE_MAXIMIZED_HORZ');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function toggleWindow(windowId) {
|
|
104
|
+
if (await isWindowMaximized(windowId)) {
|
|
105
|
+
return restoreWindow(windowId);
|
|
106
|
+
}
|
|
107
|
+
return maximizeWindow(windowId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function closeWindow(windowId) {
|
|
111
|
+
if (await commandExists('wmctrl')) {
|
|
112
|
+
return (await execFileAsync('wmctrl', ['-ic', String(windowId)])).ok;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (await commandExists('xdotool')) {
|
|
116
|
+
return (await execFileAsync('xdotool', ['windowclose', String(windowId)])).ok;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function getActiveWindowId() {
|
|
123
|
+
if (await commandExists('xdotool')) {
|
|
124
|
+
const result = await execFileAsync('xdotool', ['getactivewindow']);
|
|
125
|
+
if (result.ok) {
|
|
126
|
+
const id = result.stdout.trim();
|
|
127
|
+
if (/^\d+$/.test(id)) {
|
|
128
|
+
return id;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (await commandExists('xprop')) {
|
|
134
|
+
const result = await execFileAsync('xprop', ['-root', '_NET_ACTIVE_WINDOW']);
|
|
135
|
+
if (result.ok) {
|
|
136
|
+
const match = result.stdout.match(/0x[0-9a-f]+/i);
|
|
137
|
+
if (match) {
|
|
138
|
+
return match[0];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return '';
|
|
144
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {execFileAsync} from '../../utils/process.js';
|
|
2
|
+
import {buildForegroundWindowPowerShell, buildTitleActivationPowerShell} from './powershell.js';
|
|
3
|
+
import {commandExistsWin32} from './process.js';
|
|
4
|
+
import {findFirstAvailable} from "../../../utils/predicates.js";
|
|
5
|
+
|
|
6
|
+
export async function activateWin32Titles(titles = []) {
|
|
7
|
+
if (!titles.length) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const result = await execFileAsync('powershell', [
|
|
12
|
+
'-NoProfile',
|
|
13
|
+
'-Command',
|
|
14
|
+
buildTitleActivationPowerShell(titles),
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
return result.ok && result.stdout.trim() === 'true';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function resolveWin32App(spec = {}) {
|
|
21
|
+
const win = spec.win32 || {};
|
|
22
|
+
const command = await findFirstAvailable(win.commands, commandExistsWin32);
|
|
23
|
+
if (!command) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
command,
|
|
29
|
+
titles: win.windowTitles ?? [],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function openOrFocusWin32App(spec) {
|
|
34
|
+
const resolved = await resolveWin32App(spec);
|
|
35
|
+
if (!resolved) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (await activateWin32Titles(resolved.titles)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (await execFileAsync('powershell', ['-NoProfile', '-Command', `Start-Process ${resolved.command}`])).ok;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function toggleWin32AppWindow(spec) {
|
|
47
|
+
const resolved = await resolveWin32App(spec);
|
|
48
|
+
if (!resolved) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (!(await activateWin32Titles(resolved.titles))) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return (await execFileAsync('powershell', ['-NoProfile', '-Command', buildForegroundWindowPowerShell('toggle')])).ok;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function closeWin32AppWindow(spec) {
|
|
58
|
+
const resolved = await resolveWin32App(spec);
|
|
59
|
+
if (!resolved) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (!(await activateWin32Titles(resolved.titles))) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return (await execFileAsync('powershell', ['-NoProfile', '-Command', buildForegroundWindowPowerShell('close')])).ok;
|
|
66
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const cliPipePath = '\\\\.\\pipe\\remote-mouse';
|
|
2
|
+
|
|
3
|
+
export function getCliSocketPath() {
|
|
4
|
+
return cliPipePath;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function prepareCliServerSocket() {}
|
|
8
|
+
|
|
9
|
+
export function cleanupCliServerSocket() {}
|
|
10
|
+
|
|
11
|
+
export function secureCliServerSocket() {}
|
|
12
|
+
|
|
13
|
+
export function cleanupCliClientConnectionError() {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export function buildForegroundWindowPowerShell(mode) {
|
|
2
|
+
if (mode === 'toggle') {
|
|
3
|
+
return `
|
|
4
|
+
Add-Type @"
|
|
5
|
+
using System;
|
|
6
|
+
using System.Runtime.InteropServices;
|
|
7
|
+
public static class NativeWin {
|
|
8
|
+
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
9
|
+
[DllImport("user32.dll")] public static extern bool IsZoomed(IntPtr hWnd);
|
|
10
|
+
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
11
|
+
}
|
|
12
|
+
"@
|
|
13
|
+
$hwnd = [NativeWin]::GetForegroundWindow()
|
|
14
|
+
if ($hwnd -eq [IntPtr]::Zero) { exit 1 }
|
|
15
|
+
if ([NativeWin]::IsZoomed($hwnd)) {
|
|
16
|
+
[void][NativeWin]::ShowWindow($hwnd, 9)
|
|
17
|
+
} else {
|
|
18
|
+
[void][NativeWin]::ShowWindow($hwnd, 3)
|
|
19
|
+
}
|
|
20
|
+
`.trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return `
|
|
24
|
+
Add-Type @"
|
|
25
|
+
using System;
|
|
26
|
+
using System.Runtime.InteropServices;
|
|
27
|
+
public static class NativeWin {
|
|
28
|
+
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
29
|
+
[DllImport("user32.dll")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
30
|
+
}
|
|
31
|
+
"@
|
|
32
|
+
$hwnd = [NativeWin]::GetForegroundWindow()
|
|
33
|
+
if ($hwnd -eq [IntPtr]::Zero) { exit 1 }
|
|
34
|
+
[void][NativeWin]::PostMessage($hwnd, 0x0010, [IntPtr]::Zero, [IntPtr]::Zero)
|
|
35
|
+
`.trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function buildTitleActivationPowerShell(titles = []) {
|
|
39
|
+
const clauses = titles
|
|
40
|
+
.map((title) => String(title).replace(/"/g, '\\"'))
|
|
41
|
+
.map((title) => `if ($ws.AppActivate("${title}")) { "true"; exit 0 }`)
|
|
42
|
+
.join('; ');
|
|
43
|
+
|
|
44
|
+
return `$ws = New-Object -ComObject WScript.Shell; ${clauses}; exit 1`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function buildQrOverlayPowerShellScript({qrPath, size, posX, posY}) {
|
|
48
|
+
const escapedQrPath = String(qrPath).replace(/\\/g, '\\\\');
|
|
49
|
+
return [
|
|
50
|
+
'Add-Type -AssemblyName System.Windows.Forms',
|
|
51
|
+
'Add-Type -AssemblyName System.Drawing',
|
|
52
|
+
'$form = New-Object Windows.Forms.Form',
|
|
53
|
+
"$form.StartPosition = 'Manual'",
|
|
54
|
+
'$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None',
|
|
55
|
+
'$form.TopMost = $true',
|
|
56
|
+
'$form.ShowInTaskbar = $false',
|
|
57
|
+
`$form.ClientSize = New-Object System.Drawing.Size(${size}, ${size})`,
|
|
58
|
+
`$form.Left = ${posX}`,
|
|
59
|
+
`$form.Top = ${posY}`,
|
|
60
|
+
'$form.Padding = New-Object Windows.Forms.Padding(0)',
|
|
61
|
+
'$picture = New-Object Windows.Forms.PictureBox',
|
|
62
|
+
'$picture.Margin = New-Object Windows.Forms.Padding(0)',
|
|
63
|
+
'$picture.Padding = New-Object Windows.Forms.Padding(0)',
|
|
64
|
+
'$picture.Dock = [System.Windows.Forms.DockStyle]::Fill',
|
|
65
|
+
'$picture.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::StretchImage',
|
|
66
|
+
`$picture.Image = [System.Drawing.Image]::FromFile("${escapedQrPath}")`,
|
|
67
|
+
'$form.Controls.Add($picture)',
|
|
68
|
+
'[System.Windows.Forms.Application]::Run($form)',
|
|
69
|
+
].join('\n');
|
|
70
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {spawn} from 'node:child_process';
|
|
2
|
+
import {execFileAsync} from '../../utils/process.js';
|
|
3
|
+
|
|
4
|
+
export async function commandExistsWin32(command) {
|
|
5
|
+
const result = await execFileAsync('powershell', [
|
|
6
|
+
'-NoProfile',
|
|
7
|
+
'-Command',
|
|
8
|
+
`if (Get-Command "${command}" -ErrorAction SilentlyContinue) { "true" }`,
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
return result.ok && result.stdout.trim() === 'true';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function spawnPowerShellFile(scriptPath) {
|
|
15
|
+
return spawn(
|
|
16
|
+
'powershell',
|
|
17
|
+
['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', scriptPath],
|
|
18
|
+
{stdio: 'ignore'},
|
|
19
|
+
);
|
|
20
|
+
}
|