Skip to content

Commit

Permalink
feat(client-electron): add linux build
Browse files Browse the repository at this point in the history
fixes #138
  • Loading branch information
marcincichocki authored Aug 4, 2021
1 parent 0076154 commit 56cbfc1
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 61 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ on:
- master
jobs:
build:
runs-on: windows-2019
strategy:
matrix:
platform: [ubuntu-latest, windows-2019]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
Expand Down
1 change: 1 addition & 0 deletions configs/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const commonPlugins: WebpackPluginInstance[] = [
HOMEPAGE_URL: JSON.stringify(pkg.homepage),
BUGS_URL: JSON.stringify(pkg.bugs),
PRODUCT_NAME: JSON.stringify(pkg.build.productName),
BUILD_PLATFORM: JSON.stringify(process.platform),
}),
new LicenseWebpackPlugin({
outputFilename: 'THIRD_PARTY_LICENSES.txt',
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
"win": {
"target": "nsis"
},
"linux": {
"asarUnpack": [
"node_modules/sharp"
]
},
"extraMetadata": {
"main": "dist/main.js"
}
Expand All @@ -48,7 +53,7 @@
"webpack:watch": "webpack --config configs/webpack.config.ts --watch",
"webpack:build": "webpack --config configs/webpack.config.ts --mode production",
"electron:run": "electron dist/main.js",
"electron:build": "npm run webpack:build && electron-builder build --win"
"electron:build": "npm run webpack:build && electron-builder build"
},
"author": "Marcin Cichocki <[email protected]>",
"homepage": "https://github.com/marcincichocki/breach-protocol-autosolver#readme",
Expand Down
29 changes: 29 additions & 0 deletions src/common/node/robot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,32 @@ export class AhkRobot extends BreachProtocolRobot {
return this.bin(`send ${this.keys[key]}`);
}
}

export class XDoToolRobot extends BreachProtocolRobot {
protected readonly keys = {
[BreachProtocolRobotKeys.Escape]: 'Escape',
[BreachProtocolRobotKeys.Enter]: 'Enter',
[BreachProtocolRobotKeys.Up]: 'Up',
[BreachProtocolRobotKeys.Down]: 'Down',
[BreachProtocolRobotKeys.Left]: 'Left',
[BreachProtocolRobotKeys.Right]: 'Right',
};

protected readonly binPath = 'xdotool';

move(x: number, y: number) {
return this.bin(`mousemove ${x} ${y}`);
}

moveAway() {
return this.move(0, 0);
}

click() {
return this.bin('click 1');
}

pressKey(key: BreachProtocolRobotKeys) {
return this.bin(`key ${this.keys[key]}`);
}
}
42 changes: 41 additions & 1 deletion src/electron/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export interface AppSettings extends RobotSettings {
thresholdBufferSizeAuto: boolean;
experimentalBufferSizeRecognition: boolean;
outputDevice: 'mouse' | 'keyboard';
engine: 'nircmd' | 'ahk';
engine: 'nircmd' | 'ahk' | 'xdotool';
ahkBinPath: string;
}

Expand Down Expand Up @@ -161,3 +161,43 @@ export interface TestThresholdData {
threshold: number;
fragmentId: FragmentId;
}

