154 lines
4.7 KiB
TypeScript
154 lines
4.7 KiB
TypeScript
import { BrowserWindow, desktopCapturer, screen } from 'electron'
|
|
import * as path from 'path'
|
|
import * as fs from 'fs'
|
|
import log from 'electron-log'
|
|
|
|
let screenshotWindow: BrowserWindow | null = null
|
|
let capturedImage: Buffer | null = null
|
|
let capturedBase64: string | null = null
|
|
let isScreenshotActive = false
|
|
let lastScreenshotTime = 0
|
|
const SCREENSHOT_COOLDOWN = 2000
|
|
|
|
export function startScreenshot(mainWin: BrowserWindow): void {
|
|
const now = Date.now()
|
|
if (now - lastScreenshotTime < SCREENSHOT_COOLDOWN) {
|
|
log.warn('Screenshot on cooldown, ignoring')
|
|
return
|
|
}
|
|
if (isScreenshotActive) {
|
|
log.warn('Screenshot already active, ignoring')
|
|
return
|
|
}
|
|
isScreenshotActive = true
|
|
lastScreenshotTime = now
|
|
require('./shortcut').disableShortcut()
|
|
mainWin.hide()
|
|
|
|
const cursor = screen.getCursorScreenPoint()
|
|
const displays = screen.getAllDisplays()
|
|
const targetDisplay = displays.find(d => {
|
|
return cursor.x >= d.bounds.x && cursor.x <= d.bounds.x + d.bounds.width &&
|
|
cursor.y >= d.bounds.y && cursor.y <= d.bounds.y + d.bounds.height
|
|
}) || displays[0]
|
|
|
|
const bounds = targetDisplay.bounds
|
|
|
|
screenshotWindow = new BrowserWindow({
|
|
x: bounds.x,
|
|
y: bounds.y,
|
|
width: bounds.width,
|
|
height: bounds.height,
|
|
frame: false,
|
|
transparent: true,
|
|
alwaysOnTop: true,
|
|
skipTaskbar: true,
|
|
resizable: false,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, '../preload/preload.js'),
|
|
contextIsolation: true,
|
|
nodeIntegration: false
|
|
}
|
|
})
|
|
|
|
if (process.env.NODE_ENV === 'development' || process.argv.includes('--dev')) {
|
|
screenshotWindow.loadURL(`http://localhost:5173/#/screenshot`)
|
|
} else {
|
|
screenshotWindow.loadFile(path.join(__dirname, '../../dist/index.html'), { hash: '/screenshot' })
|
|
}
|
|
|
|
screenshotWindow.on('closed', () => {
|
|
isScreenshotActive = false
|
|
screenshotWindow = null
|
|
})
|
|
}
|
|
|
|
export async function captureScreen(region: { x: number; y: number; width: number; height: number }): Promise<string | null> {
|
|
try {
|
|
const displays = screen.getAllDisplays()
|
|
const sources = await desktopCapturer.getSources({
|
|
types: ['screen'],
|
|
thumbnailSize: { width: 4096, height: 2160 }
|
|
})
|
|
|
|
if (sources.length === 0) return null
|
|
|
|
const cursor = screen.getCursorScreenPoint()
|
|
const targetDisplay = displays.find(d => {
|
|
return cursor.x >= d.bounds.x && cursor.x <= d.bounds.x + d.bounds.width &&
|
|
cursor.y >= d.bounds.y && cursor.y <= d.bounds.y + d.bounds.height
|
|
}) || displays[0]
|
|
|
|
const sourceIndex = displays.findIndex(d => d.id === targetDisplay.id)
|
|
const source = sources[Math.min(sourceIndex, sources.length - 1)]
|
|
|
|
const fullImage = source.thumbnail
|
|
const displayWidth = targetDisplay.bounds.width
|
|
const displayHeight = targetDisplay.bounds.height
|
|
const scaleX = fullImage.getSize().width / displayWidth
|
|
const scaleY = fullImage.getSize().height / displayHeight
|
|
|
|
const cropX = Math.round(region.x * scaleX)
|
|
const cropY = Math.round(region.y * scaleY)
|
|
const cropW = Math.round(region.width * scaleX)
|
|
const cropH = Math.round(region.height * scaleY)
|
|
|
|
const cropped = fullImage.crop({
|
|
x: Math.max(0, cropX),
|
|
y: Math.max(0, cropY),
|
|
width: Math.min(cropW, fullImage.getSize().width - cropX),
|
|
height: Math.min(cropH, fullImage.getSize().height - cropY)
|
|
})
|
|
|
|
const pngBuffer = cropped.toPNG()
|
|
const base64 = pngBuffer.toString('base64')
|
|
capturedImage = pngBuffer
|
|
capturedBase64 = base64
|
|
|
|
try {
|
|
const Store = require('electron-store').default
|
|
const store = new Store()
|
|
const savePath = store.get('screenshotSavePath', '')
|
|
if (savePath) {
|
|
const timestamp = Date.now()
|
|
const filename = `screenshot_${timestamp}.png`
|
|
const fullPath = path.join(savePath, filename)
|
|
fs.writeFileSync(fullPath, pngBuffer)
|
|
log.info('Screenshot saved to', fullPath)
|
|
}
|
|
} catch (e) {
|
|
log.error('Failed to save screenshot:', e)
|
|
}
|
|
|
|
log.info('Screenshot captured', { width: region.width, height: region.height })
|
|
return base64
|
|
} catch (err) {
|
|
log.error('Screenshot capture error:', err)
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function closeScreenshot(): void {
|
|
if (screenshotWindow && !screenshotWindow.isDestroyed()) {
|
|
screenshotWindow.close()
|
|
if (!screenshotWindow.isDestroyed()) {
|
|
screenshotWindow.destroy()
|
|
}
|
|
}
|
|
screenshotWindow = null
|
|
isScreenshotActive = false
|
|
}
|
|
|
|
export function getCapturedImage(): Buffer | null {
|
|
return capturedImage
|
|
}
|
|
|
|
export function getCapturedBase64(): string | null {
|
|
return capturedBase64
|
|
}
|
|
|
|
export function clearCapturedImage(): void {
|
|
capturedImage = null
|
|
capturedBase64 = null
|
|
}
|