diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2018-03-07 14:03:41 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-03-07 14:03:41 +0100 |
| commit | 5b0118e589b5b1fb60fd9f3ba8c132bd483db975 (patch) | |
| tree | d551d049771fa97b39a167fc5237b7f72c1451b9 /app | |
| parent | eca87471d2653fe732b3fd021f180e6ca6b6589a (diff) | |
| parent | 802b1ccec0cb280d75c6ff11bc0dae1115c5831d (diff) | |
| download | mullvadvpn-5b0118e589b5b1fb60fd9f3ba8c132bd483db975.tar.xz mullvadvpn-5b0118e589b5b1fb60fd9f3ba8c132bd483db975.zip | |
Merge branch 'win32-tray'
Diffstat (limited to 'app')
| -rw-r--r-- | app/lib/rpc-file-security.js | 2 | ||||
| -rw-r--r-- | app/main.js | 180 |
2 files changed, 138 insertions, 44 deletions
diff --git a/app/lib/rpc-file-security.js b/app/lib/rpc-file-security.js index 22dedb00be..eb1901f67f 100644 --- a/app/lib/rpc-file-security.js +++ b/app/lib/rpc-file-security.js @@ -27,7 +27,7 @@ function isOwnedByLocalSystem(path: string): boolean { // $FlowFixMe: this module is only available on Windows const winsec = require('windows-security'); const ownerSid = winsec.getFileOwnerSid(path, null); - const isWellKnownSid = winsec.isWellKnownSid(ownerSid, winsec.WellKnownSid.LocalSystemSid); + const isWellKnownSid = winsec.isWellKnownSid(ownerSid, winsec.WellKnownSid.BuiltinAdministratorsSid); return isWellKnownSid; }
\ No newline at end of file diff --git a/app/main.js b/app/main.js index f097521c87..85003385ee 100644 --- a/app/main.js +++ b/app/main.js @@ -3,7 +3,7 @@ import path from 'path'; import fs from 'fs'; import mkdirp from 'mkdirp'; import { log } from './lib/platform'; -import { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage } from 'electron'; +import electron, { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage } from 'electron'; import TrayIconManager from './lib/tray-icon-manager'; import ElectronSudo from 'electron-sudo'; import shellescape from 'shell-escape'; @@ -18,7 +18,6 @@ import uuid from 'uuid'; import type { TrayIconType } from './lib/tray-icon-manager'; const isDevelopment = (process.env.NODE_ENV === 'development'); -const isMacOS = (process.platform === 'darwin'); // The name for application directory used for // scoping logs and user data in platform special folders @@ -73,13 +72,15 @@ const appDelegate = { // 1. https://github.com/electron/electron/issues/10118 // 2. https://github.com/electron/electron/pull/10191 _getLogsDirectory: () => { - // macOS: ~/Library/Logs/{appname} - if(isMacOS) { + switch(process.platform) { + case 'darwin': + // macOS: ~/Library/Logs/{appname} return path.join(app.getPath('home'), 'Library/Logs', appDirectoryName); + default: + // Linux: ~/.config/{appname}/logs + // Windows: ~\AppData\Roaming\{appname}\logs + return path.join(app.getPath('userData'), 'logs'); } - // Linux: ~/.config/{appname}/logs - // Windows: ~\AppData\Roaming\{appname}\logs - return path.join(app.getPath('userData'), 'logs'); }, onReady: async () => { @@ -90,9 +91,8 @@ const appDelegate = { appDelegate._pollForConnectionInfoFile(); }); - ipcMain.on('show-window', () => { - appDelegate._showWindow(window, appDelegate._tray); - }); + ipcMain.on('show-window', () => appDelegate._showWindow(window, appDelegate._tray)); + ipcMain.on('hide-window', () => window.hide()); window.loadURL('file://' + path.join(__dirname, 'index.html')); window.on('close', () => { @@ -138,13 +138,8 @@ const appDelegate = { }); }); - // create tray icon on macOS - if(isMacOS) { - appDelegate._tray = appDelegate._createTray(window); - } else { - appDelegate._showWindow(window, null); - } - + // create tray icon + appDelegate._tray = appDelegate._createTray(window); appDelegate._setAppMenu(); appDelegate._addContextMenu(window); @@ -301,17 +296,32 @@ const appDelegate = { } }; - // setup window flags to mimic popover on macOS - if(isMacOS) { - const win = new BrowserWindow({ + switch(process.platform) { + case 'darwin': { + // setup window flags to mimic popover on macOS + const appWindow = new BrowserWindow({ ...options, - height: contentHeight + 12, // 12 is the size of transparent area around arrow + // 12 is the size of transparent area around arrow + height: contentHeight + 12, frame: false, transparent: true }); - win.setVisibleOnAllWorkspaces(true); - return win; - } else { + + // make the window visible on all workspaces + appWindow.setVisibleOnAllWorkspaces(true); + + return appWindow; + } + + case 'win32': + // setup window flags to mimic an overlay window + return new BrowserWindow({ + ...options, + frame: false, + transparent: true + }); + + default: return new BrowserWindow(options); } }, @@ -384,54 +394,138 @@ const appDelegate = { } }, + _updateWindowPosition: (window: BrowserWindow, tray: Tray) => { + const { x, y } = appDelegate._getWindowPosition(window, tray); + window.setPosition(x, y, false); + }, + _showWindow: (window: BrowserWindow, tray: ?Tray) => { - // position window based on tray icon location if(tray) { - const { x, y } = appDelegate._getWindowPosition(window, tray); - window.setPosition(x, y, false); + appDelegate._updateWindowPosition(window, tray); } window.show(); window.focus(); }, + _getTrayPlacement: () => { + switch(process.platform) { + case 'darwin': + // macOS has menubar always placed at the top + return 'top'; + + case 'win32': { + // taskbar occupies some part of the screen excluded from work area + const primaryDisplay = electron.screen.getPrimaryDisplay(); + const displaySize = primaryDisplay.size; + const workArea = primaryDisplay.workArea; + + if(workArea.width < displaySize.width) { + return workArea.x > 0 ? 'left' : 'right'; + } else if(workArea.height < displaySize.height) { + return workArea.y > 0 ? 'top' : 'bottom'; + } else { + return 'none'; + } + } + + default: + return 'none'; + } + }, + _getWindowPosition: (window: BrowserWindow, tray: Tray): { x: number, y: number } => { const windowBounds = window.getBounds(); const trayBounds = tray.getBounds(); - // center window horizontally below the tray icon - const x = Math.round(trayBounds.x + (trayBounds.width / 2) - (windowBounds.width / 2)); + const primaryDisplay = electron.screen.getPrimaryDisplay(); + const workArea = primaryDisplay.workArea; + const placement = appDelegate._getTrayPlacement(); + const maxX = workArea.x + workArea.width - windowBounds.width; + const maxY = workArea.y + workArea.height - windowBounds.height; + + let x = 0, y = 0; + switch(placement) { + case 'top': + x = trayBounds.x + (trayBounds.width - windowBounds.width) * 0.5; + y = trayBounds.y + trayBounds.height; + break; - // position window vertically below the tray icon - const y = Math.round(trayBounds.y + trayBounds.height); + case 'bottom': + x = trayBounds.x + (trayBounds.width - windowBounds.width) * 0.5; + y = trayBounds.y - windowBounds.height; + break; - return { x, y }; + case 'left': + x = trayBounds.x + trayBounds.width; + y = trayBounds.y + (trayBounds.height - windowBounds.height) * 0.5; + break; + + case 'right': + x = trayBounds.x - windowBounds.width; + y = trayBounds.y + (trayBounds.height - windowBounds.height) * 0.5; + break; + + case 'none': + x = workArea.x + (workArea.width - windowBounds.width) * 0.5; + y = workArea.y + (workArea.height - windowBounds.height) * 0.5; + break; + } + + x = Math.min(Math.max(x, workArea.x), maxX); + y = Math.min(Math.max(y, workArea.y), maxY); + + return { + x: Math.round(x), + y: Math.round(y) + }; }, _createTray: (window: BrowserWindow): Tray => { const tray = new Tray(nativeImage.createEmpty()); - tray.setHighlightMode('never'); + + // configure tray icon + tray.setToolTip('Mullvad VPN'); tray.on('click', () => appDelegate._toggleWindow(window, tray)); - // setup NSEvent monitor to fix inconsistent window.blur - // see https://github.com/electron/electron/issues/8689 - // $FlowFixMe: this module is only available on macOS - const { NSEventMonitor, NSEventMask } = require('nseventmonitor'); - const trayIconManager = new TrayIconManager(tray, 'unsecured'); - const macEventMonitor = new NSEventMonitor(); - const eventMask = NSEventMask.leftMouseDown | NSEventMask.rightMouseDown; + // add display metrics change handler + electron.screen.addListener('display-metrics-changed', (_event, _display, changedMetrics) => { + if(changedMetrics.includes('workArea') && window.isVisible()) { + appDelegate._updateWindowPosition(window, tray); + } + }); // add IPC handler to change tray icon from renderer + const trayIconManager = new TrayIconManager(tray, 'unsecured'); ipcMain.on('changeTrayIcon', (_: Event, type: TrayIconType) => trayIconManager.iconType = type); // setup event handlers - window.on('show', () => macEventMonitor.start(eventMask, () => window.hide())); - window.on('hide', () => macEventMonitor.stop()); window.on('close', () => window.closeDevTools()); window.on('blur', () => !window.isDevToolsOpened() && window.hide()); + if(process.platform === 'darwin') { + // disable icon highlight on macOS + tray.setHighlightMode('never'); + + // apply macOS patch for windows.blur + appDelegate._macOSFixInconsistentWindowBlur(window); + } + return tray; - } + }, + + // setup NSEvent monitor to fix inconsistent window.blur on macOS + // see https://github.com/electron/electron/issues/8689 + _macOSFixInconsistentWindowBlur: (window: BrowserWindow) => { + // $FlowFixMe: this module is only available on macOS + const { NSEventMonitor, NSEventMask } = require('nseventmonitor'); + const macEventMonitor = new NSEventMonitor(); + const eventMask = NSEventMask.leftMouseDown | NSEventMask.rightMouseDown; + + window.on('show', () => macEventMonitor.start(eventMask, () => window.hide())); + window.on('hide', () => macEventMonitor.stop()); + }, + }; appDelegate.setup(); |
