const {app, BrowserWindow, Menu, ipcMain, shell} = require('electron') const path = require('path') const url = require('url') const { autoUpdater } = require('electron-updater'); const fs = require('fs'); const {getColorHexRGB} = require('electron-color-picker') require('@electron/remote/main').initialize() let orig_win; let all_wins = []; let load_project_data; (() => { // Allow advanced users to specify a custom userData directory. // Useful for portable installations, and for setting up development environments. const index = process.argv.findIndex(arg => arg === '--userData'); if (index !== -1) { if (!process.argv.at(index + 1)) { console.error('No path specified after --userData') process.exit(1) } app.setPath('userData', process.argv[index + 1]); } })() const LaunchSettings = { path: path.join(app.getPath('userData'), 'launch_settings.json'), settings: {}, get(key) { return this.settings[key] }, set(key, value) { this.settings[key] = value; let content = JSON.stringify(this.settings, null, '\t'); fs.writeFileSync(this.path, content); }, load() { try { if (fs.existsSync(this.path)) { let content = fs.readFileSync(this.path, 'utf-8'); this.settings = JSON.parse(content); } } catch (error) {} return this; } }.load(); if (LaunchSettings.get('hardware_acceleration') == false) { app.disableHardwareAcceleration(); } function createWindow(second_instance, options = {}) { if (app.requestSingleInstanceLock && !app.requestSingleInstanceLock()) { app.quit() return; } let win_options = { icon: 'icon.ico', show: false, backgroundColor: '#21252b', frame: LaunchSettings.get('native_window_frame') === true, titleBarStyle: 'hidden', minWidth: 640, minHeight: 480, width: 1080, height: 720, webPreferences: { webgl: true, webSecurity: true, nodeIntegration: true, contextIsolation: false, enableRemoteModule: true } }; if (options.position) { win_options.x = options.position[0] - 300; win_options.y = Math.max(options.position[1] - 100, 0); } let win = new BrowserWindow(win_options) if (!orig_win) orig_win = win; all_wins.push(win); require('@electron/remote/main').enable(win.webContents) var index_path = path.join(__dirname, 'index.html') if (process.platform === 'darwin') { let template = [ { "label": "Blockbench", "submenu": [ { "role": "hide" }, { "role": "hideothers" }, { "role": "unhide" }, { "type": "separator" }, { "role": "quit" } ] }, { "label": "Edit", "submenu": [ { "role": "cut" }, { "role": "copy" }, { "role": "paste" }, { "role": "selectall" } ] }, { "label": "Window", "role": "window", "submenu": [ { "label": "Toggle Full Screen", "accelerator": "Ctrl+Command+F" }, { "role": "minimize" }, { "role": "close" }, { "type": "separator" }, { "role": "front" } ] } ] var osxMenu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(osxMenu) } else { win.setMenu(null); } if (options.maximize !== false) win.maximize() win.show() win.loadURL(url.format({ pathname: index_path, protocol: 'file:', slashes: true })) win.on('closed', () => { win = null; all_wins.splice(all_wins.indexOf(win), 1); }) if (second_instance === true) { win.webContents.second_instance = true; } return win; } app.commandLine.appendSwitch('ignore-gpu-blacklist') app.commandLine.appendSwitch('ignore-gpu-blocklist') app.commandLine.appendSwitch('enable-accelerated-video') app.on('second-instance', function (event, argv, cwd) { process.argv = argv; let win = all_wins.find(win => !win.isDestroyed()); if (win && argv[argv.length-1 || 1] && argv[argv.length-1 || 1].substr(0, 2) !== '--') { win.webContents.send('open-model', argv[argv.length-1 || 1]); win.focus(); } else { createWindow(true); } }) app.on('open-file', function (event, path) { process.argv[process.argv.length-1 || 1] = path; let win = all_wins.find(win => !win.isDestroyed()); if (win) { win.webContents.send('open-model', path); } }) ipcMain.on('edit-launch-setting', (event, arg) => { LaunchSettings.set(arg.key, arg.value); }) ipcMain.on('add-recent-project', (event, path) => { app.addRecentDocument(path); }) ipcMain.on('dragging-tab', (event, value) => { all_wins.forEach(win => { if (win.isDestroyed() || win.id == event.sender.id) return; win.webContents.send('accept-detached-tab', JSON.parse(value)); }) }) ipcMain.on('new-window', (event, data, position) => { if (typeof data == 'string') load_project_data = JSON.parse(data); if (position) { position = JSON.parse(position) let place_in_window = all_wins.find(win => { if (win.isDestroyed() || win.webContents == event.sender || win.isMinimized()) return false; let pos = win.getPosition(); let size = win.getSize(); return (position.offset[0] >= pos[0] && position.offset[0] <= pos[0] + size[0] && position.offset[1] >= pos[1] && position.offset[1] <= pos[1] + size[1]); }) if (place_in_window) { place_in_window.send('load-tab', load_project_data); place_in_window.focus(); load_project_data = null; } else { createWindow(true, { maximize: false, position: position.offset }); } } else { createWindow(true); } }) ipcMain.on('close-detached-project', async (event, window_id, uuid) => { let window = all_wins.find(win => win.id == window_id); if (window) window.send('close-detached-project', uuid); }) ipcMain.on('request-color-picker', async (event, arg) => { const color = await getColorHexRGB().catch((error) => { console.warn('[Error] Failed to pick color', error) return '' }) if (color) { all_wins.forEach(win => { if (win.isDestroyed() || (!arg.sync && win.webContents.getProcessId() != event.sender.getProcessId())) return; win.webContents.send('set-main-color', color) }) } }) ipcMain.on('show-item-in-folder', async (event, path) => { shell.showItemInFolder(path); }) app.on('ready', () => { createWindow() let app_was_loaded = false; ipcMain.on('app-loaded', () => { if (load_project_data) { all_wins[all_wins.length-1].send('load-tab', load_project_data); load_project_data = null; } if (app_was_loaded) { console.log('[Blockbench] App reloaded or new window opened') return; } app_was_loaded = true; if (process.execPath && process.execPath.match(/node_modules[\\\/]electron/)) { console.log('[Blockbench] App launched in development mode') } else { autoUpdater.autoInstallOnAppQuit = true; autoUpdater.autoDownload = false; if (LaunchSettings.get('update_to_prereleases') === true) { autoUpdater.allowPrerelease = true; //autoUpdater.channel = 'beta'; } autoUpdater.on('update-available', (a) => { console.log('update-available', a) ipcMain.on('allow-auto-update', () => { autoUpdater.downloadUpdate() }) if (!orig_win.isDestroyed()) orig_win.webContents.send('update-available', a); }) autoUpdater.on('update-downloaded', (a) => { console.log('update-downloaded', a) if (!orig_win.isDestroyed()) orig_win.webContents.send('update-downloaded', a) }) autoUpdater.on('error', (a) => { console.log('update-error', a) if (!orig_win.isDestroyed()) orig_win.webContents.send('update-error', a) }) autoUpdater.on('download-progress', (a) => { console.log('update-progress', a) if (!orig_win.isDestroyed()) orig_win.webContents.send('update-progress', a) }) autoUpdater.checkForUpdates().catch(err => {}) } }) }) app.on('window-all-closed', () => { app.quit() })