diff options
Diffstat (limited to 'vendor/github.com/getlantern/systray/systray.go')
-rw-r--r-- | vendor/github.com/getlantern/systray/systray.go | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/vendor/github.com/getlantern/systray/systray.go b/vendor/github.com/getlantern/systray/systray.go new file mode 100644 index 0000000..d433173 --- /dev/null +++ b/vendor/github.com/getlantern/systray/systray.go @@ -0,0 +1,181 @@ +/* +Package systray is a cross platfrom Go library to place an icon and menu in the +notification area. +Supports Windows, Mac OSX and Linux currently. +Methods can be called from any goroutine except Run(), which should be called +at the very beginning of main() to lock at main thread. +*/ +package systray + +import ( + "runtime" + "sync" + "sync/atomic" + + "github.com/getlantern/golog" +) + +var ( + hasStarted = int64(0) + hasQuit = int64(0) +) + +// MenuItem is used to keep track each menu item of systray +// Don't create it directly, use the one systray.AddMenuItem() returned +type MenuItem struct { + // ClickedCh is the channel which will be notified when the menu item is clicked + ClickedCh chan struct{} + + // id uniquely identify a menu item, not supposed to be modified + id int32 + // title is the text shown on menu item + title string + // tooltip is the text shown when pointing to menu item + tooltip string + // disabled menu item is grayed out and has no effect when clicked + disabled bool + // checked menu item has a tick before the title + checked bool +} + +var ( + log = golog.LoggerFor("systray") + + systrayReady func() + systrayExit func() + menuItems = make(map[int32]*MenuItem) + menuItemsLock sync.RWMutex + + currentID = int32(-1) +) + +// Run initializes GUI and starts the event loop, then invokes the onReady +// callback. +// It blocks until systray.Quit() is called. +// Should be called at the very beginning of main() to lock at main thread. +func Run(onReady func(), onExit func()) { + runtime.LockOSThread() + atomic.StoreInt64(&hasStarted, 1) + + if onReady == nil { + systrayReady = func() {} + } else { + // Run onReady on separate goroutine to avoid blocking event loop + readyCh := make(chan interface{}) + go func() { + <-readyCh + onReady() + }() + systrayReady = func() { + close(readyCh) + } + } + + // unlike onReady, onExit runs in the event loop to make sure it has time to + // finish before the process terminates + if onExit == nil { + onExit = func() {} + } + systrayExit = onExit + + nativeLoop() +} + +// Quit the systray +func Quit() { + if atomic.LoadInt64(&hasStarted) == 1 && atomic.CompareAndSwapInt64(&hasQuit, 0, 1) { + quit() + } +} + +// AddMenuItem adds menu item with designated title and tooltip, returning a channel +// that notifies whenever that menu item is clicked. +// +// It can be safely invoked from different goroutines. +func AddMenuItem(title string, tooltip string) *MenuItem { + id := atomic.AddInt32(¤tID, 1) + item := &MenuItem{nil, id, title, tooltip, false, false} + item.ClickedCh = make(chan struct{}) + item.update() + return item +} + +// AddSeparator adds a separator bar to the menu +func AddSeparator() { + addSeparator(atomic.AddInt32(¤tID, 1)) +} + +// SetTitle set the text to display on a menu item +func (item *MenuItem) SetTitle(title string) { + item.title = title + item.update() +} + +// SetTooltip set the tooltip to show when mouse hover +func (item *MenuItem) SetTooltip(tooltip string) { + item.tooltip = tooltip + item.update() +} + +// Disabled checkes if the menu item is disabled +func (item *MenuItem) Disabled() bool { + return item.disabled +} + +// Enable a menu item regardless if it's previously enabled or not +func (item *MenuItem) Enable() { + item.disabled = false + item.update() +} + +// Disable a menu item regardless if it's previously disabled or not +func (item *MenuItem) Disable() { + item.disabled = true + item.update() +} + +// Hide hides a menu item +func (item *MenuItem) Hide() { + hideMenuItem(item) +} + +// Show shows a previously hidden menu item +func (item *MenuItem) Show() { + showMenuItem(item) +} + +// Checked returns if the menu item has a check mark +func (item *MenuItem) Checked() bool { + return item.checked +} + +// Check a menu item regardless if it's previously checked or not +func (item *MenuItem) Check() { + item.checked = true + item.update() +} + +// Uncheck a menu item regardless if it's previously unchecked or not +func (item *MenuItem) Uncheck() { + item.checked = false + item.update() +} + +// update propogates changes on a menu item to systray +func (item *MenuItem) update() { + menuItemsLock.Lock() + defer menuItemsLock.Unlock() + menuItems[item.id] = item + addOrUpdateMenuItem(item) +} + +func systrayMenuItemSelected(id int32) { + menuItemsLock.RLock() + item := menuItems[id] + menuItemsLock.RUnlock() + select { + case item.ClickedCh <- struct{}{}: + // in case no one waiting for the channel + default: + } +} |