export class NativeDialog {
static async confirm(options: Electron.MessageBoxOptions) {
const defaultOptions: Partial<Electron.MessageBoxOptions> = {
title: 'Confirm',
defaultId: 0,
cancelId: 1,
noLink: true,
type: 'warning',
buttons: ['Ok', 'Cancel'],
};
const { response } = await NativeDialog.showMessageBox({
...defaultOptions,
...options,
});

return !response;
}

static async alert(options?: Electron.MessageBoxOptions) {
const defaultOptions: Partial<Electron.MessageBoxOptions> = {
noLink: true,
defaultId: 0,
title: 'Alert',
type: 'warning',
buttons: ['Ok'],
};

return NativeDialog.showMessageBox({
...defaultOptions,
...options,
});
}

private static showMessageBox(
options: Electron.MessageBoxOptions
): Promise<Electron.MessageBoxReturnValue> {
return ipc.invoke('renderer:show-message-box', options);
}
}
11 changes: 7 additions & 4 deletions src/electron/common/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const options: BreachProtocolOption[] = [
{
id: 'soundEnabled',
description: 'Disable/enable sound effects.',
defaultValue: true,
defaultValue: BUILD_PLATFORM === 'win32',
},
{
id: 'experimentalBufferSizeRecognition',
Expand Down Expand Up @@ -69,7 +69,10 @@ const options: BreachProtocolOption[] = [
{
id: 'errorSoundPath',
description: 'Path to error sound.',
defaultValue: 'C:/Windows/Media/Windows Foreground.wav',
defaultValue:
BUILD_PLATFORM === 'win32'
? 'C:/Windows/Media/Windows Foreground.wav'
: '',
},
{
id: 'thresholdBufferSize',
Expand Down Expand Up @@ -114,12 +117,12 @@ const options: BreachProtocolOption[] = [
{
id: 'outputDevice',
description: 'Output device that will be used to solve breach protocol.',
defaultValue: 'mouse',
defaultValue: 'keyboard',
},
{
id: 'engine',
description: 'Program that will send mouse clicks or key strokes to OS.',
defaultValue: 'nircmd',
defaultValue: BUILD_PLATFORM === 'win32' ? 'nircmd' : 'xdotool',
},
{
id: 'ahkBinPath',
Expand Down
40 changes: 0 additions & 40 deletions src/electron/renderer/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,43 +80,3 @@ export function useHistoryRedirect(channels: string[]) {

useIpcEvent(channels, () => history.replace('/history'));
}

export class NativeDialog {
static async confirm(options: Electron.MessageBoxOptions) {
const defaultOptions: Partial<Electron.MessageBoxOptions> = {
title: 'Confirm',
defaultId: 0,
cancelId: 1,
noLink: true,
type: 'warning',
buttons: ['Ok', 'Cancel'],
};
const { response } = await NativeDialog.showMessageBox({
...defaultOptions,
...options,
});

return !response;
}

static async alert(options?: Electron.MessageBoxOptions) {
const defaultOptions: Partial<Electron.MessageBoxOptions> = {
noLink: true,
defaultId: 0,
title: 'Alert',
type: 'warning',
buttons: ['Ok'],
};

return NativeDialog.showMessageBox({
...defaultOptions,
...options,
});
}

private static showMessageBox(
options: Electron.MessageBoxOptions
): Promise<Electron.MessageBoxReturnValue> {
return ipc.invoke('renderer:show-message-box', options);
}
}
2 changes: 1 addition & 1 deletion src/electron/renderer/components/KeyBind.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NativeDialog } from '@/electron/common';
import isAccelerator from 'electron-is-accelerator';
import {
Fragment,
Expand All @@ -6,7 +7,6 @@ import {
useState,
} from 'react';
import styled from 'styled-components';
import { NativeDialog } from '../common';
import { useField } from './Form';

/**
Expand Down
24 changes: 15 additions & 9 deletions src/electron/renderer/pages/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AppSettings,
NativeDialog,
optionsDescription,
RemoveLastNHistoryEntriesAction,
SetStatusAction,
Expand All @@ -18,7 +19,7 @@ import {
import { useLocation } from 'react-router-dom';
import { ScreenshotDisplayOutput } from 'screenshot-desktop';
import styled from 'styled-components';
import { dispatch, getDisplayName, NativeDialog } from '../common';
import { dispatch, getDisplayName } from '../common';
import {
Col,
Field,
Expand Down Expand Up @@ -150,16 +151,20 @@ function updateWorkerStatus(status: WorkerStatus) {
dispatch(new SetStatusAction(status, 'renderer'));
}

const outputDeviceOptions = [
{ name: 'Keyboard(recommended)', value: 'keyboard' },
{ name: 'Mouse', value: 'mouse' },
];
const engineOptions =
BUILD_PLATFORM === 'win32'
? [
{ name: 'NirCmd', value: 'nircmd' },
{ name: 'AutoHotkey', value: 'ahk' },
]
: [{ name: 'xdotool', value: 'xdotool' }];

const AutoSolverSettings = ({ status }: { status: WorkerStatus }) => {
const { values } = useForm<AppSettings>();
const outputDeviceOptions = [
{ name: 'Keyboard(recommended)', value: 'keyboard' },
{ name: 'Mouse', value: 'mouse' },
];
const engineOptions = [
{ name: 'NirCmd', value: 'nircmd' },
{ name: 'AutoHotKey', value: 'ahk' },
];

function changeKeyBind(accelerator: Accelerator) {
ipc.send('renderer:key-bind-change', accelerator);
Expand Down Expand Up @@ -241,6 +246,7 @@ const AutoSolverSettings = ({ status }: { status: WorkerStatus }) => {
<Label>Engine</Label>
<Select
options={engineOptions}
disabled={engineOptions.length === 1}
onBeforeValueChange={notifyAboutEngine}
/>
</Field>
Expand Down
36 changes: 32 additions & 4 deletions src/electron/worker/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
BreachProtocolRobot,
NirCmdRobot,
SharpImageContainer,
XDoToolRobot,
} from '@/common/node';
import {
BreachProtocolBufferSizeFragment,
Expand All @@ -24,9 +25,11 @@ import {
workerAsyncRequestListener,
WorkerStatus,
} from '@/electron/common';
import { execSync } from 'child_process';
import { ipcRenderer as ipc, IpcRendererEvent } from 'electron';
import { listDisplays, ScreenshotDisplayOutput } from 'screenshot-desktop';
import sharp from 'sharp';
import { NativeDialog } from '../common';
import { BreachProtocolAutosolver } from './autosolver';

export class BreachProtocolWorker {
Expand Down Expand Up @@ -70,21 +73,44 @@ export class BreachProtocolWorker {
await this.loadAndSetActiveDisplay();
await BreachProtocolOCRFragment.initScheduler();

const status = this.isEngineBinPresent()
const status = this.validateExternalDependencies()
? WorkerStatus.Ready
: WorkerStatus.Disabled;

this.updateStatus(status);
}

private isEngineBinPresent() {
if (this.settings.engine === 'ahk' && !this.settings.ahkBinPath) {
return false;
private validateExternalDependencies() {
if (BUILD_PLATFORM === 'win32') {
if (this.settings.engine === 'ahk' && !this.settings.ahkBinPath) {
return false;
}
}

if (BUILD_PLATFORM === 'linux') {
const isImageMagickInstalled = this.isInstalled('import');
const isXDoToolInstalled = this.isInstalled('xdotool');

if (!isImageMagickInstalled || !isXDoToolInstalled) {
const message = 'imagemagick and xdotool packages are required!';

NativeDialog.alert({ message });

return false;
}
}

return true;
}

/** NOTE: This will only work on linux. */
private isInstalled(bin: string) {
const command = `command -v ${bin}`;
const output = execSync(command, { encoding: 'utf-8' }).trim();

return !!output;
}

async dispose() {
ipc.removeAllListeners('worker:solve');
ipc.removeAllListeners('state');
Expand Down Expand Up @@ -143,6 +169,8 @@ export class BreachProtocolWorker {
);

return new NirCmdRobot(this.settings, dpiScale);
case 'xdotool':
return new XDoToolRobot(this.settings);
default:
throw new Error(`Invalid engine "${this.settings.engine}" selected!`);
}
Expand Down
3 changes: 3 additions & 0 deletions types/constants/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ declare const BUGS_URL: string;

/** Product name from npm config. */
declare const PRODUCT_NAME: string;

/** Platform code from builded on. */
declare const BUILD_PLATFORM: NodeJS.Platform;

0 comments on commit 56cbfc1

Please sign in to comment.