diff --git a/Dotnet/AppApi/Cef/AppApiCef.cs b/Dotnet/AppApi/Cef/AppApiCef.cs index a40f6a4a..27efe3b6 100644 --- a/Dotnet/AppApi/Cef/AppApiCef.cs +++ b/Dotnet/AppApi/Cef/AppApiCef.cs @@ -216,5 +216,10 @@ namespace VRCX using var client = MainForm.Instance.Browser.GetDevToolsClient(); _ = client.Network.SetUserAgentOverrideAsync(Program.Version); } + + public override void SetTrayIconNotification(bool notify) + { + MainForm.Instance.BeginInvoke(new MethodInvoker(() => { MainForm.Instance.SetTrayIconNotification(notify); })); + } } } \ No newline at end of file diff --git a/Dotnet/AppApi/Common/AppApiCommonBase.cs b/Dotnet/AppApi/Common/AppApiCommonBase.cs index 94583e60..08da47eb 100644 --- a/Dotnet/AppApi/Common/AppApiCommonBase.cs +++ b/Dotnet/AppApi/Common/AppApiCommonBase.cs @@ -14,6 +14,7 @@ namespace VRCX public abstract void SetZoom(double zoomLevel); public abstract Task GetZoom(); public abstract void DesktopNotification(string BoldText, string Text = "", string Image = ""); + public abstract void SetTrayIconNotification(bool notify); public abstract void RestartApplication(bool isUpgrade); public abstract bool CheckForUpdateExe(); diff --git a/Dotnet/AppApi/Electron/AppApiElectron.cs b/Dotnet/AppApi/Electron/AppApiElectron.cs index 2e37703e..a491c1be 100644 --- a/Dotnet/AppApi/Electron/AppApiElectron.cs +++ b/Dotnet/AppApi/Electron/AppApiElectron.cs @@ -143,5 +143,9 @@ namespace VRCX public override void SetUserAgent() { } + + public override void SetTrayIconNotification(bool notify) + { + } } } \ No newline at end of file diff --git a/Dotnet/Cef/MainForm.cs b/Dotnet/Cef/MainForm.cs index 4265f4d5..1d2922b6 100644 --- a/Dotnet/Cef/MainForm.cs +++ b/Dotnet/Cef/MainForm.cs @@ -7,7 +7,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Drawing; -using System.Reflection; +using System.IO; using System.Windows.Forms; using CefSharp; using CefSharp.WinForms; @@ -22,6 +22,8 @@ namespace VRCX public static NativeWindow nativeWindow; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); public ChromiumWebBrowser Browser; + private readonly Icon _appIcon; + private readonly Icon _appIconNoty; private readonly Timer _saveTimer; private int LastLocationX; private int LastLocationY; @@ -41,10 +43,11 @@ namespace VRCX _saveTimer.Tick += SaveTimer_Tick; try { - var location = Assembly.GetExecutingAssembly().Location; - var icon = Icon.ExtractAssociatedIcon(location); - Icon = icon; - TrayIcon.Icon = icon; + var path = Path.GetDirectoryName(Environment.ProcessPath) ?? string.Empty; + _appIcon = new Icon(Path.Combine(path, "VRCX.ico")); + _appIconNoty = new Icon(Path.Combine(path, "VRCX_notify.ico")); + Icon = _appIcon; + TrayIcon.Icon = _appIcon; } catch (Exception ex) { @@ -246,5 +249,10 @@ namespace VRCX SaveWindowState(); Application.Exit(); } + + public void SetTrayIconNotification(bool notify) + { + TrayIcon.Icon = notify ? _appIconNoty : _appIcon; + } } } \ No newline at end of file diff --git a/Dotnet/VRCX-Cef.csproj b/Dotnet/VRCX-Cef.csproj index 88b6bc58..0b3d28d6 100644 --- a/Dotnet/VRCX-Cef.csproj +++ b/Dotnet/VRCX-Cef.csproj @@ -85,6 +85,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -100,7 +103,7 @@ - + diff --git a/Dotnet/VRCX-Electron-arm64.csproj b/Dotnet/VRCX-Electron-arm64.csproj index 81967c0a..024eae22 100644 --- a/Dotnet/VRCX-Electron-arm64.csproj +++ b/Dotnet/VRCX-Electron-arm64.csproj @@ -64,6 +64,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -74,7 +77,7 @@ - + diff --git a/Dotnet/VRCX-Electron.csproj b/Dotnet/VRCX-Electron.csproj index 31e8b018..f51ab72f 100644 --- a/Dotnet/VRCX-Electron.csproj +++ b/Dotnet/VRCX-Electron.csproj @@ -69,6 +69,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -79,7 +82,7 @@ - + diff --git a/images/VRCX.ico b/images/VRCX.ico index b33e28cf..f3ae842a 100644 Binary files a/images/VRCX.ico and b/images/VRCX.ico differ diff --git a/images/VRCX_notify.ico b/images/VRCX_notify.ico new file mode 100644 index 00000000..e257eea0 Binary files /dev/null and b/images/VRCX_notify.ico differ diff --git a/images/VRCX_notify.png b/images/VRCX_notify.png new file mode 100644 index 00000000..b2e8a19e Binary files /dev/null and b/images/VRCX_notify.png differ diff --git a/package.json b/package.json index c48e5d8c..8d4e47a1 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,8 @@ "src-electron/*", "images/VRCX.png", "images/VRCX.ico", + "images/VRCX_notify.png", + "images/VRCX_notify.ico", "Version", "src-electron/libs/linux/libopenvr_api.so" ], diff --git a/src-electron/main.js b/src-electron/main.js index bdce477b..bf741032 100644 --- a/src-electron/main.js +++ b/src-electron/main.js @@ -161,28 +161,6 @@ if (!gotTheLock) { }); } -ipcMain.handle('getArch', () => { - return process.arch.toString(); -}); - -ipcMain.handle('applyWindowSettings', (event, position, size, state) => { - if (position) { - mainWindow.setPosition(parseInt(position.x), parseInt(position.y)); - } - if (size) { - mainWindow.setSize(parseInt(size.width), parseInt(size.height)); - } - if (state) { - if (state === '0') { - mainWindow.restore(); - } else if (state === '1') { - mainWindow.restore(); - } else if (state === '2') { - mainWindow.maximize(); - } - } -}); - ipcMain.handle('dialog:openFile', async () => { const result = await dialog.showOpenDialog(mainWindow, { properties: ['openFile'], @@ -278,6 +256,14 @@ ipcMain.handle( } ); +ipcMain.handle('app:getArch', () => { + return process.arch.toString(); +}); + +ipcMain.handle('app:setTrayIconNotification', (event, notify) => { + setTrayIconNotification(notify); +}); + function tryRelaunchWithArgs(args) { if ( process.platform !== 'linux' || @@ -536,21 +522,35 @@ function destroyHmdOverlayWindow() { hmdOverlayWindow = undefined; } +let tray = null; +let trayIcon = null; +let trayIconNotify = null; function createTray() { - let tray = null; if (process.platform === 'darwin') { const image = nativeImage.createFromPath( path.join(rootDir, 'images/VRCX.png') ); - tray = new Tray(image.resize({ width: 16, height: 16 })); + trayIcon = image.resize({ width: 16, height: 16 }); + + const imageNotify = nativeImage.createFromPath( + path.join(rootDir, 'images/VRCX_notify.png') + ); + trayIconNotify = imageNotify.resize({ width: 16, height: 16 }); } else if (process.platform === 'linux') { const image = nativeImage.createFromPath( path.join(rootDir, 'images/VRCX.png') ); - tray = new Tray(image.resize({ width: 64, height: 64 })); + trayIcon = image.resize({ width: 64, height: 64 }); + + const imageNotify = nativeImage.createFromPath( + path.join(rootDir, 'images/VRCX_notify.png') + ); + trayIconNotify = imageNotify.resize({ width: 64, height: 64 }); } else { - tray = new Tray(path.join(rootDir, 'images/VRCX.ico')); + trayIcon = path.join(rootDir, 'images/VRCX.ico'); + trayIconNotify = path.join(rootDir, 'images/VRCX_notify.ico'); } + tray = new Tray(trayIcon); const contextMenu = Menu.buildFromTemplate([ { label: 'Open', @@ -583,6 +583,10 @@ function createTray() { }); } +function setTrayIconNotification(notify) { + tray.setImage(notify ? trayIconNotify : trayIcon); +} + async function installVRCX() { console.log('Home path:', homePath); console.log('AppImage path:', appImagePath); diff --git a/src-electron/preload.js b/src-electron/preload.js index 6ac5e35f..f26fbc2e 100644 --- a/src-electron/preload.js +++ b/src-electron/preload.js @@ -22,7 +22,9 @@ contextBridge.exposeInMainWorld('interopApi', { const validChannels = ['launch-command']; contextBridge.exposeInMainWorld('electron', { - getArch: () => ipcRenderer.invoke('getArch'), + getArch: () => ipcRenderer.invoke('app:getArch'), + setTrayIconNotification: (notify) => + ipcRenderer.invoke('app:setTrayIconNotification', notify), openFileDialog: () => ipcRenderer.invoke('dialog:openFile'), openDirectoryDialog: () => ipcRenderer.invoke('dialog:openDirectory'), onWindowPositionChanged: (callback) => diff --git a/src/stores/ui.js b/src/stores/ui.js index c9bf7c25..2675464e 100644 --- a/src/stores/ui.js +++ b/src/stores/ui.js @@ -23,6 +23,7 @@ export const useUiStore = defineStore('Ui', () => { const notifiedMenus = ref([]); const shiftHeld = ref(false); + const trayIconNotify = ref(false); watch( () => watchState.isLoggedIn, @@ -41,6 +42,7 @@ export const useUiStore = defineStore('Ui', () => { !notifiedMenus.value.includes(index) ) { notifiedMenus.value.push(index); + updateTrayIconNotify(); } } @@ -58,6 +60,22 @@ export const useUiStore = defineStore('Ui', () => { function removeNotify(index) { notifiedMenus.value = notifiedMenus.value.filter((i) => i !== index); + updateTrayIconNotify(); + } + + function updateTrayIconNotify() { + const newState = + notifiedMenus.value.includes('notification') || + notifiedMenus.value.includes('friendLog'); + + if (trayIconNotify.value !== newState) { + trayIconNotify.value = newState; + if (LINUX) { + window.electron.setTrayIconNotification(trayIconNotify.value); + return; + } + AppApi.SetTrayIconNotification(trayIconNotify.value); + } } return { diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts index 370c3202..34c9dbd8 100644 --- a/src/types/globals.d.ts +++ b/src/types/globals.d.ts @@ -36,6 +36,7 @@ declare global { }; electron: { getArch: () => Promise; + setTrayIconNotification: (notify: boolean) => Promise; openFileDialog: () => Promise; openDirectoryDialog: () => Promise; desktopNotification: ( @@ -187,6 +188,7 @@ declare global { CopyImageToClipboard(path: string): Promise; FlashWindow(): Promise; SetUserAgent(): Promise; + SetTrayIconNotification(notify: boolean): Promise; // Common Functions GetColourFromUserID(userId: string): Promise;