const { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage, dialog } = require('electron'); const path = require('path'); const Store = require('electron-store'); const si = require('systeminformation'); const usb = require('usb'); const store = new Store({ defaults: { preferences: {} } }); let mainWindow = null; let tray = null; function createTrayIcon() { const size = 16; const canvas = Buffer.alloc(size * size * 4); for (let y = 0; y < size; y++) { for (let x = 0; x < size; x++) { const idx = (y * size + x) * 4; const cx = x - size / 2, cy = y - size / 2; if (Math.sqrt(cx * cx + cy * cy) < size / 2 - 1) { canvas[idx] = 0; canvas[idx + 1] = 120; canvas[idx + 2] = 255; canvas[idx + 3] = 255; } else { canvas[idx] = 0; canvas[idx + 1] = 0; canvas[idx + 2] = 0; canvas[idx + 3] = 0; } } } return nativeImage.createFromBuffer(canvas, { width: size, height: size }); } function createWindow() { mainWindow = new BrowserWindow({ width: 900, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, }, }); mainWindow.loadFile(path.join(__dirname, 'src', 'index.html')); mainWindow.on('close', (e) => { if (!app.isQuitting) { e.preventDefault(); mainWindow.hide(); } }); } function createTray() { tray = new Tray(createTrayIcon()); tray.setToolTip('Corsair LCD Control'); const contextMenu = Menu.buildFromTemplate([ { label: 'Show', click: () => mainWindow.show() }, { label: 'Hide', click: () => mainWindow.hide() }, { type: 'separator' }, { label: 'Quit', click: () => { app.isQuitting = true; app.quit(); } }, ]); tray.setContextMenu(contextMenu); tray.on('double-click', () => mainWindow.show()); } const VENDORS = { 0x1B1C: 'Corsair', 0x046D: 'Logitech', 0x0A5C: 'Broadcom', 0x8087: 'Intel', 0x05E3: 'Genesys Logic', 0x04F2: 'Chicony', 0x0BDA: 'Realtek', 0x1D6B: 'Linux Foundation', 0x3034: 'Realtek', 0x5964: 'Asmedia', }; function formatHexId(vendorId, productId) { const hex = `${vendorId.toString(16).padStart(4, '0')}:${productId.toString(16).padStart(4, '0')}`; const name = VENDORS[vendorId] || ''; return name ? `${name} [${hex}]` : hex; } function readStringDescriptor(device, index) { return new Promise((resolve) => { if (!index) return resolve(''); try { device.getStringDescriptor(index, (err, desc) => { resolve(err ? '' : desc); }); } catch { resolve(''); } }); } // --- USB --- ipcMain.handle('usb:listDevices', async () => { try { const devices = usb.getDeviceList(); const results = []; for (const d of devices) { const entry = { vendorId: d.deviceDescriptor.idVendor, productId: d.deviceDescriptor.idProduct, displayName: formatHexId(d.deviceDescriptor.idVendor, d.deviceDescriptor.idProduct), manufacturer: '', product: '', serialNumber: '', }; try { d.open(true); const [mfg, prod, sn] = await Promise.all([ readStringDescriptor(d, d.deviceDescriptor.iManufacturer), readStringDescriptor(d, d.deviceDescriptor.iProduct), readStringDescriptor(d, d.deviceDescriptor.iSerialNumber), ]); if (mfg || prod) { entry.manufacturer = mfg; entry.product = prod; entry.displayName = [mfg, prod].filter(Boolean).join(' '); } entry.serialNumber = sn; d.close(); } catch { // no permission — keep the hex-based displayName } results.push(entry); } return results; } catch (err) { return { error: err.message }; } }); ipcMain.handle('usb:connect', async (_e, { vendorId, productId }) => { try { const device = usb.findByIds(vendorId, productId); if (!device) throw new Error('Device not found'); device.open(); return { success: true }; } catch (err) { return { error: err.message }; } }); // --- Preferences --- ipcMain.handle('prefs:get', (_e, key) => { if (key) return store.get(key); return store.store; }); ipcMain.handle('prefs:set', (_e, key, value) => { store.set(key, value); return { success: true }; }); ipcMain.handle('prefs:delete', (_e, key) => { store.delete(key); return { success: true }; }); // --- File Picker --- ipcMain.handle('dialog:openFile', async (_e, options) => { const result = await dialog.showOpenDialog(mainWindow, { properties: ['openFile'], ...options, }); return result; }); ipcMain.handle('dialog:saveFile', async (_e, options) => { const result = await dialog.showSaveDialog(mainWindow, options); return result; }); // --- System Info --- ipcMain.handle('system:cpuTemp', async () => { try { const data = await si.cpuTemperature(); return data; } catch (err) { return { error: err.message }; } }); ipcMain.handle('system:ram', async () => { try { const data = await si.mem(); return { total: data.total, free: data.free, used: data.used, usedPercent: ((data.used / data.total) * 100).toFixed(1), }; } catch (err) { return { error: err.message }; } }); ipcMain.handle('system:gpu', async () => { try { const data = await si.graphics(); const controllers = data.controllers.map((c) => ({ vendor: c.vendor, model: c.model, temperatureGpu: c.temperatureGpu, memoryUsed: c.memoryUsed, memoryTotal: c.memoryTotal, })); return controllers; } catch (err) { return { error: err.message }; } }); app.whenReady().then(() => { createWindow(); createTray(); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); });