@@ -35,12 +35,12 @@ internal object TrayClickTracker {
3535 val screenSize = Toolkit .getDefaultToolkit().screenSize
3636 val position = convertPositionToCorner(x, y, screenSize.width, screenSize.height)
3737 lastClickPosition.set(TrayClickPosition (x, y, position))
38- saveTrayClickPosition(x, y, position)
38+ runCatching { saveTrayClickPosition(x, y, position) }
3939 }
4040
4141 fun setClickPosition (x : Int , y : Int , position : TrayPosition ) {
4242 lastClickPosition.set(TrayClickPosition (x, y, position))
43- saveTrayClickPosition(x, y, position)
43+ runCatching { saveTrayClickPosition(x, y, position) }
4444 }
4545
4646 fun getLastClickPosition (): TrayClickPosition ? = lastClickPosition.get()
@@ -62,11 +62,27 @@ private const val Y_KEY = "TrayY"
6262
6363// Use a writable tmp/cache directory to avoid read-only filesystem issues (e.g., macOS app bundle working dir)
6464private fun trayPropertiesFile (): File {
65+ val appId = AppIdProvider .appId()
6566 val tmpBase = System .getProperty(" java.io.tmpdir" ) ? : " ."
66- val appDir = File (File (tmpBase, " ComposeNativeTray" ), AppIdProvider .appId())
67- // Best-effort create directory; if it fails, we will fallback to legacy file access patterns safely
68- runCatching { if (! appDir.exists()) appDir.mkdirs() }.onFailure { /* ignore */ }
69- return File (appDir, PROPERTIES_FILE )
67+ val tmpDir = File (File (tmpBase, " ComposeNativeTray" ), appId)
68+
69+ val userHome = System .getProperty(" user.home" )
70+ val macCacheDir = if (userHome != null ) {
71+ val base = File (File (File (userHome, " Library" ), " Caches" ), " ComposeNativeTray" )
72+ File (base, appId)
73+ } else null
74+
75+ val candidates = listOfNotNull(tmpDir, macCacheDir)
76+
77+ for (dir in candidates) {
78+ runCatching { if (! dir.exists()) dir.mkdirs() }
79+ if (dir.exists() && dir.canWrite()) {
80+ return File (dir, PROPERTIES_FILE )
81+ }
82+ }
83+
84+ // Fallback to tmp even if not verified writable; write will be guarded by runCatching
85+ return File (tmpDir, PROPERTIES_FILE )
7086}
7187
7288// Legacy properties file in working directory (for backward compatibility)
@@ -79,6 +95,15 @@ private fun oldTmpPropertiesFile(): File {
7995 return File (oldDir, PROPERTIES_FILE )
8096}
8197
98+ // macOS user cache directory location for properties (for read/write fallback)
99+ private fun macCachePropertiesFile (): File ? {
100+ val userHome = System .getProperty(" user.home" ) ? : return null
101+ val appId = AppIdProvider .appId()
102+ val base = File (File (File (userHome, " Library" ), " Caches" ), " ComposeNativeTray" )
103+ val dir = File (base, appId)
104+ return File (dir, PROPERTIES_FILE )
105+ }
106+
82107private fun loadPropertiesFrom (file : File ): Properties ? {
83108 if (! file.exists()) return null
84109 return runCatching {
@@ -96,6 +121,7 @@ private fun storePropertiesTo(file: File, props: Properties) {
96121internal fun saveTrayPosition (position : TrayPosition ) {
97122 val preferredFile = trayPropertiesFile()
98123 val properties = loadPropertiesFrom(preferredFile)
124+ ? : macCachePropertiesFile()?.let { loadPropertiesFrom(it) }
99125 ? : loadPropertiesFrom(oldTmpPropertiesFile())
100126 ? : loadPropertiesFrom(legacyPropertiesFile())
101127 ? : Properties ()
@@ -106,6 +132,7 @@ internal fun saveTrayPosition(position: TrayPosition) {
106132internal fun saveTrayClickPosition (x : Int , y : Int , position : TrayPosition ) {
107133 val preferredFile = trayPropertiesFile()
108134 val properties = loadPropertiesFrom(preferredFile)
135+ ? : macCachePropertiesFile()?.let { loadPropertiesFrom(it) }
109136 ? : loadPropertiesFrom(oldTmpPropertiesFile())
110137 ? : loadPropertiesFrom(legacyPropertiesFile())
111138 ? : Properties ()
@@ -116,8 +143,9 @@ internal fun saveTrayClickPosition(x: Int, y: Int, position: TrayPosition) {
116143}
117144
118145internal fun loadTrayClickPosition (): TrayClickPosition ? {
119- // Prefer new location, fallback to old tmp location, then legacy working dir
146+ // Prefer new location, fallback to mac cache, then old tmp location, then legacy working dir
120147 val props = loadPropertiesFrom(trayPropertiesFile())
148+ ? : macCachePropertiesFile()?.let { loadPropertiesFrom(it) }
121149 ? : loadPropertiesFrom(oldTmpPropertiesFile())
122150 ? : loadPropertiesFrom(legacyPropertiesFile()) ? : return null
123151
@@ -184,6 +212,7 @@ fun getTrayPosition(): TrayPosition {
184212 // Legacy fallback - just position without coordinates
185213 run {
186214 val props = loadPropertiesFrom(trayPropertiesFile())
215+ ? : macCachePropertiesFile()?.let { loadPropertiesFrom(it) }
187216 ? : loadPropertiesFrom(oldTmpPropertiesFile())
188217 ? : loadPropertiesFrom(legacyPropertiesFile())
189218 val position = props?.getProperty(POSITION_KEY , null )
0 commit comments