Compare commits
No commits in common. "c48b8d72da242e48b6e2f01dc6e011e8b88d8948" and "17b5068644ebafb3c50e89fe6b0d359df891302c" have entirely different histories.
c48b8d72da
...
17b5068644
@ -25,6 +25,7 @@ InstallDirRegKey HKLM "Software\Tom Butcher\Farm Control" "InstallDir"
|
|||||||
RequestExecutionLevel admin
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
!define MUI_ABORTWARNING
|
!define MUI_ABORTWARNING
|
||||||
|
!define MUI_BRANDINGTEXT "Farm Control v${VERSION}-b${BUILD_NUMBER} Installer"
|
||||||
|
|
||||||
!ifndef INSTALLER_ICON
|
!ifndef INSTALLER_ICON
|
||||||
!define INSTALLER_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
|
!define INSTALLER_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
|
||||||
@ -42,8 +43,6 @@ UninstallIcon "${INSTALLER_ICON}"
|
|||||||
!insertmacro MUI_UNPAGE_INSTFILES
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
!insertmacro MUI_LANGUAGE "English"
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
|
||||||
!define MUI_BRANDINGTEXT "Farm Control v${VERSION}-b${BUILD_NUMBER} Installer"
|
|
||||||
|
|
||||||
Function .onInit
|
Function .onInit
|
||||||
${If} ${Silent}
|
${If} ${Silent}
|
||||||
SetAutoClose true
|
SetAutoClose true
|
||||||
|
|||||||
@ -28,40 +28,15 @@
|
|||||||
done_uninstall:
|
done_uninstall:
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro fixShortcutWorkingDir SHORTCUT_PATH WORKING_DIR
|
|
||||||
Push $0
|
|
||||||
Push $1
|
|
||||||
Push $2
|
|
||||||
StrCpy $1 "${SHORTCUT_PATH}"
|
|
||||||
StrCpy $2 "${WORKING_DIR}"
|
|
||||||
InitPluginsDir
|
|
||||||
FileOpen $0 "$PLUGINSDIR\fix-shortcut.ps1" w
|
|
||||||
FileWrite $0 '$$s = (New-Object -COM WScript.Shell).CreateShortcut("'
|
|
||||||
FileWrite $0 $1
|
|
||||||
FileWrite $0 '")$\r$\n'
|
|
||||||
FileWrite $0 '$$s.WorkingDirectory = "'
|
|
||||||
FileWrite $0 $2
|
|
||||||
FileWrite $0 '"$\r$\n'
|
|
||||||
FileWrite $0 '$$s.Save()$\r$\n'
|
|
||||||
FileClose $0
|
|
||||||
ExecWait '"$SYSDIR\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -File "$PLUGINSDIR\fix-shortcut.ps1"'
|
|
||||||
Delete "$PLUGINSDIR\fix-shortcut.ps1"
|
|
||||||
Pop $2
|
|
||||||
Pop $1
|
|
||||||
Pop $0
|
|
||||||
!macroend
|
|
||||||
|
|
||||||
!macro createDesktopShortcut
|
!macro createDesktopShortcut
|
||||||
SetShellVarContext all
|
SetShellVarContext all
|
||||||
CreateShortCut "$DESKTOP\Farm Control.lnk" "$INSTDIR\bin\launcher.exe" "" "$INSTDIR\bin\launcher.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
CreateShortCut "$DESKTOP\Farm Control.lnk" "$INSTDIR\bin\launcher.exe"
|
||||||
!insertmacro fixShortcutWorkingDir "$DESKTOP\Farm Control.lnk" "$INSTDIR\bin"
|
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro createStartMenuShortcut
|
!macro createStartMenuShortcut
|
||||||
SetShellVarContext all
|
SetShellVarContext all
|
||||||
CreateDirectory "$SMPROGRAMS\Farm Control"
|
CreateDirectory "$SMPROGRAMS\Farm Control"
|
||||||
CreateShortCut "$SMPROGRAMS\Farm Control\Farm Control.lnk" "$INSTDIR\bin\launcher.exe" "" "$INSTDIR\bin\launcher.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
CreateShortCut "$SMPROGRAMS\Farm Control\Farm Control.lnk" "$INSTDIR\bin\launcher.exe"
|
||||||
!insertmacro fixShortcutWorkingDir "$SMPROGRAMS\Farm Control\Farm Control.lnk" "$INSTDIR\bin"
|
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro removeDesktopShortcut
|
!macro removeDesktopShortcut
|
||||||
|
|||||||
@ -277,38 +277,6 @@ if (existsSync(rceditSrc)) {
|
|||||||
cpSync(rceditSrc, rceditDest, { recursive: true, force: true })
|
cpSync(rceditSrc, rceditDest, { recursive: true, force: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchNativeWrapperPath(nativeTsPath) {
|
|
||||||
if (!existsSync(nativeTsPath)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let source = readFileSync(nativeTsPath, 'utf8')
|
|
||||||
const needle =
|
|
||||||
'const nativeWrapperPath = join(process.cwd(), `libNativeWrapper.${suffix}`);'
|
|
||||||
const replacement =
|
|
||||||
'const nativeWrapperPath = join(dirname(process.execPath), `libNativeWrapper.${suffix}`);'
|
|
||||||
|
|
||||||
if (!source.includes(needle)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!source.includes('dirname')) {
|
|
||||||
source = source.replace(
|
|
||||||
'import { join } from "path";',
|
|
||||||
'import { dirname, join } from "path";'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
source = source.replace(needle, replacement)
|
|
||||||
writeFileSync(nativeTsPath, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const distDir of ['dist', 'dist-macos-arm64', 'dist-win-x64']) {
|
|
||||||
patchNativeWrapperPath(
|
|
||||||
path.join(electrobunDir, distDir, 'api/bun/proc/native.ts')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const markerPath = path.join(electrobunDir, '.farmcontrol-electrobun-patched')
|
const markerPath = path.join(electrobunDir, '.farmcontrol-electrobun-patched')
|
||||||
writeFileSync(markerPath, `patched-at=${new Date().toISOString()}\n`)
|
writeFileSync(markerPath, `patched-at=${new Date().toISOString()}\n`)
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,6 @@ if (!existsSync(launcherPath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const child = spawn(launcherPath, [], {
|
const child = spawn(launcherPath, [], {
|
||||||
cwd: dirname(launcherPath),
|
|
||||||
detached: true,
|
detached: true,
|
||||||
stdio: 'ignore',
|
stdio: 'ignore',
|
||||||
windowsHide: true
|
windowsHide: true
|
||||||
|
|||||||
@ -1,19 +1,9 @@
|
|||||||
import { chdirSync } from 'node:fs'
|
|
||||||
import { dirname } from 'node:path'
|
|
||||||
import {
|
import {
|
||||||
captureLaunchUrl,
|
captureLaunchUrl,
|
||||||
closeSingleInstanceServer,
|
closeSingleInstanceServer,
|
||||||
ensureSingleInstanceLock
|
ensureSingleInstanceLock
|
||||||
} from '../desktop/single-instance.js'
|
} from '../desktop/single-instance.js'
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
try {
|
|
||||||
chdirSync(dirname(process.execPath))
|
|
||||||
} catch {
|
|
||||||
// Best effort; some launchers may not allow changing directory.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const launchUrl = captureLaunchUrl()
|
const launchUrl = captureLaunchUrl()
|
||||||
const gotSingleInstanceLock = await ensureSingleInstanceLock({ launchUrl })
|
const gotSingleInstanceLock = await ensureSingleInstanceLock({ launchUrl })
|
||||||
|
|
||||||
|
|||||||
@ -5,10 +5,6 @@ import {
|
|||||||
MAC_TRAFFIC_LIGHT_OFFSET
|
MAC_TRAFFIC_LIGHT_OFFSET
|
||||||
} from './macos-window-effects.js'
|
} from './macos-window-effects.js'
|
||||||
import { sendToRenderer, setMessageSender } from './notify.js'
|
import { sendToRenderer, setMessageSender } from './notify.js'
|
||||||
import {
|
|
||||||
clampWindowToWorkArea,
|
|
||||||
isWindowWorkAreaMaximized
|
|
||||||
} from './windows-work-area.js'
|
|
||||||
|
|
||||||
const isMacOS = process.platform === 'darwin'
|
const isMacOS = process.platform === 'darwin'
|
||||||
const isWindows = process.platform === 'win32'
|
const isWindows = process.platform === 'win32'
|
||||||
@ -171,20 +167,12 @@ function syncWindowsWebviewLayout(window) {
|
|||||||
window.setSize(width, height)
|
window.setSize(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleWindowsWindowChange(window) {
|
|
||||||
if (clampWindowToWorkArea(window)) {
|
|
||||||
syncWindowsWebviewLayout(window)
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcastWindowState()
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyWindowsStartupWindowState(window) {
|
function applyWindowsStartupWindowState(window) {
|
||||||
if (!window) return
|
if (!window) return
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.maximize?.()
|
window.maximize?.()
|
||||||
handleWindowsWindowChange(window)
|
syncWindowsWebviewLayout(window)
|
||||||
setTimeout(() => syncWindowsWebviewLayout(window), 0)
|
setTimeout(() => syncWindowsWebviewLayout(window), 0)
|
||||||
setTimeout(() => syncWindowsWebviewLayout(window), 100)
|
setTimeout(() => syncWindowsWebviewLayout(window), 100)
|
||||||
setTimeout(broadcastWindowState, 100)
|
setTimeout(broadcastWindowState, 100)
|
||||||
@ -193,13 +181,9 @@ function applyWindowsStartupWindowState(window) {
|
|||||||
|
|
||||||
function setupWindowEvents(window) {
|
function setupWindowEvents(window) {
|
||||||
// Electrobun emits resize/focus, not Electron's maximize/fullscreen events.
|
// Electrobun emits resize/focus, not Electron's maximize/fullscreen events.
|
||||||
const onWindowChange = isWindows
|
window.on?.('resize', broadcastWindowState)
|
||||||
? () => handleWindowsWindowChange(window)
|
|
||||||
: broadcastWindowState
|
|
||||||
|
|
||||||
window.on?.('resize', onWindowChange)
|
|
||||||
window.on?.('focus', broadcastWindowState)
|
window.on?.('focus', broadcastWindowState)
|
||||||
window.on?.('move', onWindowChange)
|
window.on?.('move', broadcastWindowState)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupMainWindowMessaging(window = mainWindow) {
|
export function setupMainWindowMessaging(window = mainWindow) {
|
||||||
@ -330,9 +314,7 @@ export function getWindowState() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
isFullScreen: mainWindow.isFullScreen?.() ?? false,
|
isFullScreen: mainWindow.isFullScreen?.() ?? false,
|
||||||
isMaximized: isWindows
|
isMaximized: mainWindow.isMaximized?.() ?? false
|
||||||
? isWindowWorkAreaMaximized(mainWindow)
|
|
||||||
: (mainWindow.isMaximized?.() ?? false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,10 +331,6 @@ export function handleWindowControl(action) {
|
|||||||
} else {
|
} else {
|
||||||
mainWindow.maximize?.()
|
mainWindow.maximize?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWindows) {
|
|
||||||
handleWindowsWindowChange(mainWindow)
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
case 'fullscreen':
|
case 'fullscreen':
|
||||||
if (mainWindow.isFullScreen?.()) {
|
if (mainWindow.isFullScreen?.()) {
|
||||||
|
|||||||
@ -1,83 +0,0 @@
|
|||||||
import { Screen } from 'electrobun/bun'
|
|
||||||
|
|
||||||
const FRAME_TOLERANCE_PX = 4
|
|
||||||
|
|
||||||
function framesMatch(a, b, tolerance = FRAME_TOLERANCE_PX) {
|
|
||||||
return (
|
|
||||||
Math.abs(a.x - b.x) <= tolerance &&
|
|
||||||
Math.abs(a.y - b.y) <= tolerance &&
|
|
||||||
Math.abs(a.width - b.width) <= tolerance &&
|
|
||||||
Math.abs(a.height - b.height) <= tolerance
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFrameCenter(frame) {
|
|
||||||
return {
|
|
||||||
x: frame.x + frame.width / 2,
|
|
||||||
y: frame.y + frame.height / 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDisplayForFrame(frame) {
|
|
||||||
const { x, y } = getFrameCenter(frame)
|
|
||||||
const displays = Screen.getAllDisplays()
|
|
||||||
|
|
||||||
const match = displays.find(({ bounds }) => {
|
|
||||||
return (
|
|
||||||
x >= bounds.x &&
|
|
||||||
x < bounds.x + bounds.width &&
|
|
||||||
y >= bounds.y &&
|
|
||||||
y < bounds.y + bounds.height
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return match ?? Screen.getPrimaryDisplay()
|
|
||||||
}
|
|
||||||
|
|
||||||
function frameCoversMonitor(frame, bounds) {
|
|
||||||
return (
|
|
||||||
frame.x <= bounds.x + FRAME_TOLERANCE_PX &&
|
|
||||||
frame.y <= bounds.y + FRAME_TOLERANCE_PX &&
|
|
||||||
frame.x + frame.width >= bounds.x + bounds.width - FRAME_TOLERANCE_PX &&
|
|
||||||
frame.y + frame.height >= bounds.y + bounds.height - FRAME_TOLERANCE_PX
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isWindowWorkAreaMaximized(window) {
|
|
||||||
if (!window?.getFrame) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const frame = window.getFrame()
|
|
||||||
const { workArea } = getDisplayForFrame(frame)
|
|
||||||
return framesMatch(frame, workArea)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clampWindowToWorkArea(window) {
|
|
||||||
if (!window?.getFrame || !window?.setFrame) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.isFullScreen?.()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const frame = window.getFrame()
|
|
||||||
if (!frame.width || !frame.height) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const display = getDisplayForFrame(frame)
|
|
||||||
const { bounds, workArea } = display
|
|
||||||
|
|
||||||
if (framesMatch(frame, workArea)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frameCoversMonitor(frame, bounds)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
window.setFrame(workArea.x, workArea.y, workArea.width, workArea.height)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user