github.com/shurcool/trayhost@v0.0.0-20181020202213-114974ef9e16/trayhost.go (about)

     1  package trayhost
     2  
     3  import (
     4  	"time"
     5  	"unsafe"
     6  )
     7  
     8  /*
     9  #cgo darwin CFLAGS: -DDARWIN -x objective-c
    10  #cgo darwin LDFLAGS: -framework Cocoa
    11  
    12  #cgo linux pkg-config: gtk+-2.0
    13  #cgo linux CFLAGS: -DLINUX -I/usr/include/libappindicator-0.1
    14  #cgo linux LDFLAGS: -ldl
    15  
    16  #cgo windows CFLAGS: -DWIN32
    17  
    18  #include <stdlib.h>
    19  #include "platform/common.h"
    20  #include "platform/platform.h"
    21  */
    22  import "C"
    23  
    24  var menuItems []MenuItem
    25  
    26  // MenuItem is a menu item.
    27  type MenuItem struct {
    28  	// Title is the title of menu item.
    29  	//
    30  	// If empty, it acts as a separator. SeparatorMenuItem can be used
    31  	// to create such separator menu items.
    32  	Title string
    33  
    34  	// Enabled can optionally control if this menu item is enabled or disabled.
    35  	//
    36  	// nil means always enabled.
    37  	Enabled func() bool
    38  
    39  	// Handler is triggered when the item is activated. nil means no handler.
    40  	Handler func()
    41  }
    42  
    43  // Initialize sets up the application properties.
    44  // imageData is the icon image in PNG format.
    45  func Initialize(title string, imageData []byte, items []MenuItem) {
    46  	cTitle := C.CString(title)
    47  	defer C.free(unsafe.Pointer(cTitle))
    48  	img, freeImg := create_image(Image{Kind: "png", Bytes: imageData})
    49  	defer freeImg()
    50  
    51  	// Initialize menu.
    52  	C.init(cTitle, img)
    53  
    54  	menuItems = items
    55  	for id, item := range menuItems {
    56  		addItem(id, item)
    57  	}
    58  }
    59  
    60  // EnterLoop enters main loop.
    61  func EnterLoop() {
    62  	C.native_loop()
    63  }
    64  
    65  // Exit exits the application. It can be called from a MenuItem handler.
    66  func Exit() {
    67  	C.exit_loop()
    68  }
    69  
    70  // SeparatorMenuItem creates a separator MenuItem.
    71  func SeparatorMenuItem() MenuItem { return MenuItem{Title: ""} }
    72  
    73  func addItem(id int, item MenuItem) {
    74  	if item.Title == "" {
    75  		C.add_separator_item()
    76  	} else {
    77  		// ignore errors
    78  		addMenuItem(id, item)
    79  	}
    80  }
    81  
    82  func cAddMenuItem(id C.int, title *C.char, disabled C.int) {
    83  	C.add_menu_item(id, title, disabled)
    84  }
    85  
    86  func cbool(b bool) C.int {
    87  	if b {
    88  		return 1
    89  	}
    90  	return 0
    91  }
    92  
    93  // ---
    94  
    95  // SetClipboardText sets the system clipboard to the specified UTF-8 encoded
    96  // string.
    97  //
    98  // This function may only be called from the main thread.
    99  func SetClipboardText(text string) {
   100  	cp := C.CString(text)
   101  	defer C.free(unsafe.Pointer(cp))
   102  
   103  	C.set_clipboard_string(cp)
   104  }
   105  
   106  // ImageKind is a file extension in lower case: "png", "jpg", "tiff", etc. Empty string means no image.
   107  type ImageKind string
   108  
   109  // Image is an encoded image of certain kind.
   110  type Image struct {
   111  	Kind  ImageKind
   112  	Bytes []byte
   113  }
   114  
   115  // ClipboardContent holds the contents of system clipboard.
   116  type ClipboardContent struct {
   117  	Text  string
   118  	Image Image
   119  	Files []string
   120  }
   121  
   122  // GetClipboardContent returns the contents of the system clipboard, if it
   123  // contains or is convertible to a UTF-8 encoded string, image, and/or files.
   124  //
   125  // This function may only be called from the main thread.
   126  func GetClipboardContent() (ClipboardContent, error) {
   127  	var cc ClipboardContent
   128  
   129  	ccc := C.get_clipboard_content()
   130  	if ccc.text != nil {
   131  		cc.Text = C.GoString(ccc.text)
   132  	}
   133  	if ccc.image.kind != nil {
   134  		cc.Image = Image{
   135  			Kind:  ImageKind(C.GoString(ccc.image.kind)),
   136  			Bytes: C.GoBytes(ccc.image.bytes, ccc.image.length),
   137  		}
   138  	}
   139  	if ccc.files.count > 0 {
   140  		cc.Files = make([]string, int(ccc.files.count))
   141  		for i := 0; i < int(ccc.files.count); i++ {
   142  			var x *C.char
   143  			p := (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(ccc.files.names)) + uintptr(i)*unsafe.Sizeof(x)))
   144  			cc.Files[i] = C.GoString(*p)
   145  		}
   146  	}
   147  
   148  	return cc, nil
   149  }
   150  
   151  // ---
   152  
   153  // TODO: Garbage collection. Really only need this until the notification is cleared, so its Handler is accessible.
   154  var notifications []Notification
   155  
   156  // Notification represents a user notification.
   157  type Notification struct {
   158  	Title string // Title of user notification.
   159  	Body  string // Body of user notification.
   160  	Image Image  // Image shown in the content of user notification.
   161  
   162  	// Timeout specifies time after which the notification is cleared.
   163  	//
   164  	// A Timeout of zero means no timeout.
   165  	Timeout time.Duration
   166  
   167  	// Activation (click) handler.
   168  	//
   169  	// nil means no handler.
   170  	Handler func()
   171  }
   172  
   173  // Display displays the user notification.
   174  func (n Notification) Display() {
   175  	cTitle := C.CString(n.Title)
   176  	defer C.free(unsafe.Pointer(cTitle))
   177  	cBody := C.CString(n.Body)
   178  	defer C.free(unsafe.Pointer(cBody))
   179  	img, freeImg := create_image(n.Image)
   180  	defer freeImg()
   181  
   182  	// TODO: Move out of Display.
   183  	notificationId := (C.int)(len(notifications))
   184  	notifications = append(notifications, n)
   185  
   186  	C.display_notification(notificationId, cTitle, cBody, img, C.double(n.Timeout.Seconds()))
   187  }
   188  
   189  // UpdateMenu removes all current menu items, and adds new menu items.
   190  func UpdateMenu(newMenu []MenuItem) {
   191  	C.clear_menu_items()
   192  	menuItems = newMenu
   193  	for id, item := range newMenu {
   194  		addItem(id, item)
   195  	}
   196  }