summaryrefslogtreecommitdiff
path: root/vendor/github.com/getlantern/systray/systray.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/getlantern/systray/systray.go')
-rw-r--r--vendor/github.com/getlantern/systray/systray.go181
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(&currentID, 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(&currentID, 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:
+ }
+}