-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtray.go
More file actions
253 lines (214 loc) · 6.61 KB
/
Copy pathtray.go
File metadata and controls
253 lines (214 loc) · 6.61 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
package main
import (
"fmt"
"log"
"os"
"time"
"fyne.io/systray"
)
// runTray starts the system tray icon and blocks until the user selects "Salir".
// If autoOpen is true, it opens the dashboard via the webview abstraction.
// If systray fails, falls back to blocking forever so the HTTP service stays alive.
func runTray(dashURL string, autoOpen bool) {
defer func() {
if r := recover(); r != nil {
log.Printf("[tray] PANIC in systray: %v — falling back to headless mode", r)
select {} // keep service alive
}
}()
systray.Run(
func() { onTrayReady(dashURL, autoOpen) },
onTrayExit,
)
}
// trayPrinterInfoText builds the printer count + DPI summary string for the menu.
func trayPrinterInfoText() string {
printers, err := listAllPrinters()
if err != nil || len(printers) == 0 {
return "0 impresora(s)"
}
count := len(printers)
// Find the most common DPI among detected printers
dpiCounts := map[int]int{}
for _, p := range printers {
dpi := GetPrinterDPI(p.Name)
dpiCounts[dpi]++
}
// Pick DPI with highest count
bestDPI := defaultDPI
bestCount := 0
for dpi, c := range dpiCounts {
if c > bestCount {
bestDPI = dpi
bestCount = c
}
}
return fmt.Sprintf("%d impresora(s) — %d DPI", count, bestDPI)
}
// trayTooltipText builds the tooltip string with whitelabel branding.
func trayTooltipText() string {
cfg := getConfig()
if cfg.Whitelabel.Name != "" {
return fmt.Sprintf("TSC Bridge — %s", cfg.Whitelabel.Name)
}
return "TSC Bridge v" + version
}
// sendTestPrint sends a basic TSPL test label to the given printer.
// Uses the default preset to generate the label layout.
func sendTestPrint(printerName string) error {
cfg := getConfig()
presetID := cfg.DefaultPreset
if presetID == "" {
presetID = "matrix-3x1-30x22"
}
preset := getPresetByID(presetID, cfg.CustomPresets)
if preset == nil {
// Fallback to a simple test without preset
tspl := "SIZE 40 mm, 25 mm\r\nGAP 3 mm, 0 mm\r\nDIRECTION 0,0\r\nSPEED 4\r\nDENSITY 8\r\nSET TEAR ON\r\nCLS\r\n"
tspl += "TEXT 16,16,\"3\",0,1,1,\"TSC BRIDGE TEST\"\r\n"
tspl += fmt.Sprintf("TEXT 16,56,\"2\",0,1,1,\"v%s\"\r\n", version)
tspl += "BARCODE 16,96,\"128\",48,1,0,2,2,\"TSCBRIDGE\"\r\n"
tspl += "PRINT 1\r\n"
return sendToPrinter(printerName, []byte(tspl))
}
// Generate test label using preset (same logic as handleTestPrint)
tspl := generatePresetHeader(preset)
for i := 0; i < preset.Columns; i++ {
x := 8
if i < len(preset.ColOffsets) {
x = preset.ColOffsets[i]
}
tspl += fmt.Sprintf("TEXT %d,16,\"3\",0,1,1,\"TEST COL %d\"\r\n", x, i+1)
tspl += fmt.Sprintf("BARCODE %d,48,\"128\",40,1,0,2,2,\"12345%d\"\r\n", x, i)
}
tspl += "PRINT 1\r\n"
return sendToPrinter(printerName, []byte(tspl))
}
// sendToPrinter routes print data to the correct backend (network or local).
func sendToPrinter(printerName string, data []byte) error {
allPrinters, _ := listAllPrinters()
var targetPrinter *PrinterInfo
if printerName == "" {
if len(allPrinters) > 0 {
targetPrinter = &allPrinters[0]
printerName = targetPrinter.Name
}
} else {
targetPrinter = findPrinter(printerName, allPrinters)
}
if printerName == "" {
return fmt.Errorf("no printer available")
}
if targetPrinter != nil && (targetPrinter.Type == "network" || targetPrinter.Type == "manual" || targetPrinter.Type == "raw") {
return networkRawPrint(targetPrinter.Address, data)
}
return rawPrint(printerName, data)
}
func onTrayReady(dashURL string, autoOpen bool) {
systray.SetTooltip(trayTooltipText())
// macOS menubar icons: 22px (standard), template mode for auto dark/light
trayIcon := generateAppIcon(22)
regularIcon := generateAppIcon(22)
systray.SetTemplateIcon(trayIcon, regularIcon)
// --- Printer info (disabled) ---
mPrinterInfo := systray.AddMenuItem(trayPrinterInfoText(), "Impresoras detectadas")
mPrinterInfo.Disable()
systray.AddSeparator()
// --- Dashboard ---
mOpen := systray.AddMenuItem("Abrir Dashboard", "Abrir panel de control")
// --- Re-detect printers ---
mRescan := systray.AddMenuItem("Re-detectar impresoras", "Buscar impresoras en la red")
// --- Test Print ---
mTestPrint := systray.AddMenuItem("Test Print", "Imprimir etiqueta de prueba")
systray.AddSeparator()
// --- Auto-start toggle ---
mAutoStart := systray.AddMenuItemCheckbox(
"Iniciar con el sistema",
"Iniciar TSC Bridge al encender el equipo",
isAutoStartEnabled(),
)
systray.AddSeparator()
// --- Port & version info (disabled) ---
cfg := getConfig()
mInfo := systray.AddMenuItem(
fmt.Sprintf("Puerto %d — v%s", cfg.Port, version),
"Informacion del servicio",
)
mInfo.Disable()
systray.AddSeparator()
// --- Quit ---
mQuit := systray.AddMenuItem("Salir", "Detener servicio y salir")
// Auto-open dashboard on first launch
if autoOpen {
go func() {
time.Sleep(2 * time.Second) // let HTTP server goroutine start accepting
showDashboard(dashURL)
}()
}
// Background goroutine: update printer info every 10 seconds
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for range ticker.C {
text := trayPrinterInfoText()
mPrinterInfo.SetTitle(text)
mPrinterInfo.SetTooltip("Impresoras detectadas")
}
}()
// Event loop
go func() {
for {
select {
case <-mOpen.ClickedCh:
go showDashboard(dashURL)
case <-mRescan.ClickedCh:
go func() {
log.Printf("[tray] Re-detecting printers...")
mRescan.SetTitle("Escaneando...")
mRescan.Disable()
refreshNetworkPrinters()
DetectAllPrinterDPIs()
text := trayPrinterInfoText()
mPrinterInfo.SetTitle(text)
mRescan.SetTitle("Re-detectar impresoras")
mRescan.Enable()
log.Printf("[tray] Re-detection complete: %s", text)
}()
case <-mTestPrint.ClickedCh:
go func() {
cfg := getConfig()
printerName := cfg.DefaultPrinter
log.Printf("[tray] Test print requested (printer=%q)", printerName)
if err := sendTestPrint(printerName); err != nil {
log.Printf("[tray] Test print failed: %v", err)
} else {
log.Printf("[tray] Test print sent successfully")
}
}()
case <-mAutoStart.ClickedCh:
enabled := !isAutoStartEnabled()
if err := setAutoStart(enabled); err != nil {
log.Printf("[tray] autostart toggle error: %v", err)
} else {
if enabled {
mAutoStart.Check()
} else {
mAutoStart.Uncheck()
}
configMu.Lock()
appConfig.AutoStart = enabled
configMu.Unlock()
saveConfig()
log.Printf("[tray] autostart toggled: %v", enabled)
}
case <-mQuit.ClickedCh:
systray.Quit()
}
}
}()
}
func onTrayExit() {
log.Printf("System tray exit — shutting down")
destroyWebview()
os.Exit(0)
}