parent
c387979be1
commit
27c46d0b8e
@ -0,0 +1,73 @@ |
||||
/* |
||||
* SPDX-License-Identifier: GPL-3.0 |
||||
* Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
|
||||
* Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! |
||||
*/ |
||||
|
||||
import { app } from "electron"; |
||||
import { BrowserWindow } from "electron/main"; |
||||
import { copyFileSync, mkdirSync, readdirSync } from "fs"; |
||||
import { join } from "path"; |
||||
import { SplashProps } from "shared/browserWinProperties"; |
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths"; |
||||
|
||||
import { autoStart } from "./autoStart"; |
||||
import { DATA_DIR } from "./constants"; |
||||
import { createWindows } from "./mainWindow"; |
||||
import { Settings, State } from "./settings"; |
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; |
||||
|
||||
interface Data { |
||||
minimizeToTray: boolean; |
||||
discordBranch: "stable" | "canary" | "ptb"; |
||||
autoStart: boolean; |
||||
importSettings: boolean; |
||||
richPresence: boolean; |
||||
} |
||||
|
||||
export function createFirstLaunchTour() { |
||||
const win = new BrowserWindow({ |
||||
...SplashProps, |
||||
frame: true, |
||||
autoHideMenuBar: true, |
||||
height: 470, |
||||
width: 550, |
||||
icon: ICON_PATH |
||||
}); |
||||
|
||||
makeLinksOpenExternally(win); |
||||
|
||||
win.loadFile(join(VIEW_DIR, "first-launch.html")); |
||||
win.webContents.addListener("console-message", (_e, _l, msg) => { |
||||
if (msg === "cancel") return app.exit(); |
||||
|
||||
if (!msg.startsWith("form:")) return; |
||||
const data = JSON.parse(msg.slice(5)) as Data; |
||||
|
||||
State.store.firstLaunch = false; |
||||
Settings.store.minimizeToTray = data.minimizeToTray; |
||||
Settings.store.discordBranch = data.discordBranch; |
||||
Settings.store.arRPC = data.richPresence; |
||||
|
||||
if (data.autoStart) autoStart.enable(); |
||||
|
||||
if (data.importSettings) { |
||||
const from = join(app.getPath("userData"), "..", "Vencord", "settings"); |
||||
const to = join(DATA_DIR, "settings"); |
||||
try { |
||||
const files = readdirSync(from); |
||||
mkdirSync(to, { recursive: true }); |
||||
|
||||
for (const file of files) { |
||||
copyFileSync(join(from, file), join(to, file)); |
||||
} |
||||
} catch (e) { |
||||
console.error("Failed to import settings:", e); |
||||
} |
||||
} |
||||
|
||||
win.close(); |
||||
|
||||
createWindows(); |
||||
}); |
||||
} |
@ -0,0 +1,108 @@ |
||||
/* |
||||
* SPDX-License-Identifier: GPL-3.0 |
||||
* Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
|
||||
* Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! |
||||
*/ |
||||
|
||||
import "./ipc"; |
||||
|
||||
import { app, BrowserWindow, nativeTheme } from "electron"; |
||||
import { checkUpdates } from "updater/main"; |
||||
|
||||
import { DATA_DIR } from "./constants"; |
||||
import { createFirstLaunchTour } from "./firstLaunch"; |
||||
import { createWindows, mainWin } from "./mainWindow"; |
||||
import { registerMediaPermissionsHandler } from "./mediaPermissions"; |
||||
import { registerScreenShareHandler } from "./screenShare"; |
||||
import { Settings, State } from "./settings"; |
||||
import { isDeckGameMode } from "./utils/steamOS"; |
||||
|
||||
if (IS_DEV) { |
||||
require("source-map-support").install(); |
||||
} |
||||
|
||||
// Make the Vencord files use our DATA_DIR
|
||||
process.env.VENCORD_USER_DATA_DIR = DATA_DIR; |
||||
|
||||
function init() { |
||||
const { disableSmoothScroll, hardwareAcceleration } = Settings.store; |
||||
|
||||
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(","); |
||||
const disabledFeatures = app.commandLine.getSwitchValue("disable-features").split(","); |
||||
|
||||
if (hardwareAcceleration === false) { |
||||
app.disableHardwareAcceleration(); |
||||
} else { |
||||
enabledFeatures.push("VaapiVideoDecodeLinuxGL", "VaapiVideoEncoder", "VaapiVideoDecoder"); |
||||
} |
||||
|
||||
if (disableSmoothScroll) { |
||||
app.commandLine.appendSwitch("disable-smooth-scrolling"); |
||||
} |
||||
|
||||
// work around chrome 66 disabling autoplay by default
|
||||
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required"); |
||||
// WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
|
||||
// HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
|
||||
//
|
||||
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
|
||||
disabledFeatures.push( |
||||
"WinRetrieveSuggestionsOnlyOnDemand", |
||||
"HardwareMediaKeyHandling", |
||||
"MediaSessionService", |
||||
"WidgetLayering" |
||||
); |
||||
|
||||
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].filter(Boolean).join(",")); |
||||
app.commandLine.appendSwitch("disable-features", [...new Set(disabledFeatures)].filter(Boolean).join(",")); |
||||
|
||||
// In the Flatpak on SteamOS the theme is detected as light, but SteamOS only has a dark mode, so we just override it
|
||||
if (isDeckGameMode) nativeTheme.themeSource = "dark"; |
||||
|
||||
app.on("second-instance", (_event, _cmdLine, _cwd, data: any) => { |
||||
if (data.IS_DEV) app.quit(); |
||||
else if (mainWin) { |
||||
if (mainWin.isMinimized()) mainWin.restore(); |
||||
if (!mainWin.isVisible()) mainWin.show(); |
||||
mainWin.focus(); |
||||
} |
||||
}); |
||||
|
||||
app.whenReady().then(async () => { |
||||
checkUpdates(); |
||||
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop"); |
||||
|
||||
registerScreenShareHandler(); |
||||
registerMediaPermissionsHandler(); |
||||
|
||||
bootstrap(); |
||||
|
||||
app.on("activate", () => { |
||||
if (BrowserWindow.getAllWindows().length === 0) createWindows(); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
if (!app.requestSingleInstanceLock({ IS_DEV })) { |
||||
if (IS_DEV) { |
||||
console.log("Vesktop is already running. Quitting previous instance..."); |
||||
init(); |
||||
} else { |
||||
console.log("Vesktop is already running. Quitting..."); |
||||
app.quit(); |
||||
} |
||||
} else { |
||||
init(); |
||||
} |
||||
|
||||
async function bootstrap() { |
||||
if (!Object.hasOwn(State.store, "firstLaunch")) { |
||||
createFirstLaunchTour(); |
||||
} else { |
||||
createWindows(); |
||||
} |
||||
} |
||||
|
||||
app.on("window-all-closed", () => { |
||||
if (process.platform !== "darwin") app.quit(); |
||||
}); |
@ -0,0 +1,155 @@ |
||||
/* |
||||
* SPDX-License-Identifier: GPL-3.0 |
||||
* Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
|
||||
* Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! |
||||
*/ |
||||
|
||||
if (process.platform === "linux") import("./venmic"); |
||||
|
||||
import { execFile } from "child_process"; |
||||
import { app, BrowserWindow, clipboard, dialog, nativeImage, RelaunchOptions, session, shell } from "electron"; |
||||
import { mkdirSync, readFileSync, watch } from "fs"; |
||||
import { open, readFile } from "fs/promises"; |
||||
import { release } from "os"; |
||||
import { join } from "path"; |
||||
import { debounce } from "shared/utils/debounce"; |
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents"; |
||||
import { setBadgeCount } from "./appBadge"; |
||||
import { autoStart } from "./autoStart"; |
||||
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants"; |
||||
import { mainWin } from "./mainWindow"; |
||||
import { Settings } from "./settings"; |
||||
import { handle, handleSync } from "./utils/ipcWrappers"; |
||||
import { PopoutWindows } from "./utils/popout"; |
||||
import { isDeckGameMode, showGamePage } from "./utils/steamOS"; |
||||
import { isValidVencordInstall } from "./utils/vencordLoader"; |
||||
|
||||
handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js")); |
||||
handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () => |
||||
readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8") |
||||
); |
||||
|
||||
handleSync(IpcEvents.GET_RENDERER_SCRIPT, () => readFileSync(join(__dirname, "renderer.js"), "utf-8")); |
||||
handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css")); |
||||
|
||||
handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain); |
||||
handleSync(IpcEvents.GET_VERSION, () => app.getVersion()); |
||||
|
||||
handleSync( |
||||
IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY, |
||||
() => process.platform === "win32" && Number(release().split(".").pop()) >= 22621 |
||||
); |
||||
|
||||
handleSync(IpcEvents.AUTOSTART_ENABLED, () => autoStart.isEnabled()); |
||||
handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable); |
||||
handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable); |
||||
|
||||
handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => { |
||||
Settings.setData(settings, path); |
||||
}); |
||||
|
||||
handle(IpcEvents.RELAUNCH, async () => { |
||||
const options: RelaunchOptions = { |
||||
args: process.argv.slice(1).concat(["--relaunch"]) |
||||
}; |
||||
if (isDeckGameMode) { |
||||
// We can't properly relaunch when running under gamescope, but we can at least navigate to our page in Steam.
|
||||
await showGamePage(); |
||||
} else if (app.isPackaged && process.env.APPIMAGE) { |
||||
execFile(process.env.APPIMAGE, options.args); |
||||
} else { |
||||
app.relaunch(options); |
||||
} |
||||
app.exit(); |
||||
}); |
||||
|
||||
handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => { |
||||
shell.showItemInFolder(path); |
||||
}); |
||||
|
||||
handle(IpcEvents.FOCUS, () => { |
||||
mainWin.show(); |
||||
mainWin.setSkipTaskbar(false); |
||||
}); |
||||
|
||||
handle(IpcEvents.CLOSE, (e, key?: string) => { |
||||
const popout = PopoutWindows.get(key!); |
||||
if (popout) return popout.close(); |
||||
|
||||
const win = BrowserWindow.fromWebContents(e.sender) ?? e.sender; |
||||
win.close(); |
||||
}); |
||||
|
||||
handle(IpcEvents.MINIMIZE, e => { |
||||
mainWin.minimize(); |
||||
}); |
||||
|
||||
handle(IpcEvents.MAXIMIZE, e => { |
||||
if (mainWin.isMaximized()) { |
||||
mainWin.unmaximize(); |
||||
} else { |
||||
mainWin.maximize(); |
||||
} |
||||
}); |
||||
|
||||
handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => { |
||||
const ses = session.defaultSession; |
||||
|
||||
const available = ses.availableSpellCheckerLanguages; |
||||
const applicable = languages.filter(l => available.includes(l)).slice(0, 3); |
||||
if (applicable.length) ses.setSpellCheckerLanguages(applicable); |
||||
}); |
||||
|
||||
handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => { |
||||
e.sender.replaceMisspelling(word); |
||||
}); |
||||
|
||||
handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => { |
||||
e.sender.session.addWordToSpellCheckerDictionary(word); |
||||
}); |
||||
|
||||
handle(IpcEvents.SELECT_VENCORD_DIR, async () => { |
||||
const res = await dialog.showOpenDialog(mainWin!, { |
||||
properties: ["openDirectory"] |
||||
}); |
||||
if (!res.filePaths.length) return "cancelled"; |
||||
|
||||
const dir = res.filePaths[0]; |
||||
if (!isValidVencordInstall(dir)) return "invalid"; |
||||
|
||||
return dir; |
||||
}); |
||||
|
||||
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count)); |
||||
|
||||
handle(IpcEvents.CLIPBOARD_COPY_IMAGE, async (_, buf: ArrayBuffer, src: string) => { |
||||
clipboard.write({ |
||||
html: `<img src="${src.replaceAll('"', '\\"')}">`, |
||||
image: nativeImage.createFromBuffer(Buffer.from(buf)) |
||||
}); |
||||
}); |
||||
|
||||
function readCss() { |
||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => ""); |
||||
} |
||||
|
||||
open(VENCORD_QUICKCSS_FILE, "a+").then(fd => { |
||||
fd.close(); |
||||
watch( |
||||
VENCORD_QUICKCSS_FILE, |
||||
{ persistent: false }, |
||||
debounce(async () => { |
||||
mainWin?.webContents.postMessage("VencordQuickCssUpdate", await readCss()); |
||||
}, 50) |
||||
); |
||||
}); |
||||
|
||||
mkdirSync(VENCORD_THEMES_DIR, { recursive: true }); |
||||
watch( |
||||
VENCORD_THEMES_DIR, |
||||
{ persistent: false }, |
||||
debounce(() => { |
||||
mainWin?.webContents.postMessage("VencordThemeUpdate", void 0); |
||||
}) |
||||
); |
@ -0,0 +1,488 @@ |
||||
/* |
||||
* SPDX-License-Identifier: GPL-3.0 |
||||
* Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
|
||||
* Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! |
||||
*/ |
||||
|
||||
import { |
||||
app, |
||||
BrowserWindow, |
||||
BrowserWindowConstructorOptions, |
||||
dialog, |
||||
Menu, |
||||
MenuItemConstructorOptions, |
||||
nativeTheme, |
||||
shell, |
||||
Tray |
||||
} from "electron"; |
||||
import { rm } from "fs/promises"; |
||||
import { join } from "path"; |
||||
import { IpcEvents } from "shared/IpcEvents"; |
||||
import { isTruthy } from "shared/utils/guards"; |
||||
import { once } from "shared/utils/once"; |
||||
import type { SettingsStore } from "shared/utils/SettingsStore"; |
||||
|
||||
import { ICON_PATH } from "../shared/paths"; |
||||
import { createAboutWindow } from "./about"; |
||||
import { initArRPC } from "./arrpc"; |
||||
import { |
||||
BrowserUserAgent, |
||||
DATA_DIR, |
||||
DEFAULT_HEIGHT, |
||||
DEFAULT_WIDTH, |
||||
MessageBoxChoice, |
||||
MIN_HEIGHT, |
||||
MIN_WIDTH, |
||||
VENCORD_FILES_DIR |
||||
} from "./constants"; |
||||
import { Settings, State, VencordSettings } from "./settings"; |
||||
import { createSplashWindow } from "./splash"; |
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; |
||||
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS"; |
||||
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader"; |
||||
|
||||
let isQuitting = false; |
||||
let tray: Tray; |
||||
|
||||
applyDeckKeyboardFix(); |
||||
|
||||
app.on("before-quit", () => { |
||||
isQuitting = true; |
||||
}); |
||||
|
||||
export let mainWin: BrowserWindow; |
||||
|
||||
function makeSettingsListenerHelpers<O extends object>(o: SettingsStore<O>) { |
||||
const listeners = new Map<(data: any) => void, PropertyKey>(); |
||||
|
||||
const addListener: typeof o.addChangeListener = (path, cb) => { |
||||
listeners.set(cb, path); |
||||
o.addChangeListener(path, cb); |
||||
}; |
||||
const removeAllListeners = () => { |
||||
for (const [listener, path] of listeners) { |
||||
o.removeChangeListener(path as any, listener); |
||||
} |
||||
|
||||
listeners.clear(); |
||||
}; |
||||
|
||||
return [addListener, removeAllListeners] as const; |
||||
} |
||||
|
||||
const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpers(Settings); |
||||
const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings); |
||||
|
||||
function initTray(win: BrowserWindow) { |
||||
const onTrayClick = () => { |
||||
if (Settings.store.clickTrayToShowHide && win.isVisible()) win.hide(); |
||||
else win.show(); |
||||
}; |
||||
const trayMenu = Menu.buildFromTemplate([ |
||||
{ |
||||
label: "Open", |
||||
click() { |
||||
win.show(); |
||||
} |
||||
}, |
||||
{ |
||||
label: "About and More", |
||||
click: createAboutWindow |
||||
}, |
||||
{ |
||||
label: "Fix/Repair Vencord", |
||||
async click() { |
||||
await downloadVencordFiles(); |
||||
app.relaunch(); |
||||
app.quit(); |
||||
} |
||||
}, |
||||
{ |
||||
label: "Reset User Data", |
||||
async click() { |
||||
await clearData(win); |
||||
} |
||||
}, |
||||
{ |
||||
type: "separator" |
||||
}, |
||||
{ |
||||
label: "Open Updater", |
||||
click() { |
||||
const updaterPath = join(app.getPath('exe'), '..', 'Updater.exe'); |
||||
shell.openPath(updaterPath); |
||||
} |
||||
}, |
||||
{ |
||||
label: "Restart Aerocord", |
||||
click() { |
||||
app.relaunch(); |
||||
app.quit(); |
||||
} |
||||
}, |
||||
{ |
||||
label: "Close Aerocord", |
||||
click() { |
||||
isQuitting = true; |
||||
app.quit(); |
||||
} |
||||
} |
||||
]); |
||||
|
||||
tray = new Tray(ICON_PATH); |
||||
tray.setToolTip("Aerocord"); |
||||
tray.setContextMenu(trayMenu); |
||||
tray.on("click", onTrayClick); |
||||
} |
||||
|
||||
async function clearData(win: BrowserWindow) { |
||||
const { response } = await dialog.showMessageBox(win, { |
||||
message: "Are you sure you want to reset Aerocord?", |
||||
detail: "This will log you out, clear caches and reset all your settings!\n\nAerocord will automatically restart after this operation.", |
||||
buttons: ["Yes", "No"], |
||||
cancelId: MessageBoxChoice.Cancel, |
||||
defaultId: MessageBoxChoice.Default, |
||||
type: "warning" |
||||
}); |
||||
|
||||
if (response === MessageBoxChoice.Cancel) return; |
||||
|
||||
win.close(); |
||||
|
||||
await win.webContents.session.clearStorageData(); |
||||
await win.webContents.session.clearCache(); |
||||
await win.webContents.session.clearCodeCaches({}); |
||||
await rm(DATA_DIR, { force: true, recursive: true }); |
||||
|
||||
app.relaunch(); |
||||
app.quit(); |
||||
} |
||||
|
||||
type MenuItemList = Array<MenuItemConstructorOptions | false>; |
||||
|
||||
function initMenuBar(win: BrowserWindow) { |
||||
const isWindows = process.platform === "win32"; |
||||
const isDarwin = process.platform === "darwin"; |
||||
const wantCtrlQ = !isWindows || VencordSettings.store.winCtrlQ; |
||||
|
||||
const subMenu = [ |
||||
{ |
||||
label: "About and Documentation", |
||||
click: createAboutWindow |
||||
}, |
||||
{ |
||||
label: "Force Update Vencord", |
||||
async click() { |
||||
await downloadVencordFiles(); |
||||
app.relaunch(); |
||||
app.quit(); |
||||
}, |
||||
toolTip: "Aerocord will automatically restart after this operation" |
||||
}, |
||||
{ |
||||
label: "Reset Aerocord", |
||||
async click() { |
||||
await clearData(win); |
||||
}, |
||||
toolTip: "Aerocord will automatically restart after this operation" |
||||
}, |
||||
{ |
||||
label: "Relaunch", |
||||
accelerator: "CmdOrCtrl+Shift+R", |
||||
click() { |
||||
app.relaunch(); |
||||
app.quit(); |
||||
} |
||||
}, |
||||
...(!isDarwin |
||||
? [] |
||||
: ([ |
||||
{ |
||||
type: "separator" |
||||
}, |
||||
{ |
||||
label: "Settings", |
||||
accelerator: "CmdOrCtrl+,", |
||||
async click() { |
||||
mainWin.webContents.executeJavaScript( |
||||
"Vencord.Webpack.Common.SettingsRouter.open('My Account')" |
||||
); |
||||
} |
||||
}, |
||||
{ |
||||
type: "separator" |
||||
}, |
||||
{ |
||||
role: "hide" |
||||
}, |
||||
{ |
||||
role: "hideOthers" |
||||
}, |
||||
{ |
||||
role: "unhide" |
||||
}, |
||||
{ |
||||
type: "separator" |
||||
} |
||||
] satisfies MenuItemList)), |
||||
{ |
||||
label: "Quit", |
||||
accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0, |
||||
visible: !isWindows, |
||||
role: "quit", |
||||
click() { |
||||
app.quit(); |
||||
} |
||||
}, |
||||
isWindows && { |
||||
label: "Quit", |
||||
accelerator: "Alt+F4", |
||||
role: "quit", |
||||
click() { |
||||
app.quit(); |
||||
} |
||||
}, |
||||
// See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256
|
||||
{ |
||||
label: "Zoom in (hidden, hack for Qwertz and others)", |
||||
accelerator: "CmdOrCtrl+=", |
||||
role: "zoomIn", |
||||
visible: false |
||||
} |
||||
] satisfies MenuItemList; |
||||
|
||||
const menu = Menu.buildFromTemplate([ |
||||
{ |
||||
label: "Vesktop", |
||||
role: "appMenu", |
||||
submenu: subMenu.filter(isTruthy) |
||||
}, |
||||
{ role: "fileMenu" }, |
||||
{ role: "editMenu" }, |
||||
{ role: "viewMenu" }, |
||||
{ role: "windowMenu" } |
||||
]); |
||||
|
||||
Menu.setApplicationMenu(menu); |
||||
} |
||||
|
||||
function getWindowBoundsOptions(): BrowserWindowConstructorOptions { |
||||
// We want the default window behaivour to apply in game mode since it expects everything to be fullscreen and maximized.
|
||||
if (isDeckGameMode) return {}; |
||||
|
||||
const { x, y, width, height } = State.store.windowBounds ?? {}; |
||||
|
||||
const options = { |
||||
width: width ?? DEFAULT_WIDTH, |
||||
height: height ?? DEFAULT_HEIGHT |
||||
} as BrowserWindowConstructorOptions; |
||||
|
||||
if (x != null && y != null) { |
||||
options.x = x; |
||||
options.y = y; |
||||
} |
||||
|
||||
if (!Settings.store.disableMinSize) { |
||||
options.minWidth = MIN_WIDTH; |
||||
options.minHeight = MIN_HEIGHT; |
||||
} |
||||
|
||||
return options; |
||||
} |
||||
|
||||
function getDarwinOptions(): BrowserWindowConstructorOptions { |
||||
const options = { |
||||
titleBarStyle: "hidden", |
||||
trafficLightPosition: { x: 10, y: 10 } |
||||
} as BrowserWindowConstructorOptions; |
||||
|
||||
const { splashTheming, splashBackground } = Settings.store; |
||||
const { macosTranslucency } = VencordSettings.store; |
||||
|
||||
if (macosTranslucency) { |
||||
options.vibrancy = "sidebar"; |
||||
options.backgroundColor = "#ffffff00"; |
||||
} else { |
||||
if (splashTheming) { |
||||
options.backgroundColor = splashBackground; |
||||
} else { |
||||
options.backgroundColor = nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff"; |
||||
} |
||||
} |
||||
|
||||
return options; |
||||
} |
||||
|
||||
function initWindowBoundsListeners(win: BrowserWindow) { |
||||
const saveState = () => { |
||||
State.store.maximized = win.isMaximized(); |
||||
State.store.minimized = win.isMinimized(); |
||||
}; |
||||
|
||||
win.on("maximize", saveState); |
||||
win.on("minimize", saveState); |
||||
win.on("unmaximize", saveState); |
||||
|
||||
const saveBounds = () => { |
||||
State.store.windowBounds = win.getBounds(); |
||||
}; |
||||
|
||||
win.on("resize", saveBounds); |
||||
win.on("move", saveBounds); |
||||
} |
||||
|
||||
function initSettingsListeners(win: BrowserWindow) { |
||||
addSettingsListener("tray", enable => { |
||||
if (enable) initTray(win); |
||||
else tray?.destroy(); |
||||
}); |
||||
addSettingsListener("disableMinSize", disable => { |
||||
if (disable) { |
||||
// 0 no work
|
||||
win.setMinimumSize(1, 1); |
||||
} else { |
||||
win.setMinimumSize(MIN_WIDTH, MIN_HEIGHT); |
||||
|
||||
const { width, height } = win.getBounds(); |
||||
win.setBounds({ |
||||
width: Math.max(width, MIN_WIDTH), |
||||
height: Math.max(height, MIN_HEIGHT) |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
addVencordSettingsListener("macosTranslucency", enabled => { |
||||
if (enabled) { |
||||
win.setVibrancy("sidebar"); |
||||
win.setBackgroundColor("#ffffff00"); |
||||
} else { |
||||
win.setVibrancy(null); |
||||
win.setBackgroundColor("#ffffff"); |
||||
} |
||||
}); |
||||
|
||||
addSettingsListener("enableMenu", enabled => { |
||||
win.setAutoHideMenuBar(enabled ?? false); |
||||
}); |
||||
} |
||||
|
||||
function initSpellCheck(win: BrowserWindow) { |
||||
win.webContents.on("context-menu", (_, data) => { |
||||
win.webContents.send(IpcEvents.SPELLCHECK_RESULT, data.misspelledWord, data.dictionarySuggestions); |
||||
}); |
||||
} |
||||
|
||||
function createMainWindow() { |
||||
// Clear up previous settings listeners
|
||||
removeSettingsListeners(); |
||||
removeVencordSettingsListeners(); |
||||
|
||||
const { staticTitle, transparencyOption, enableMenu, customTitleBar } = Settings.store; |
||||
|
||||
const { frameless, transparent } = VencordSettings.store; |
||||
|
||||
const noFrame = frameless === true || customTitleBar === true; |
||||
|
||||
const win = (mainWin = new BrowserWindow({ |
||||
show: false, |
||||
webPreferences: { |
||||
nodeIntegration: false, |
||||
sandbox: false, |
||||
contextIsolation: true, |
||||
devTools: true, |
||||
preload: join(__dirname, "preload.js"), |
||||
spellcheck: true |
||||
}, |
||||
icon: ICON_PATH, |
||||
frame: !noFrame, |
||||
...(transparent && { |
||||
transparent: true, |
||||
backgroundColor: "#00000000" |
||||
}), |
||||
...(transparencyOption && |
||||
transparencyOption !== "none" && { |
||||
backgroundColor: "#00000000", |
||||
backgroundMaterial: transparencyOption |
||||
}), |
||||
// Fix transparencyOption for custom discord titlebar
|
||||
...(customTitleBar && |
||||
transparencyOption && |
||||
transparencyOption !== "none" && { |
||||
transparent: true |
||||
}), |
||||
...(staticTitle && { title: "Vesktop" }), |
||||
...(process.platform === "darwin" && getDarwinOptions()), |
||||
...getWindowBoundsOptions(), |
||||
autoHideMenuBar: enableMenu |
||||
})); |
||||
win.setMenuBarVisibility(false); |
||||
|
||||
win.on("close", e => { |
||||
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false; |
||||
if (isQuitting || (process.platform !== "darwin" && !useTray)) return; |
||||
|
||||
e.preventDefault(); |
||||
|
||||
if (process.platform === "darwin") app.hide(); |
||||
else win.hide(); |
||||
|
||||
return false; |
||||
}); |
||||
|
||||
if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault()); |
||||
|
||||
initWindowBoundsListeners(win); |
||||
if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win); |
||||
initMenuBar(win); |
||||
makeLinksOpenExternally(win); |
||||
initSettingsListeners(win); |
||||
initSpellCheck(win); |
||||
|
||||
win.webContents.setUserAgent(BrowserUserAgent); |
||||
|
||||
const subdomain = |
||||
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb" |
||||
? `${Settings.store.discordBranch}.` |
||||
: ""; |
||||
|
||||
win.loadURL(`https://${subdomain}discord.com/app`); |
||||
|
||||
return win; |
||||
} |
||||
|
||||
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js"))); |
||||
|
||||
export async function createWindows() { |
||||
const startMinimized = process.argv.includes("--start-minimized"); |
||||
const splash = createSplashWindow(startMinimized); |
||||
// SteamOS letterboxes and scales it terribly, so just full screen it
|
||||
if (isDeckGameMode) splash.setFullScreen(true); |
||||
await ensureVencordFiles(); |
||||
runVencordMain(); |
||||
|
||||
mainWin = createMainWindow(); |
||||
|
||||
mainWin.webContents.on("did-finish-load", () => { |
||||
splash.destroy(); |
||||
|
||||
if (!startMinimized) { |
||||
mainWin!.show(); |
||||
if (State.store.maximized && !isDeckGameMode) mainWin!.maximize(); |
||||
} |
||||
|
||||
if (isDeckGameMode) { |
||||
// always use entire display
|
||||
mainWin!.setFullScreen(true); |
||||
|
||||
askToApplySteamLayout(mainWin); |
||||
} |
||||
|
||||
mainWin.once("show", () => { |
||||
if (State.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) { |
||||
mainWin!.maximize(); |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
initArRPC(); |
||||
} |
@ -0,0 +1,24 @@ |
||||
/* |
||||
* SPDX-License-Identifier: GPL-3.0 |
||||
* Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
|
||||
* Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop! |
||||
*/ |
||||
|
||||
import { session, systemPreferences } from "electron"; |
||||
|
||||
export function registerMediaPermissionsHandler() { |
||||
if (process.platform !== "darwin") return; |
||||
|
||||
session.defaultSession.setPermissionRequestHandler(async (_webContents, permission, callback, details) => { |
||||
let granted = true; |
||||
|
||||
if (details.mediaTypes?.includes("audio")) { |
||||
granted = await systemPreferences.askForMediaAccess("microphone"); |
||||
} |
||||
if (details.mediaTypes?.includes("video")) { |
||||
granted &&= await systemPreferences.askForMediaAccess("camera"); |
||||
} |
||||
|
||||
callback(granted); |
||||
}); |
||||
} |
Loading…
Reference in new issue