-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
347 lines (296 loc) · 8.97 KB
/
Copy pathmain.go
File metadata and controls
347 lines (296 loc) · 8.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/getlantern/systray"
)
// Global variables
var (
isSleepPrevented bool
timerDuration time.Duration
timerEndTime time.Time
timerActive bool
autoStartEnabled bool
)
func main() {
// Start the systray
systray.Run(onReady, onExit)
}
func onReady() {
// Set up the menu bar icon
systray.SetIcon(getIcon("default"))
systray.SetTitle("")
systray.SetTooltip("Jolt - Prevent your Mac from sleeping")
// Check if auto-start is enabled
autoStartEnabled = isAutoStartEnabled()
// Create menu items
mToggle := systray.AddMenuItem("Enable Jolt", "Toggle sleep prevention")
systray.AddSeparator()
// Timer submenu
mTimerSubmenu := systray.AddMenuItem("Set Timer", "Set a timer for sleep prevention")
m15min := mTimerSubmenu.AddSubMenuItem("15 minutes", "Prevent sleep for 15 minutes")
m30min := mTimerSubmenu.AddSubMenuItem("30 minutes", "Prevent sleep for 30 minutes")
m1hour := mTimerSubmenu.AddSubMenuItem("1 hour", "Prevent sleep for 1 hour")
m2hour := mTimerSubmenu.AddSubMenuItem("2 hours", "Prevent sleep for 2 hours")
mCustom := mTimerSubmenu.AddSubMenuItem("Custom...", "Set a custom timer duration")
mCancelTimer := mTimerSubmenu.AddSubMenuItem("Cancel Timer", "Cancel the active timer")
mCancelTimer.Disable()
// Auto-start menu item
mAutoStart := systray.AddMenuItem("Start at Login", "Toggle start at login")
systray.AddSeparator()
mQuit := systray.AddMenuItem("Quit", "Quit Jolt")
// Handle menu item clicks
go func() {
for {
select {
case <-mToggle.ClickedCh:
toggleSleepPrevention()
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-m15min.ClickedCh:
setTimer(15 * time.Minute)
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-m30min.ClickedCh:
setTimer(30 * time.Minute)
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-m1hour.ClickedCh:
setTimer(time.Hour)
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-m2hour.ClickedCh:
setTimer(2 * time.Hour)
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-mCustom.ClickedCh:
// In a real implementation, this would show a dialog
// For now, we'll just set a 45-minute timer as an example
setTimer(45 * time.Minute)
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-mCancelTimer.ClickedCh:
cancelTimer()
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-mAutoStart.ClickedCh:
toggleAutoStart()
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
case <-mQuit.ClickedCh:
systray.Quit()
return
}
}
}()
// Timer monitoring goroutine
go func() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for range ticker.C {
if timerActive && time.Now().After(timerEndTime) {
// Timer has expired
disableSleepPrevention()
timerActive = false
updateMenuItems(mToggle, mCancelTimer, mAutoStart)
// Show notification
showNotification("Jolt Timer Expired", "Sleep prevention has been disabled.")
}
if timerActive {
// Update the remaining time in the menu
remaining := timerEndTime.Sub(time.Now())
hours := int(remaining.Hours())
minutes := int(remaining.Minutes()) % 60
seconds := int(remaining.Seconds()) % 60
if hours > 0 {
systray.SetTitle(fmt.Sprintf("%d:%02d:%02d", hours, minutes, seconds))
} else {
systray.SetTitle(fmt.Sprintf("%d:%02d", minutes, seconds))
}
}
}
}()
}
func onExit() {
// Make sure to disable sleep prevention when exiting
if isSleepPrevented {
disableSleepPrevention()
}
}
func toggleSleepPrevention() {
if isSleepPrevented {
disableSleepPrevention()
} else {
enableSleepPrevention()
}
}
func enableSleepPrevention() {
// Use caffeinate command to prevent sleep
cmd := exec.Command("caffeinate", "-d", "-i", "-u", "-s")
err := cmd.Start()
if err != nil {
fmt.Println("Error enabling sleep prevention:", err)
return
}
isSleepPrevented = true
systray.SetIcon(getIcon("active"))
systray.SetTitle("")
systray.SetTooltip("Jolt - Sleep Prevention Active")
}
func disableSleepPrevention() {
// Kill all caffeinate processes
if err := exec.Command("killall", "caffeinate").Run(); err != nil {
fmt.Println("Error disabling sleep prevention:", err)
}
isSleepPrevented = false
timerActive = false
systray.SetIcon(getIcon("default"))
systray.SetTitle("")
systray.SetTooltip("Jolt - Prevent your Mac from sleeping")
}
func setTimer(duration time.Duration) {
timerDuration = duration
timerEndTime = time.Now().Add(duration)
timerActive = true
if !isSleepPrevented {
enableSleepPrevention()
}
}
func cancelTimer() {
timerActive = false
systray.SetTitle("")
}
func updateMenuItems(mToggle, mCancelTimer, mAutoStart *systray.MenuItem) {
if isSleepPrevented {
mToggle.SetTitle("Disable Jolt")
} else {
mToggle.SetTitle("Enable Jolt")
}
if timerActive {
mCancelTimer.Enable()
} else {
mCancelTimer.Disable()
}
if autoStartEnabled {
mAutoStart.SetTitle("Start at Login")
mAutoStart.Check()
} else {
mAutoStart.SetTitle("Start at Login")
mAutoStart.Uncheck()
}
}
// toggleAutoStart toggles the auto-start feature
func toggleAutoStart() {
if autoStartEnabled {
disableAutoStart()
} else {
enableAutoStart()
}
}
func showNotification(title, message string) {
// Use AppleScript to show a notification
script := fmt.Sprintf(`display notification "%s" with title "%s"`, message, title)
if err := exec.Command("osascript", "-e", script).Run(); err != nil {
fmt.Println("Failed to show notification:", err)
}
}
// getIcon loads and returns the appropriate icon based on the state
func getIcon(state string) []byte {
var iconName string
// Determine which icon to load based on the state
if state == "active" {
iconName = "icon_active.png"
} else {
iconName = "icon_default.png"
}
// Get the application path
appPath := getAppPath()
fmt.Printf("App path: %s\n", appPath)
// Correct icon location
iconPath := fmt.Sprintf("%s/Resources/assets/icons/%s", appPath, iconName)
// Try to load the icon
fmt.Printf("Trying to load icon from: %s\n", iconPath)
iconData, err := os.ReadFile(iconPath)
if err == nil {
fmt.Printf("Successfully loaded icon from: %s\n", iconPath)
return iconData
}
// Log error and return a placeholder if not found
fmt.Printf("Error loading icon %s: %v\n", iconName, err)
return []byte{0} // Placeholder (1x1 transparent pixel)
}
// getAppPath returns the path to the application bundle
func getAppPath() string {
// Get the executable path
execPath, err := os.Executable()
if err != nil {
fmt.Println("Error getting executable path:", err)
return ""
}
// For a macOS app bundle, the executable is in Contents/MacOS/
// Go up two directories to get the app bundle path
return filepath.Dir(filepath.Dir(execPath))
}
// isAutoStartEnabled checks if the app is set to start at login
func isAutoStartEnabled() bool {
// Get the path to the app bundle
appPath, err := exec.Command("pwd").Output()
if err != nil {
fmt.Println("Error getting current directory:", err)
return false
}
// Trim newline from the output
appPathStr := fmt.Sprintf("%s/build/Jolt.app", string(appPath[:len(appPath)-1]))
// AppleScript to check if the app is in Login Items
script := fmt.Sprintf(`
tell application "System Events"
get the name of every login item whose path contains "%s"
end tell
`, appPathStr)
output, err := exec.Command("osascript", "-e", script).Output()
if err != nil {
fmt.Println("Error checking auto-start status:", err)
return false
}
// If the output is not empty, the app is in Login Items
return len(output) > 0
}
// enableAutoStart adds the app to Login Items
func enableAutoStart() {
// Get the path to the app bundle
appPath, err := exec.Command("pwd").Output()
if err != nil {
fmt.Println("Error getting current directory:", err)
return
}
// Trim newline from the output
appPathStr := fmt.Sprintf("%s/build/Jolt.app", string(appPath[:len(appPath)-1]))
// AppleScript to add the app to Login Items
script := fmt.Sprintf(`
tell application "System Events"
make login item at end with properties {path:"%s", hidden:false}
end tell
`, appPathStr)
if err := exec.Command("osascript", "-e", script).Run(); err != nil {
fmt.Println("Error enabling auto-start:", err)
} else {
autoStartEnabled = true
}
}
// disableAutoStart removes the app from Login Items
func disableAutoStart() {
// Get the path to the app bundle
appPath, err := exec.Command("pwd").Output()
if err != nil {
fmt.Println("Error getting current directory:", err)
return
}
// Trim newline from the output
appPathStr := fmt.Sprintf("%s/build/Jolt.app", string(appPath[:len(appPath)-1]))
// AppleScript to remove the app from Login Items
script := fmt.Sprintf(`
tell application "System Events"
delete (every login item whose path contains "%s")
end tell
`, appPathStr)
if err := exec.Command("osascript", "-e", script).Run(); err != nil {
fmt.Println("Error disabling auto-start:", err)
} else {
autoStartEnabled = false
}
}