99 ipcMain,
1010 nativeImage,
1111 nativeTheme,
12- screen,
1312} = require ( "electron" ) ;
13+ // Optional: set HSP_DISABLE_GPU=1 to test whether GPU stack affects shortcuts (e.g. Print Screen) on Windows.
14+ if ( process . platform === "win32" && process . env . HSP_DISABLE_GPU === "1" ) {
15+ try {
16+ app . disableHardwareAcceleration ( ) ;
17+ } catch ( _ ) { }
18+ }
1419const path = require ( "path" ) ;
1520const fs = require ( "fs" ) ;
1621const crypto = require ( "crypto" ) ;
@@ -581,15 +586,58 @@ function resolveAppIconIcoPath() {
581586 return null ;
582587}
583588
589+ /** All existing .ico paths (packaged app may have the file only inside app.asar — see nativeImage note below). */
590+ function collectAppIconIcoCandidates ( ) {
591+ const candidates = [
592+ process . resourcesPath && path . join ( process . resourcesPath , "app.asar.unpacked" , "assets" , "icon.ico" ) ,
593+ process . resourcesPath && path . join ( process . resourcesPath , "assets" , "icon.ico" ) ,
594+ app . getAppPath && path . join ( app . getAppPath ( ) , "assets" , "icon.ico" ) ,
595+ path . join ( __dirname , ".." , "assets" , "icon.ico" ) ,
596+ ] . filter ( Boolean ) ;
597+ const out = [ ] ;
598+ const seen = new Set ( ) ;
599+ for ( const p of candidates ) {
600+ try {
601+ if ( ! p || ! fs . existsSync ( p ) ) continue ;
602+ const n = path . normalize ( p ) ;
603+ if ( seen . has ( n ) ) continue ;
604+ seen . add ( n ) ;
605+ out . push ( n ) ;
606+ } catch ( _ ) { }
607+ }
608+ return out ;
609+ }
610+
611+ /**
612+ * Paths under app.asar\... are real for Node (readFileSync) but not for nativeImage.createFromPath /
613+ * app.getFileIcon (shell/GDI cannot read inside the asar archive). Always prefer readFileSync + createFromBuffer for .ico.
614+ */
615+ function nativeImageFromIcoFilePath ( p ) {
616+ if ( ! p ) return null ;
617+ try {
618+ if ( ! fs . existsSync ( p ) ) return null ;
619+ const buf = fs . readFileSync ( p ) ;
620+ const img = nativeImage . createFromBuffer ( buf ) ;
621+ return img . isEmpty ( ) ? null : img ;
622+ } catch ( _ ) {
623+ return null ;
624+ }
625+ }
626+
584627function nativeImageFromAppIcon ( ) {
585- const p = resolveAppIconIcoPath ( ) ;
586- if ( p ) {
628+ const paths = collectAppIconIcoCandidates ( ) ;
629+ for ( const p of paths ) {
630+ const img = nativeImageFromIcoFilePath ( p ) ;
631+ if ( img ) return img ;
632+ }
633+ for ( const p of paths ) {
587634 try {
635+ const inAsarArchive = p . includes ( "app.asar" ) && ! p . includes ( "app.asar.unpacked" ) ;
636+ if ( inAsarArchive ) continue ;
588637 const img = nativeImage . createFromPath ( p ) ;
589638 if ( ! img . isEmpty ( ) ) return img ;
590639 } catch ( _ ) { }
591640 }
592- // Packaged app: .ico can fail to load from ASAR paths; Windows still reads icons embedded in the exe (packager --icon).
593641 if ( process . platform === "win32" && app . isPackaged ) {
594642 try {
595643 const img = nativeImage . createFromPath ( process . execPath ) ;
@@ -1788,13 +1836,16 @@ function log(msg) {
17881836
17891837/** Windows: resolve before the first show() or the taskbar often keeps a blank/generic icon. */
17901838async function resolveBrowserWindowIcon ( ) {
1791- const fallback = nativeImageFromAppIcon ( ) ;
1792- if ( process . platform !== "win32" || ! app . isPackaged ) return fallback ;
1793- const iconPaths = [ resolveAppIconIcoPath ( ) , process . execPath ] . filter (
1794- ( p ) => p && ( p === process . execPath || fs . existsSync ( p ) ) ,
1795- ) ;
1796- for ( const p of iconPaths ) {
1797- for ( const size of [ "large" , "normal" ] ) {
1839+ const fromFile = nativeImageFromAppIcon ( ) ;
1840+ if ( fromFile && ! fromFile . isEmpty ( ) ) return fromFile ;
1841+ if ( process . platform !== "win32" || ! app . isPackaged ) return fromFile ;
1842+ const shellPaths = [ process . execPath , ...collectAppIconIcoCandidates ( ) ] . filter ( ( p ) => {
1843+ if ( ! p || ! fs . existsSync ( p ) ) return false ;
1844+ const inAsarArchive = p . includes ( "app.asar" ) && ! p . includes ( "app.asar.unpacked" ) ;
1845+ return ! inAsarArchive ;
1846+ } ) ;
1847+ for ( const p of shellPaths ) {
1848+ for ( const size of [ "large" , "normal" , "small" ] ) {
17981849 try {
17991850 const img = await app . getFileIcon ( p , { size } ) ;
18001851 if ( ! img . isEmpty ( ) ) return img ;
@@ -1805,7 +1856,7 @@ async function resolveBrowserWindowIcon() {
18051856 }
18061857 }
18071858 }
1808- return fallback ;
1859+ return fromFile ;
18091860}
18101861
18111862async function createWindow ( ) {
@@ -1822,7 +1873,9 @@ async function createWindow() {
18221873 const windowIcon = await resolveBrowserWindowIcon ( ) ;
18231874 if ( process . platform === "win32" && app . isPackaged && ( ! windowIcon || windowIcon . isEmpty ( ) ) ) {
18241875 try {
1825- log ( "warn: taskbar icon: resolveBrowserWindowIcon returned empty" ) ;
1876+ log (
1877+ `warn: taskbar icon empty; candidates=${ collectAppIconIcoCandidates ( ) . join ( " | " ) } exe=${ process . execPath } ` ,
1878+ ) ;
18261879 } catch ( _ ) { }
18271880 }
18281881
@@ -1841,25 +1894,19 @@ async function createWindow() {
18411894 webPreferences : {
18421895 nodeIntegration : false ,
18431896 contextIsolation : true ,
1897+ spellcheck : false ,
18441898 } ,
18451899 show : false ,
18461900 } ) ;
18471901
1902+ try {
1903+ mainWindow . webContents . setIgnoreMenuShortcuts ( false ) ;
1904+ } catch ( _ ) { }
1905+
18481906 mainWindow . once ( "ready-to-show" , ( ) => {
1849- // Edge-to-edge without WS_MAXIMIZE: on Windows, maximize() can interact badly with shell icons
1850- // and some system shortcuts (e.g. Print Screen); filling the work area matches prior "fullscreen" intent.
18511907 try {
1852- if ( process . platform === "win32" ) {
1853- const wa = screen . getPrimaryDisplay ( ) . workArea ;
1854- mainWindow . setBounds ( wa ) ;
1855- } else {
1856- mainWindow . maximize ( ) ;
1857- }
1858- } catch ( _ ) {
1859- try {
1860- mainWindow . maximize ( ) ;
1861- } catch ( _ ) { }
1862- }
1908+ mainWindow . maximize ( ) ;
1909+ } catch ( _ ) { }
18631910 mainWindow . show ( ) ;
18641911 } ) ;
18651912
0 commit comments