mirror of
https://github.com/nkanaev/yarr.git
synced 2026-05-07 01:36:00 +00:00
switch to fyne.io/systray
This commit is contained in:
@@ -16,11 +16,6 @@ The licenses are included, and the authorship comments are left intact.
|
||||
- allowed uri schemes
|
||||
- added svg whitelist
|
||||
|
||||
- systray
|
||||
https://github.com/getlantern/systray (commit:2c0986d) Apache 2.0
|
||||
|
||||
removed golog dependency
|
||||
|
||||
- fixconsole
|
||||
https://github.com/apenwarr/fixconsole (commit:5a9f648) Apache 2.0
|
||||
|
||||
|
||||
6
go.mod
6
go.mod
@@ -5,9 +5,13 @@ go 1.23.0
|
||||
toolchain go1.23.5
|
||||
|
||||
require (
|
||||
fyne.io/systray v1.12.0
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/sys v0.31.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.23.0 // indirect
|
||||
require (
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1,3 +1,7 @@
|
||||
fyne.io/systray v1.12.0 h1:CA1Kk0e2zwFlxtc02L3QFSiIbxJ/P0n582YrZHT7aTM=
|
||||
fyne.io/systray v1.12.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"fyne.io/systray"
|
||||
"github.com/nkanaev/yarr/src/server"
|
||||
"github.com/nkanaev/yarr/src/systray"
|
||||
)
|
||||
|
||||
func Start(s *server.Server) {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
taken from:
|
||||
|
||||
repo:
|
||||
https://github.com/getlantern/systray
|
||||
|
||||
hash:
|
||||
2c0986dda9aea361e925f90e848d9036be7b5367
|
||||
|
||||
changes:
|
||||
|
||||
-removed `getlantern/golog` dependency
|
||||
-prevent from compiling in linux
|
||||
@@ -1,120 +0,0 @@
|
||||
systray is a cross-platform Go library to place an icon and menu in the notification area.
|
||||
|
||||
## Features
|
||||
|
||||
* Supported on Windows, macOS, and Linux
|
||||
* Menu items can be checked and/or disabled
|
||||
* Methods may be called from any Goroutine
|
||||
|
||||
## API
|
||||
|
||||
```go
|
||||
func main() {
|
||||
systray.Run(onReady, onExit)
|
||||
}
|
||||
|
||||
func onReady() {
|
||||
systray.SetIcon(icon.Data)
|
||||
systray.SetTitle("Awesome App")
|
||||
systray.SetTooltip("Pretty awesome超级棒")
|
||||
mQuit := systray.AddMenuItem("Quit", "Quit the whole app")
|
||||
|
||||
// Sets the icon of a menu item. Only available on Mac and Windows.
|
||||
mQuit.SetIcon(icon.Data)
|
||||
}
|
||||
|
||||
func onExit() {
|
||||
// clean up here
|
||||
}
|
||||
```
|
||||
|
||||
See [full API](https://pkg.go.dev/github.com/getlantern/systray?tab=doc) as well as [CHANGELOG](https://github.com/getlantern/systray/tree/master/CHANGELOG.md).
|
||||
|
||||
## Try the example app!
|
||||
|
||||
Have go v1.12+ or higher installed? Here's an example to get started on macOS:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/getlantern/systray
|
||||
cd example
|
||||
env GO111MODULE=on go build
|
||||
./example
|
||||
```
|
||||
|
||||
On Windows, you should build like this:
|
||||
|
||||
```
|
||||
env GO111MODULE=on go build -ldflags "-H=windowsgui"
|
||||
```
|
||||
|
||||
The following text will then appear on the console:
|
||||
|
||||
|
||||
```sh
|
||||
go: finding github.com/skratchdot/open-golang latest
|
||||
go: finding github.com/getlantern/systray latest
|
||||
go: finding github.com/getlantern/golog latest
|
||||
```
|
||||
|
||||
Now look for *Awesome App* in your menu bar!
|
||||
|
||||

|
||||
|
||||
## The Webview example
|
||||
|
||||
The code under `webview_example` is to demostrate how it can co-exist with other UI elements. Note that the example doesn't work on macOS versions older than 10.15 Catalina.
|
||||
|
||||
## Platform notes
|
||||
|
||||
### Linux
|
||||
|
||||
* Building apps requires gcc as well as the `gtk3` and `libappindicator3` development headers to be installed. For Debian or Ubuntu, you may install these using:
|
||||
|
||||
```sh
|
||||
sudo apt-get install gcc libgtk-3-dev libappindicator3-dev
|
||||
```
|
||||
|
||||
On Linux Mint, `libxapp-dev` is also required .
|
||||
|
||||
To build `webview_example`, you also need to install `libwebkit2gtk-4.0-dev` and remove `webview_example/rsrc.syso` which is required on Windows.
|
||||
|
||||
### Windows
|
||||
|
||||
* To avoid opening a console at application startup, use these compile flags:
|
||||
|
||||
```sh
|
||||
go build -ldflags -H=windowsgui
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
On macOS, you will need to create an application bundle to wrap the binary; simply folders with the following minimal structure and assets:
|
||||
|
||||
```
|
||||
SystrayApp.app/
|
||||
Contents/
|
||||
Info.plist
|
||||
MacOS/
|
||||
go-executable
|
||||
Resources/
|
||||
SystrayApp.icns
|
||||
```
|
||||
|
||||
When running as an app bundle, you may want to add one or both of the following to your Info.plist:
|
||||
|
||||
```xml
|
||||
<!-- avoid having a blurry icon and text -->
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
|
||||
<!-- avoid showing the app on the Dock -->
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
```
|
||||
|
||||
Consult the [Official Apple Documentation here](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1).
|
||||
|
||||
## Credits
|
||||
|
||||
- https://github.com/xilp/systray
|
||||
- https://github.com/cratonica/trayhost
|
||||
@@ -1,268 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <libappindicator/app-indicator.h>
|
||||
#include "systray.h"
|
||||
|
||||
static AppIndicator *global_app_indicator;
|
||||
static GtkWidget *global_tray_menu = NULL;
|
||||
static GList *global_menu_items = NULL;
|
||||
static char temp_file_name[PATH_MAX] = "";
|
||||
|
||||
typedef struct {
|
||||
GtkWidget *menu_item;
|
||||
int menu_id;
|
||||
long signalHandlerId;
|
||||
} MenuItemNode;
|
||||
|
||||
typedef struct {
|
||||
int menu_id;
|
||||
int parent_menu_id;
|
||||
char* title;
|
||||
char* tooltip;
|
||||
short disabled;
|
||||
short checked;
|
||||
short isCheckable;
|
||||
} MenuItemInfo;
|
||||
|
||||
void registerSystray(void) {
|
||||
gtk_init(0, NULL);
|
||||
global_app_indicator = app_indicator_new("systray", "", APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
|
||||
app_indicator_set_status(global_app_indicator, APP_INDICATOR_STATUS_ACTIVE);
|
||||
global_tray_menu = gtk_menu_new();
|
||||
app_indicator_set_menu(global_app_indicator, GTK_MENU(global_tray_menu));
|
||||
systray_ready();
|
||||
}
|
||||
|
||||
int nativeLoop(void) {
|
||||
gtk_main();
|
||||
systray_on_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _unlink_temp_file() {
|
||||
if (strlen(temp_file_name) != 0) {
|
||||
int ret = unlink(temp_file_name);
|
||||
if (ret == -1) {
|
||||
printf("failed to remove temp icon file %s: %s\n", temp_file_name, strerror(errno));
|
||||
}
|
||||
temp_file_name[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
// runs in main thread, should always return FALSE to prevent gtk to execute it again
|
||||
gboolean do_set_icon(gpointer data) {
|
||||
_unlink_temp_file();
|
||||
char *tmpdir = getenv("TMPDIR");
|
||||
if (NULL == tmpdir) {
|
||||
tmpdir = "/tmp";
|
||||
}
|
||||
strncpy(temp_file_name, tmpdir, PATH_MAX-1);
|
||||
strncat(temp_file_name, "/systray_XXXXXX", PATH_MAX-1);
|
||||
temp_file_name[PATH_MAX-1] = '\0';
|
||||
|
||||
GBytes* bytes = (GBytes*)data;
|
||||
int fd = mkstemp(temp_file_name);
|
||||
if (fd == -1) {
|
||||
printf("failed to create temp icon file %s: %s\n", temp_file_name, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
gsize size = 0;
|
||||
gconstpointer icon_data = g_bytes_get_data(bytes, &size);
|
||||
ssize_t written = write(fd, icon_data, size);
|
||||
close(fd);
|
||||
if(written != size) {
|
||||
printf("failed to write temp icon file %s: %s\n", temp_file_name, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
app_indicator_set_icon_full(global_app_indicator, temp_file_name, "");
|
||||
app_indicator_set_attention_icon_full(global_app_indicator, temp_file_name, "");
|
||||
g_bytes_unref(bytes);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void _systray_menu_item_selected(int *id) {
|
||||
systray_menu_item_selected(*id);
|
||||
}
|
||||
|
||||
GtkMenuItem* find_menu_by_id(int id) {
|
||||
GList* it;
|
||||
for(it = global_menu_items; it != NULL; it = it->next) {
|
||||
MenuItemNode* item = (MenuItemNode*)(it->data);
|
||||
if(item->menu_id == id) {
|
||||
return GTK_MENU_ITEM(item->menu_item);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// runs in main thread, should always return FALSE to prevent gtk to execute it again
|
||||
gboolean do_add_or_update_menu_item(gpointer data) {
|
||||
MenuItemInfo *mii = (MenuItemInfo*)data;
|
||||
GList* it;
|
||||
for(it = global_menu_items; it != NULL; it = it->next) {
|
||||
MenuItemNode* item = (MenuItemNode*)(it->data);
|
||||
if(item->menu_id == mii->menu_id) {
|
||||
gtk_menu_item_set_label(GTK_MENU_ITEM(item->menu_item), mii->title);
|
||||
|
||||
if (mii->isCheckable) {
|
||||
// We need to block the "activate" event, to emulate the same behaviour as in the windows version
|
||||
// A Check/Uncheck does change the checkbox, but does not trigger the checkbox menuItem channel
|
||||
g_signal_handler_block(GTK_CHECK_MENU_ITEM(item->menu_item), item->signalHandlerId);
|
||||
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->menu_item), mii->checked == 1);
|
||||
g_signal_handler_unblock(GTK_CHECK_MENU_ITEM(item->menu_item), item->signalHandlerId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// menu id doesn't exist, add new item
|
||||
if(it == NULL) {
|
||||
GtkWidget *menu_item;
|
||||
if (mii->isCheckable) {
|
||||
menu_item = gtk_check_menu_item_new_with_label(mii->title);
|
||||
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item), mii->checked == 1);
|
||||
} else {
|
||||
menu_item = gtk_menu_item_new_with_label(mii->title);
|
||||
}
|
||||
int *id = malloc(sizeof(int));
|
||||
*id = mii->menu_id;
|
||||
long signalHandlerId = g_signal_connect_swapped(
|
||||
G_OBJECT(menu_item),
|
||||
"activate",
|
||||
G_CALLBACK(_systray_menu_item_selected),
|
||||
id
|
||||
);
|
||||
|
||||
if (mii->parent_menu_id == 0) {
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(global_tray_menu), menu_item);
|
||||
} else {
|
||||
GtkMenuItem* parentMenuItem = find_menu_by_id(mii->parent_menu_id);
|
||||
GtkWidget* parentMenu = gtk_menu_item_get_submenu(parentMenuItem);
|
||||
|
||||
if(parentMenu == NULL) {
|
||||
parentMenu = gtk_menu_new();
|
||||
gtk_menu_item_set_submenu(parentMenuItem, parentMenu);
|
||||
}
|
||||
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(parentMenu), menu_item);
|
||||
}
|
||||
|
||||
MenuItemNode* new_item = malloc(sizeof(MenuItemNode));
|
||||
new_item->menu_id = mii->menu_id;
|
||||
new_item->signalHandlerId = signalHandlerId;
|
||||
new_item->menu_item = menu_item;
|
||||
GList* new_node = malloc(sizeof(GList));
|
||||
new_node->data = new_item;
|
||||
new_node->next = global_menu_items;
|
||||
if(global_menu_items != NULL) {
|
||||
global_menu_items->prev = new_node;
|
||||
}
|
||||
global_menu_items = new_node;
|
||||
it = new_node;
|
||||
}
|
||||
GtkWidget* menu_item = GTK_WIDGET(((MenuItemNode*)(it->data))->menu_item);
|
||||
gtk_widget_set_sensitive(menu_item, mii->disabled != 1);
|
||||
gtk_widget_show(menu_item);
|
||||
|
||||
free(mii->title);
|
||||
free(mii->tooltip);
|
||||
free(mii);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean do_add_separator(gpointer data) {
|
||||
GtkWidget *separator = gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_append(GTK_MENU_SHELL(global_tray_menu), separator);
|
||||
gtk_widget_show(separator);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// runs in main thread, should always return FALSE to prevent gtk to execute it again
|
||||
gboolean do_hide_menu_item(gpointer data) {
|
||||
MenuItemInfo *mii = (MenuItemInfo*)data;
|
||||
GList* it;
|
||||
for(it = global_menu_items; it != NULL; it = it->next) {
|
||||
MenuItemNode* item = (MenuItemNode*)(it->data);
|
||||
if(item->menu_id == mii->menu_id){
|
||||
gtk_widget_hide(GTK_WIDGET(item->menu_item));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// runs in main thread, should always return FALSE to prevent gtk to execute it again
|
||||
gboolean do_show_menu_item(gpointer data) {
|
||||
MenuItemInfo *mii = (MenuItemInfo*)data;
|
||||
GList* it;
|
||||
for(it = global_menu_items; it != NULL; it = it->next) {
|
||||
MenuItemNode* item = (MenuItemNode*)(it->data);
|
||||
if(item->menu_id == mii->menu_id){
|
||||
gtk_widget_show(GTK_WIDGET(item->menu_item));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// runs in main thread, should always return FALSE to prevent gtk to execute it again
|
||||
gboolean do_quit(gpointer data) {
|
||||
_unlink_temp_file();
|
||||
// app indicator doesn't provide a way to remove it, hide it as a workaround
|
||||
app_indicator_set_status(global_app_indicator, APP_INDICATOR_STATUS_PASSIVE);
|
||||
gtk_main_quit();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void setIcon(const char* iconBytes, int length, bool template) {
|
||||
GBytes* bytes = g_bytes_new_static(iconBytes, length);
|
||||
g_idle_add(do_set_icon, bytes);
|
||||
}
|
||||
|
||||
void setTitle(char* ctitle) {
|
||||
app_indicator_set_label(global_app_indicator, ctitle, "");
|
||||
free(ctitle);
|
||||
}
|
||||
|
||||
void setTooltip(char* ctooltip) {
|
||||
free(ctooltip);
|
||||
}
|
||||
|
||||
void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template) {
|
||||
}
|
||||
|
||||
void add_or_update_menu_item(int menu_id, int parent_menu_id, char* title, char* tooltip, short disabled, short checked, short isCheckable) {
|
||||
MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
|
||||
mii->menu_id = menu_id;
|
||||
mii->parent_menu_id = parent_menu_id;
|
||||
mii->title = title;
|
||||
mii->tooltip = tooltip;
|
||||
mii->disabled = disabled;
|
||||
mii->checked = checked;
|
||||
mii->isCheckable = isCheckable;
|
||||
g_idle_add(do_add_or_update_menu_item, mii);
|
||||
}
|
||||
|
||||
void add_separator(int menu_id) {
|
||||
MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
|
||||
mii->menu_id = menu_id;
|
||||
g_idle_add(do_add_separator, mii);
|
||||
}
|
||||
|
||||
void hide_menu_item(int menu_id) {
|
||||
MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
|
||||
mii->menu_id = menu_id;
|
||||
g_idle_add(do_hide_menu_item, mii);
|
||||
}
|
||||
|
||||
void show_menu_item(int menu_id) {
|
||||
MenuItemInfo *mii = malloc(sizeof(MenuItemInfo));
|
||||
mii->menu_id = menu_id;
|
||||
g_idle_add(do_show_menu_item, mii);
|
||||
}
|
||||
|
||||
void quit() {
|
||||
g_idle_add(do_quit, NULL);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package systray
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DDARWIN -x objective-c -fobjc-arc
|
||||
#cgo darwin LDFLAGS: -framework Cocoa -framework WebKit
|
||||
|
||||
#include "systray.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SetTemplateIcon sets the systray icon as a template icon (on Mac), falling back
|
||||
// to a regular icon on other platforms.
|
||||
// templateIconBytes and regularIconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
|
||||
C.setIcon(cstr, (C.int)(len(templateIconBytes)), true)
|
||||
}
|
||||
|
||||
// SetIcon sets the icon of a menu item. Only works on macOS and Windows.
|
||||
// iconBytes should be the content of .ico/.jpg/.png
|
||||
func (item *MenuItem) SetIcon(iconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
|
||||
C.setMenuItemIcon(cstr, (C.int)(len(iconBytes)), C.int(item.id), false)
|
||||
}
|
||||
|
||||
// SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows, it
|
||||
// falls back to the regular icon bytes and on Linux it does nothing.
|
||||
// templateIconBytes and regularIconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
|
||||
C.setMenuItemIcon(cstr, (C.int)(len(templateIconBytes)), C.int(item.id), true)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//go:build never
|
||||
// +build never
|
||||
|
||||
package systray
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DDARWIN -x objective-c -fobjc-arc
|
||||
#cgo darwin LDFLAGS: -framework Cocoa -framework WebKit
|
||||
|
||||
#include "systray.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// SetTemplateIcon sets the systray icon as a template icon (on macOS), falling back
|
||||
// to a regular icon on other platforms.
|
||||
// templateIconBytes and iconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
SetIcon(regularIconBytes)
|
||||
}
|
||||
|
||||
// SetIcon sets the icon of a menu item. Only works on macOS and Windows.
|
||||
// iconBytes should be the content of .ico/.jpg/.png
|
||||
func (item *MenuItem) SetIcon(iconBytes []byte) {
|
||||
}
|
||||
|
||||
// SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows, it
|
||||
// falls back to the regular icon bytes and on Linux it does nothing.
|
||||
// templateIconBytes and regularIconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package systray
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DDARWIN -x objective-c -fobjc-arc
|
||||
#cgo darwin LDFLAGS: -framework Cocoa
|
||||
|
||||
#include "systray.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func registerSystray() {
|
||||
C.registerSystray()
|
||||
}
|
||||
|
||||
func nativeLoop() {
|
||||
C.nativeLoop()
|
||||
}
|
||||
|
||||
func quit() {
|
||||
C.quit()
|
||||
}
|
||||
|
||||
// SetIcon sets the systray icon.
|
||||
// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
|
||||
// for other platforms.
|
||||
func SetIcon(iconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
|
||||
C.setIcon(cstr, (C.int)(len(iconBytes)), false)
|
||||
}
|
||||
|
||||
// SetTitle sets the systray title, only available on Mac and Linux.
|
||||
func SetTitle(title string) {
|
||||
C.setTitle(C.CString(title))
|
||||
}
|
||||
|
||||
// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
|
||||
// only available on Mac and Windows.
|
||||
func SetTooltip(tooltip string) {
|
||||
C.setTooltip(C.CString(tooltip))
|
||||
}
|
||||
|
||||
func addOrUpdateMenuItem(item *MenuItem) {
|
||||
var disabled C.short
|
||||
if item.disabled {
|
||||
disabled = 1
|
||||
}
|
||||
var checked C.short
|
||||
if item.checked {
|
||||
checked = 1
|
||||
}
|
||||
var isCheckable C.short
|
||||
if item.isCheckable {
|
||||
isCheckable = 1
|
||||
}
|
||||
var parentID uint32 = 0
|
||||
if item.parent != nil {
|
||||
parentID = item.parent.id
|
||||
}
|
||||
C.add_or_update_menu_item(
|
||||
C.int(item.id),
|
||||
C.int(parentID),
|
||||
C.CString(item.title),
|
||||
C.CString(item.tooltip),
|
||||
disabled,
|
||||
checked,
|
||||
isCheckable,
|
||||
)
|
||||
}
|
||||
|
||||
func addSeparator(id uint32) {
|
||||
C.add_separator(C.int(id))
|
||||
}
|
||||
|
||||
func hideMenuItem(item *MenuItem) {
|
||||
C.hide_menu_item(
|
||||
C.int(item.id),
|
||||
)
|
||||
}
|
||||
|
||||
func showMenuItem(item *MenuItem) {
|
||||
C.show_menu_item(
|
||||
C.int(item.id),
|
||||
)
|
||||
}
|
||||
|
||||
//export systray_ready
|
||||
func systray_ready() {
|
||||
systrayReady()
|
||||
}
|
||||
|
||||
//export systray_on_exit
|
||||
func systray_on_exit() {
|
||||
systrayExit()
|
||||
}
|
||||
|
||||
//export systray_menu_item_selected
|
||||
func systray_menu_item_selected(cID C.int) {
|
||||
systrayMenuItemSelected(uint32(cID))
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package systray
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const iconFilePath = "example/icon/iconwin.ico"
|
||||
|
||||
func TestBaseWindowsTray(t *testing.T) {
|
||||
systrayReady = func() {}
|
||||
systrayExit = func() {}
|
||||
|
||||
runtime.LockOSThread()
|
||||
|
||||
if err := wt.initInstance(); err != nil {
|
||||
t.Fatalf("initInstance failed: %s", err)
|
||||
}
|
||||
|
||||
if err := wt.createMenu(); err != nil {
|
||||
t.Fatalf("createMenu failed: %s", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
pDestroyWindow.Call(uintptr(wt.window))
|
||||
wt.wcex.unregister()
|
||||
}()
|
||||
|
||||
if err := wt.setIcon(iconFilePath); err != nil {
|
||||
t.Errorf("SetIcon failed: %s", err)
|
||||
}
|
||||
|
||||
if err := wt.setTooltip("Cyrillic tooltip тест:)"); err != nil {
|
||||
t.Errorf("SetIcon failed: %s", err)
|
||||
}
|
||||
|
||||
var id int32 = 0
|
||||
err := wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple enabled", false, false)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple disabled", true, false)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addSeparatorMenuItem(atomic.AddInt32(&id, 1))
|
||||
if err != nil {
|
||||
t.Errorf("addSeparatorMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple checked enabled", false, true)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
err = wt.addOrUpdateMenuItem(atomic.AddInt32(&id, 1), "Simple checked disabled", true, true)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
|
||||
err = wt.hideMenuItem(1)
|
||||
if err != nil {
|
||||
t.Errorf("hideMenuItem failed: %s", err)
|
||||
}
|
||||
|
||||
err = wt.hideMenuItem(100)
|
||||
if err == nil {
|
||||
t.Error("hideMenuItem failed: must return error on invalid item id")
|
||||
}
|
||||
|
||||
err = wt.addOrUpdateMenuItem(2, "Simple disabled update", true, false)
|
||||
if err != nil {
|
||||
t.Errorf("mergeMenuItem failed: %s", err)
|
||||
}
|
||||
|
||||
time.AfterFunc(1*time.Second, quit)
|
||||
|
||||
m := struct {
|
||||
WindowHandle windows.Handle
|
||||
Message uint32
|
||||
Wparam uintptr
|
||||
Lparam uintptr
|
||||
Time uint32
|
||||
Pt point
|
||||
}{}
|
||||
for {
|
||||
ret, _, err := pGetMessage.Call(uintptr(unsafe.Pointer(&m)), 0, 0, 0)
|
||||
res := int32(ret)
|
||||
if res == -1 {
|
||||
t.Errorf("win32 GetMessage failed: %v", err)
|
||||
return
|
||||
} else if res == 0 {
|
||||
break
|
||||
}
|
||||
pTranslateMessage.Call(uintptr(unsafe.Pointer(&m)))
|
||||
pDispatchMessage.Call(uintptr(unsafe.Pointer(&m)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowsRun(t *testing.T) {
|
||||
onReady := func() {
|
||||
b, err := ioutil.ReadFile(iconFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't load icon file: %v", err)
|
||||
}
|
||||
SetIcon(b)
|
||||
SetTitle("Test title с кириллицей")
|
||||
|
||||
bSomeBtn := AddMenuItem("Йа кнопко", "")
|
||||
bSomeBtn.Check()
|
||||
AddSeparator()
|
||||
bQuit := AddMenuItem("Quit", "Quit the whole app")
|
||||
go func() {
|
||||
<-bQuit.ClickedCh
|
||||
t.Log("Quit reqested")
|
||||
Quit()
|
||||
}()
|
||||
time.AfterFunc(1*time.Second, Quit)
|
||||
}
|
||||
|
||||
onExit := func() {
|
||||
t.Log("Exit success")
|
||||
}
|
||||
|
||||
Run(onReady, onExit)
|
||||
}
|
||||
1
src/systray/.gitignore → vendor/fyne.io/systray/.gitignore
generated
vendored
1
src/systray/.gitignore → vendor/fyne.io/systray/.gitignore
generated
vendored
@@ -10,3 +10,4 @@ dll/systray_unsigned.dll
|
||||
out.txt
|
||||
.vs
|
||||
on_exit*.txt
|
||||
.vscode
|
||||
0
src/systray/CHANGELOG.md → vendor/fyne.io/systray/CHANGELOG.md
generated
vendored
0
src/systray/CHANGELOG.md → vendor/fyne.io/systray/CHANGELOG.md
generated
vendored
0
src/systray/LICENSE → vendor/fyne.io/systray/LICENSE
generated
vendored
0
src/systray/LICENSE → vendor/fyne.io/systray/LICENSE
generated
vendored
0
src/systray/Makefile → vendor/fyne.io/systray/Makefile
generated
vendored
0
src/systray/Makefile → vendor/fyne.io/systray/Makefile
generated
vendored
147
vendor/fyne.io/systray/README.md
generated
vendored
Normal file
147
vendor/fyne.io/systray/README.md
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
# Systray
|
||||
|
||||
systray is a cross-platform Go library to place an icon and menu in the notification area.
|
||||
This repository is a fork of [getlantern/systray](https://github.com/getlantern/systray)
|
||||
removing the GTK dependency and support for legacy linux system tray.
|
||||
|
||||
## Features
|
||||
|
||||
* Supported on Windows, macOS, Linux and many BSD systems
|
||||
* Menu items can be checked and/or disabled
|
||||
* Methods may be called from any Goroutine
|
||||
|
||||
## API
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fyne.io/systray"
|
||||
import "fyne.io/systray/example/icon"
|
||||
|
||||
func main() {
|
||||
systray.Run(onReady, onExit)
|
||||
}
|
||||
|
||||
func onReady() {
|
||||
systray.SetIcon(icon.Data)
|
||||
systray.SetTitle("Awesome App")
|
||||
systray.SetTooltip("Pretty awesome超级棒")
|
||||
mQuit := systray.AddMenuItem("Quit", "Quit the whole app")
|
||||
|
||||
// Sets the icon of a menu item.
|
||||
mQuit.SetIcon(icon.Data)
|
||||
}
|
||||
|
||||
func onExit() {
|
||||
// clean up here
|
||||
}
|
||||
```
|
||||
|
||||
### Running in a Fyne app
|
||||
|
||||
This repository is designed to allow any toolkit to integrate system tray without any additional dependencies.
|
||||
It is maintained by the Fyne team, but if you are using Fyne there is an even easier to use API in the main repository that wraps this project.
|
||||
|
||||
In your app you can use a standard `fyne.Menu` structure and pass it to `SetSystemTrayMenu` when your app is a desktop app, as follows:
|
||||
|
||||
```go
|
||||
menu := fyne.NewMenu("MyApp",
|
||||
fyne.NewMenuItem("Show", func() {
|
||||
log.Println("Tapped show")
|
||||
}))
|
||||
|
||||
if desk, ok := myApp.(desktop.App); ok {
|
||||
desk.SetSystemTrayMenu(menu)
|
||||
}
|
||||
```
|
||||
|
||||
You can find out more in the toolkit documentation:
|
||||
[System Tray Menu](https://developer.fyne.io/explore/systray).
|
||||
|
||||
### Run in another toolkit
|
||||
|
||||
Most graphical toolkits will grab the main loop so the `Run` code above is not possible.
|
||||
For this reason there is another entry point `RunWithExternalLoop`.
|
||||
This function of the library returns a start and end function that should be called
|
||||
when the application has started and will end, to loop in appropriate features.
|
||||
|
||||
See [full API](https://pkg.go.dev/fyne.io/systray?tab=doc) as well as [CHANGELOG](https://github.com/fyne-io/systray/tree/master/CHANGELOG.md).
|
||||
|
||||
Note: this package requires cgo, so make sure you set `CGO_ENABLED=1` before building.
|
||||
|
||||
## Try the example app!
|
||||
|
||||
Have go v1.12+ or higher installed? Here's an example to get started on macOS or Linux:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/fyne-io/systray
|
||||
cd systray/example
|
||||
go run .
|
||||
```
|
||||
|
||||
On Windows, you should follow the instructions above, but use the followign run command:
|
||||
|
||||
```
|
||||
go run -ldflags "-H=windowsgui" .
|
||||
```
|
||||
|
||||
Now look for *Awesome App* in your menu bar!
|
||||
|
||||

|
||||
|
||||
## Platform notes
|
||||
|
||||
### Linux/BSD
|
||||
|
||||
This implementation uses DBus to communicate through the SystemNotifier/AppIndicator spec, older tray implementations may not load the icon.
|
||||
|
||||
If you are running an older desktop environment, or system tray provider, you may require a proxy app which can convert the new DBus calls to the old format.
|
||||
The recommended tool for Gnome based trays is [snixembed](https://git.sr.ht/~steef/snixembed), others are available.
|
||||
Search for "StatusNotifierItems XEmbedded" in your package manager.
|
||||
|
||||
### Windows
|
||||
|
||||
* To avoid opening a console at application startup, use "fyne package" for your app or manually use these compile flags:
|
||||
|
||||
```sh
|
||||
go build -ldflags -H=windowsgui
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
On macOS, you will need to create an application bundle to wrap the binary; simply use "fyne package" or add folders with the following minimal structure and assets:
|
||||
|
||||
```
|
||||
SystrayApp.app/
|
||||
Contents/
|
||||
Info.plist
|
||||
MacOS/
|
||||
go-executable
|
||||
Resources/
|
||||
SystrayApp.icns
|
||||
```
|
||||
|
||||
If bundling manually, you may want to add one or both of the following to your Info.plist:
|
||||
|
||||
```xml
|
||||
<!-- avoid having a blurry icon and text -->
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
|
||||
<!-- avoid showing the app on the Dock -->
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
```
|
||||
|
||||
Consult the [Official Apple Documentation here](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1).
|
||||
|
||||
On macOS, it's possible to set the underlying
|
||||
[`NSStatusItemBehavior`](https://developer.apple.com/documentation/appkit/nsstatusitembehavior?language=objc)
|
||||
with `systray.SetRemovalAllowed(true)`. When enabled, the user can cmd-drag the
|
||||
icon off the menu bar.
|
||||
|
||||
## Credits
|
||||
|
||||
- https://github.com/getlantern/systray
|
||||
- https://github.com/xilp/systray
|
||||
- https://github.com/cratonica/trayhost
|
||||
484
vendor/fyne.io/systray/internal/generated/menu/dbus_menu.go
generated
vendored
Normal file
484
vendor/fyne.io/systray/internal/generated/menu/dbus_menu.go
generated
vendored
Normal file
@@ -0,0 +1,484 @@
|
||||
// Code generated by dbus-codegen-go DO NOT EDIT.
|
||||
package menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/godbus/dbus/v5/introspect"
|
||||
)
|
||||
|
||||
var (
|
||||
// Introspection for com.canonical.dbusmenu
|
||||
IntrospectDataDbusmenu = introspect.Interface{
|
||||
Name: "com.canonical.dbusmenu",
|
||||
Methods: []introspect.Method{{Name: "GetLayout", Args: []introspect.Arg{
|
||||
{Name: "parentId", Type: "i", Direction: "in"},
|
||||
{Name: "recursionDepth", Type: "i", Direction: "in"},
|
||||
{Name: "propertyNames", Type: "as", Direction: "in"},
|
||||
{Name: "revision", Type: "u", Direction: "out"},
|
||||
{Name: "layout", Type: "(ia{sv}av)", Direction: "out"},
|
||||
}},
|
||||
{Name: "GetGroupProperties", Args: []introspect.Arg{
|
||||
{Name: "ids", Type: "ai", Direction: "in"},
|
||||
{Name: "propertyNames", Type: "as", Direction: "in"},
|
||||
{Name: "properties", Type: "a(ia{sv})", Direction: "out"},
|
||||
}},
|
||||
{Name: "GetProperty", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "in"},
|
||||
{Name: "name", Type: "s", Direction: "in"},
|
||||
{Name: "value", Type: "v", Direction: "out"},
|
||||
}},
|
||||
{Name: "Event", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "in"},
|
||||
{Name: "eventId", Type: "s", Direction: "in"},
|
||||
{Name: "data", Type: "v", Direction: "in"},
|
||||
{Name: "timestamp", Type: "u", Direction: "in"},
|
||||
}},
|
||||
{Name: "EventGroup", Args: []introspect.Arg{
|
||||
{Name: "events", Type: "a(isvu)", Direction: "in"},
|
||||
{Name: "idErrors", Type: "ai", Direction: "out"},
|
||||
}},
|
||||
{Name: "AboutToShow", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "in"},
|
||||
{Name: "needUpdate", Type: "b", Direction: "out"},
|
||||
}},
|
||||
{Name: "AboutToShowGroup", Args: []introspect.Arg{
|
||||
{Name: "ids", Type: "ai", Direction: "in"},
|
||||
{Name: "updatesNeeded", Type: "ai", Direction: "out"},
|
||||
{Name: "idErrors", Type: "ai", Direction: "out"},
|
||||
}},
|
||||
},
|
||||
Signals: []introspect.Signal{{Name: "ItemsPropertiesUpdated", Args: []introspect.Arg{
|
||||
{Name: "updatedProps", Type: "a(ia{sv})", Direction: "out"},
|
||||
{Name: "removedProps", Type: "a(ias)", Direction: "out"},
|
||||
}},
|
||||
{Name: "LayoutUpdated", Args: []introspect.Arg{
|
||||
{Name: "revision", Type: "u", Direction: "out"},
|
||||
{Name: "parent", Type: "i", Direction: "out"},
|
||||
}},
|
||||
{Name: "ItemActivationRequested", Args: []introspect.Arg{
|
||||
{Name: "id", Type: "i", Direction: "out"},
|
||||
{Name: "timestamp", Type: "u", Direction: "out"},
|
||||
}},
|
||||
},
|
||||
Properties: []introspect.Property{{Name: "Version", Type: "u", Access: "read"},
|
||||
{Name: "TextDirection", Type: "s", Access: "read"},
|
||||
{Name: "Status", Type: "s", Access: "read"},
|
||||
{Name: "IconThemePath", Type: "as", Access: "read"},
|
||||
},
|
||||
Annotations: []introspect.Annotation{},
|
||||
}
|
||||
)
|
||||
|
||||
// Signal is a common interface for all signals.
|
||||
type Signal interface {
|
||||
Name() string
|
||||
Interface() string
|
||||
Sender() string
|
||||
|
||||
path() dbus.ObjectPath
|
||||
values() []interface{}
|
||||
}
|
||||
|
||||
// Emit sends the given signal to the bus.
|
||||
func Emit(conn *dbus.Conn, s Signal) error {
|
||||
return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...)
|
||||
}
|
||||
|
||||
// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved.
|
||||
var ErrUnknownSignal = errors.New("unknown signal")
|
||||
|
||||
// LookupSignal converts the given raw D-Bus signal with variable body
|
||||
// into one with typed structured body or returns ErrUnknownSignal error.
|
||||
func LookupSignal(signal *dbus.Signal) (Signal, error) {
|
||||
switch signal.Name {
|
||||
case InterfaceDbusmenu + "." + "ItemsPropertiesUpdated":
|
||||
v0, ok := signal.Body[0].([]struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .UpdatedProps is %T, not []struct {V0 int32;V1 map[string]dbus.Variant}", signal.Body[0])
|
||||
}
|
||||
v1, ok := signal.Body[1].([]struct {
|
||||
V0 int32
|
||||
V1 []string
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .RemovedProps is %T, not []struct {V0 int32;V1 []string}", signal.Body[1])
|
||||
}
|
||||
return &Dbusmenu_ItemsPropertiesUpdatedSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &Dbusmenu_ItemsPropertiesUpdatedSignalBody{
|
||||
UpdatedProps: v0,
|
||||
RemovedProps: v1,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceDbusmenu + "." + "LayoutUpdated":
|
||||
v0, ok := signal.Body[0].(uint32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Revision is %T, not uint32", signal.Body[0])
|
||||
}
|
||||
v1, ok := signal.Body[1].(int32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Parent is %T, not int32", signal.Body[1])
|
||||
}
|
||||
return &Dbusmenu_LayoutUpdatedSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &Dbusmenu_LayoutUpdatedSignalBody{
|
||||
Revision: v0,
|
||||
Parent: v1,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceDbusmenu + "." + "ItemActivationRequested":
|
||||
v0, ok := signal.Body[0].(int32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Id is %T, not int32", signal.Body[0])
|
||||
}
|
||||
v1, ok := signal.Body[1].(uint32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Timestamp is %T, not uint32", signal.Body[1])
|
||||
}
|
||||
return &Dbusmenu_ItemActivationRequestedSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &Dbusmenu_ItemActivationRequestedSignalBody{
|
||||
Id: v0,
|
||||
Timestamp: v1,
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, ErrUnknownSignal
|
||||
}
|
||||
}
|
||||
|
||||
// AddMatchSignal registers a match rule for the given signal,
|
||||
// opts are appended to the automatically generated signal's rules.
|
||||
func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.AddMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// RemoveMatchSignal unregisters the previously registered subscription.
|
||||
func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.RemoveMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// Interface name constants.
|
||||
const (
|
||||
InterfaceDbusmenu = "com.canonical.dbusmenu"
|
||||
)
|
||||
|
||||
// Dbusmenuer is com.canonical.dbusmenu interface.
|
||||
type Dbusmenuer interface {
|
||||
// GetLayout is com.canonical.dbusmenu.GetLayout method.
|
||||
GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
V2 []dbus.Variant
|
||||
}, err *dbus.Error)
|
||||
// GetGroupProperties is com.canonical.dbusmenu.GetGroupProperties method.
|
||||
GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}, err *dbus.Error)
|
||||
// GetProperty is com.canonical.dbusmenu.GetProperty method.
|
||||
GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error)
|
||||
// Event is com.canonical.dbusmenu.Event method.
|
||||
Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error)
|
||||
// EventGroup is com.canonical.dbusmenu.EventGroup method.
|
||||
EventGroup(events []struct {
|
||||
V0 int32
|
||||
V1 string
|
||||
V2 dbus.Variant
|
||||
V3 uint32
|
||||
}) (idErrors []int32, err *dbus.Error)
|
||||
// AboutToShow is com.canonical.dbusmenu.AboutToShow method.
|
||||
AboutToShow(id int32) (needUpdate bool, err *dbus.Error)
|
||||
// AboutToShowGroup is com.canonical.dbusmenu.AboutToShowGroup method.
|
||||
AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error)
|
||||
}
|
||||
|
||||
// ExportDbusmenu exports the given object that implements com.canonical.dbusmenu on the bus.
|
||||
func ExportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath, v Dbusmenuer) error {
|
||||
return conn.ExportSubtreeMethodTable(map[string]interface{}{
|
||||
"GetLayout": v.GetLayout,
|
||||
"GetGroupProperties": v.GetGroupProperties,
|
||||
"GetProperty": v.GetProperty,
|
||||
"Event": v.Event,
|
||||
"EventGroup": v.EventGroup,
|
||||
"AboutToShow": v.AboutToShow,
|
||||
"AboutToShowGroup": v.AboutToShowGroup,
|
||||
}, path, InterfaceDbusmenu)
|
||||
}
|
||||
|
||||
// UnexportDbusmenu unexports com.canonical.dbusmenu interface on the named path.
|
||||
func UnexportDbusmenu(conn *dbus.Conn, path dbus.ObjectPath) error {
|
||||
return conn.Export(nil, path, InterfaceDbusmenu)
|
||||
}
|
||||
|
||||
// UnimplementedDbusmenu can be embedded to have forward compatible server implementations.
|
||||
type UnimplementedDbusmenu struct{}
|
||||
|
||||
func (*UnimplementedDbusmenu) iface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) GetLayout(parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
V2 []dbus.Variant
|
||||
}, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) Event(id int32, eventId string, data dbus.Variant, timestamp uint32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) EventGroup(events []struct {
|
||||
V0 int32
|
||||
V1 string
|
||||
V2 dbus.Variant
|
||||
V3 uint32
|
||||
}) (idErrors []int32, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedDbusmenu) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
// NewDbusmenu creates and allocates com.canonical.dbusmenu.
|
||||
func NewDbusmenu(object dbus.BusObject) *Dbusmenu {
|
||||
return &Dbusmenu{object}
|
||||
}
|
||||
|
||||
// Dbusmenu implements com.canonical.dbusmenu D-Bus interface.
|
||||
type Dbusmenu struct {
|
||||
object dbus.BusObject
|
||||
}
|
||||
|
||||
// GetLayout calls com.canonical.dbusmenu.GetLayout method.
|
||||
func (o *Dbusmenu) GetLayout(ctx context.Context, parentId int32, recursionDepth int32, propertyNames []string) (revision uint32, layout struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
V2 []dbus.Variant
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetLayout", 0, parentId, recursionDepth, propertyNames).Store(&revision, &layout)
|
||||
return
|
||||
}
|
||||
|
||||
// GetGroupProperties calls com.canonical.dbusmenu.GetGroupProperties method.
|
||||
func (o *Dbusmenu) GetGroupProperties(ctx context.Context, ids []int32, propertyNames []string) (properties []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetGroupProperties", 0, ids, propertyNames).Store(&properties)
|
||||
return
|
||||
}
|
||||
|
||||
// GetProperty calls com.canonical.dbusmenu.GetProperty method.
|
||||
func (o *Dbusmenu) GetProperty(ctx context.Context, id int32, name string) (value dbus.Variant, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".GetProperty", 0, id, name).Store(&value)
|
||||
return
|
||||
}
|
||||
|
||||
// Event calls com.canonical.dbusmenu.Event method.
|
||||
func (o *Dbusmenu) Event(ctx context.Context, id int32, eventId string, data dbus.Variant, timestamp uint32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".Event", 0, id, eventId, data, timestamp).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// EventGroup calls com.canonical.dbusmenu.EventGroup method.
|
||||
func (o *Dbusmenu) EventGroup(ctx context.Context, events []struct {
|
||||
V0 int32
|
||||
V1 string
|
||||
V2 dbus.Variant
|
||||
V3 uint32
|
||||
}) (idErrors []int32, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".EventGroup", 0, events).Store(&idErrors)
|
||||
return
|
||||
}
|
||||
|
||||
// AboutToShow calls com.canonical.dbusmenu.AboutToShow method.
|
||||
func (o *Dbusmenu) AboutToShow(ctx context.Context, id int32) (needUpdate bool, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShow", 0, id).Store(&needUpdate)
|
||||
return
|
||||
}
|
||||
|
||||
// AboutToShowGroup calls com.canonical.dbusmenu.AboutToShowGroup method.
|
||||
func (o *Dbusmenu) AboutToShowGroup(ctx context.Context, ids []int32) (updatesNeeded []int32, idErrors []int32, err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceDbusmenu+".AboutToShowGroup", 0, ids).Store(&updatesNeeded, &idErrors)
|
||||
return
|
||||
}
|
||||
|
||||
// GetVersion gets com.canonical.dbusmenu.Version property.
|
||||
func (o *Dbusmenu) GetVersion(ctx context.Context) (version uint32, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Version").Store(&version)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTextDirection gets com.canonical.dbusmenu.TextDirection property.
|
||||
func (o *Dbusmenu) GetTextDirection(ctx context.Context) (textDirection string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "TextDirection").Store(&textDirection)
|
||||
return
|
||||
}
|
||||
|
||||
// GetStatus gets com.canonical.dbusmenu.Status property.
|
||||
func (o *Dbusmenu) GetStatus(ctx context.Context) (status string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "Status").Store(&status)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconThemePath gets com.canonical.dbusmenu.IconThemePath property.
|
||||
func (o *Dbusmenu) GetIconThemePath(ctx context.Context) (iconThemePath []string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceDbusmenu, "IconThemePath").Store(&iconThemePath)
|
||||
return
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemsPropertiesUpdatedSignal represents com.canonical.dbusmenu.ItemsPropertiesUpdated signal.
|
||||
type Dbusmenu_ItemsPropertiesUpdatedSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *Dbusmenu_ItemsPropertiesUpdatedSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Name() string {
|
||||
return "ItemsPropertiesUpdated"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Interface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemsPropertiesUpdatedSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.UpdatedProps, s.Body.RemovedProps}
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemsPropertiesUpdatedSignalBody is body container.
|
||||
type Dbusmenu_ItemsPropertiesUpdatedSignalBody struct {
|
||||
UpdatedProps []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}
|
||||
RemovedProps []struct {
|
||||
V0 int32
|
||||
V1 []string
|
||||
}
|
||||
}
|
||||
|
||||
// Dbusmenu_LayoutUpdatedSignal represents com.canonical.dbusmenu.LayoutUpdated signal.
|
||||
type Dbusmenu_LayoutUpdatedSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *Dbusmenu_LayoutUpdatedSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) Name() string {
|
||||
return "LayoutUpdated"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) Interface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_LayoutUpdatedSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.Revision, s.Body.Parent}
|
||||
}
|
||||
|
||||
// Dbusmenu_LayoutUpdatedSignalBody is body container.
|
||||
type Dbusmenu_LayoutUpdatedSignalBody struct {
|
||||
Revision uint32
|
||||
Parent int32
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemActivationRequestedSignal represents com.canonical.dbusmenu.ItemActivationRequested signal.
|
||||
type Dbusmenu_ItemActivationRequestedSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *Dbusmenu_ItemActivationRequestedSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) Name() string {
|
||||
return "ItemActivationRequested"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) Interface() string {
|
||||
return InterfaceDbusmenu
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *Dbusmenu_ItemActivationRequestedSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.Id, s.Body.Timestamp}
|
||||
}
|
||||
|
||||
// Dbusmenu_ItemActivationRequestedSignalBody is body container.
|
||||
type Dbusmenu_ItemActivationRequestedSignalBody struct {
|
||||
Id int32
|
||||
Timestamp uint32
|
||||
}
|
||||
633
vendor/fyne.io/systray/internal/generated/notifier/status_notifier_item.go
generated
vendored
Normal file
633
vendor/fyne.io/systray/internal/generated/notifier/status_notifier_item.go
generated
vendored
Normal file
@@ -0,0 +1,633 @@
|
||||
// Code generated by dbus-codegen-go DO NOT EDIT.
|
||||
package notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/godbus/dbus/v5/introspect"
|
||||
)
|
||||
|
||||
var (
|
||||
// Introspection for org.kde.StatusNotifierItem
|
||||
IntrospectDataStatusNotifierItem = introspect.Interface{
|
||||
Name: "org.kde.StatusNotifierItem",
|
||||
Methods: []introspect.Method{{Name: "ContextMenu", Args: []introspect.Arg{
|
||||
{Name: "x", Type: "i", Direction: "in"},
|
||||
{Name: "y", Type: "i", Direction: "in"},
|
||||
}},
|
||||
{Name: "Activate", Args: []introspect.Arg{
|
||||
{Name: "x", Type: "i", Direction: "in"},
|
||||
{Name: "y", Type: "i", Direction: "in"},
|
||||
}},
|
||||
{Name: "SecondaryActivate", Args: []introspect.Arg{
|
||||
{Name: "x", Type: "i", Direction: "in"},
|
||||
{Name: "y", Type: "i", Direction: "in"},
|
||||
}},
|
||||
{Name: "Scroll", Args: []introspect.Arg{
|
||||
{Name: "delta", Type: "i", Direction: "in"},
|
||||
{Name: "orientation", Type: "s", Direction: "in"},
|
||||
}},
|
||||
},
|
||||
Signals: []introspect.Signal{{Name: "NewTitle"},
|
||||
{Name: "NewIcon"},
|
||||
{Name: "NewAttentionIcon"},
|
||||
{Name: "NewOverlayIcon"},
|
||||
{Name: "NewStatus", Args: []introspect.Arg{
|
||||
{Name: "status", Type: "s", Direction: ""},
|
||||
}},
|
||||
{Name: "NewIconThemePath", Args: []introspect.Arg{
|
||||
{Name: "icon_theme_path", Type: "s", Direction: "out"},
|
||||
}},
|
||||
{Name: "NewMenu"},
|
||||
},
|
||||
Properties: []introspect.Property{{Name: "Category", Type: "s", Access: "read"},
|
||||
{Name: "Id", Type: "s", Access: "read"},
|
||||
{Name: "Title", Type: "s", Access: "read"},
|
||||
{Name: "Status", Type: "s", Access: "read"},
|
||||
{Name: "WindowId", Type: "i", Access: "read"},
|
||||
{Name: "IconThemePath", Type: "s", Access: "read"},
|
||||
{Name: "Menu", Type: "o", Access: "read"},
|
||||
{Name: "ItemIsMenu", Type: "b", Access: "read"},
|
||||
{Name: "IconName", Type: "s", Access: "read"},
|
||||
{Name: "IconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
|
||||
}},
|
||||
{Name: "OverlayIconName", Type: "s", Access: "read"},
|
||||
{Name: "OverlayIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
|
||||
}},
|
||||
{Name: "AttentionIconName", Type: "s", Access: "read"},
|
||||
{Name: "AttentionIconPixmap", Type: "a(iiay)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusImageVector"},
|
||||
}},
|
||||
{Name: "AttentionMovieName", Type: "s", Access: "read"},
|
||||
{Name: "ToolTip", Type: "(sa(iiay)ss)", Access: "read", Annotations: []introspect.Annotation{
|
||||
{Name: "org.qtproject.QtDBus.QtTypeName", Value: "KDbusToolTipStruct"},
|
||||
}},
|
||||
},
|
||||
Annotations: []introspect.Annotation{},
|
||||
}
|
||||
)
|
||||
|
||||
// Signal is a common interface for all signals.
|
||||
type Signal interface {
|
||||
Name() string
|
||||
Interface() string
|
||||
Sender() string
|
||||
|
||||
path() dbus.ObjectPath
|
||||
values() []interface{}
|
||||
}
|
||||
|
||||
// Emit sends the given signal to the bus.
|
||||
func Emit(conn *dbus.Conn, s Signal) error {
|
||||
return conn.Emit(s.path(), s.Interface()+"."+s.Name(), s.values()...)
|
||||
}
|
||||
|
||||
// ErrUnknownSignal is returned by LookupSignal when a signal cannot be resolved.
|
||||
var ErrUnknownSignal = errors.New("unknown signal")
|
||||
|
||||
// LookupSignal converts the given raw D-Bus signal with variable body
|
||||
// into one with typed structured body or returns ErrUnknownSignal error.
|
||||
func LookupSignal(signal *dbus.Signal) (Signal, error) {
|
||||
switch signal.Name {
|
||||
case InterfaceStatusNotifierItem + "." + "NewTitle":
|
||||
return &StatusNotifierItem_NewTitleSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewTitleSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewIcon":
|
||||
return &StatusNotifierItem_NewIconSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewIconSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewAttentionIcon":
|
||||
return &StatusNotifierItem_NewAttentionIconSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewAttentionIconSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewOverlayIcon":
|
||||
return &StatusNotifierItem_NewOverlayIconSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewOverlayIconSignalBody{},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewStatus":
|
||||
v0, ok := signal.Body[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .Status is %T, not string", signal.Body[0])
|
||||
}
|
||||
return &StatusNotifierItem_NewStatusSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewStatusSignalBody{
|
||||
Status: v0,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewIconThemePath":
|
||||
v0, ok := signal.Body[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prop .IconThemePath is %T, not string", signal.Body[0])
|
||||
}
|
||||
return &StatusNotifierItem_NewIconThemePathSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewIconThemePathSignalBody{
|
||||
IconThemePath: v0,
|
||||
},
|
||||
}, nil
|
||||
case InterfaceStatusNotifierItem + "." + "NewMenu":
|
||||
return &StatusNotifierItem_NewMenuSignal{
|
||||
sender: signal.Sender,
|
||||
Path: signal.Path,
|
||||
Body: &StatusNotifierItem_NewMenuSignalBody{},
|
||||
}, nil
|
||||
default:
|
||||
return nil, ErrUnknownSignal
|
||||
}
|
||||
}
|
||||
|
||||
// AddMatchSignal registers a match rule for the given signal,
|
||||
// opts are appended to the automatically generated signal's rules.
|
||||
func AddMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.AddMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// RemoveMatchSignal unregisters the previously registered subscription.
|
||||
func RemoveMatchSignal(conn *dbus.Conn, s Signal, opts ...dbus.MatchOption) error {
|
||||
return conn.RemoveMatchSignal(append([]dbus.MatchOption{
|
||||
dbus.WithMatchInterface(s.Interface()),
|
||||
dbus.WithMatchMember(s.Name()),
|
||||
}, opts...)...)
|
||||
}
|
||||
|
||||
// Interface name constants.
|
||||
const (
|
||||
InterfaceStatusNotifierItem = "org.kde.StatusNotifierItem"
|
||||
)
|
||||
|
||||
// StatusNotifierItemer is org.kde.StatusNotifierItem interface.
|
||||
type StatusNotifierItemer interface {
|
||||
// ContextMenu is org.kde.StatusNotifierItem.ContextMenu method.
|
||||
ContextMenu(x int32, y int32) (err *dbus.Error)
|
||||
// Activate is org.kde.StatusNotifierItem.Activate method.
|
||||
Activate(x int32, y int32) (err *dbus.Error)
|
||||
// SecondaryActivate is org.kde.StatusNotifierItem.SecondaryActivate method.
|
||||
SecondaryActivate(x int32, y int32) (err *dbus.Error)
|
||||
// Scroll is org.kde.StatusNotifierItem.Scroll method.
|
||||
Scroll(delta int32, orientation string) (err *dbus.Error)
|
||||
}
|
||||
|
||||
// ExportStatusNotifierItem exports the given object that implements org.kde.StatusNotifierItem on the bus.
|
||||
func ExportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath, v StatusNotifierItemer) error {
|
||||
return conn.ExportSubtreeMethodTable(map[string]interface{}{
|
||||
"ContextMenu": v.ContextMenu,
|
||||
"Activate": v.Activate,
|
||||
"SecondaryActivate": v.SecondaryActivate,
|
||||
"Scroll": v.Scroll,
|
||||
}, path, InterfaceStatusNotifierItem)
|
||||
}
|
||||
|
||||
// UnexportStatusNotifierItem unexports org.kde.StatusNotifierItem interface on the named path.
|
||||
func UnexportStatusNotifierItem(conn *dbus.Conn, path dbus.ObjectPath) error {
|
||||
return conn.Export(nil, path, InterfaceStatusNotifierItem)
|
||||
}
|
||||
|
||||
// UnimplementedStatusNotifierItem can be embedded to have forward compatible server implementations.
|
||||
type UnimplementedStatusNotifierItem struct{}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) iface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) ContextMenu(x int32, y int32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) Activate(x int32, y int32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) SecondaryActivate(x int32, y int32) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
func (*UnimplementedStatusNotifierItem) Scroll(delta int32, orientation string) (err *dbus.Error) {
|
||||
err = &dbus.ErrMsgUnknownMethod
|
||||
return
|
||||
}
|
||||
|
||||
// NewStatusNotifierItem creates and allocates org.kde.StatusNotifierItem.
|
||||
func NewStatusNotifierItem(object dbus.BusObject) *StatusNotifierItem {
|
||||
return &StatusNotifierItem{object}
|
||||
}
|
||||
|
||||
// StatusNotifierItem implements org.kde.StatusNotifierItem D-Bus interface.
|
||||
type StatusNotifierItem struct {
|
||||
object dbus.BusObject
|
||||
}
|
||||
|
||||
// ContextMenu calls org.kde.StatusNotifierItem.ContextMenu method.
|
||||
func (o *StatusNotifierItem) ContextMenu(ctx context.Context, x int32, y int32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".ContextMenu", 0, x, y).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// Activate calls org.kde.StatusNotifierItem.Activate method.
|
||||
func (o *StatusNotifierItem) Activate(ctx context.Context, x int32, y int32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Activate", 0, x, y).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// SecondaryActivate calls org.kde.StatusNotifierItem.SecondaryActivate method.
|
||||
func (o *StatusNotifierItem) SecondaryActivate(ctx context.Context, x int32, y int32) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".SecondaryActivate", 0, x, y).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// Scroll calls org.kde.StatusNotifierItem.Scroll method.
|
||||
func (o *StatusNotifierItem) Scroll(ctx context.Context, delta int32, orientation string) (err error) {
|
||||
err = o.object.CallWithContext(ctx, InterfaceStatusNotifierItem+".Scroll", 0, delta, orientation).Store()
|
||||
return
|
||||
}
|
||||
|
||||
// GetCategory gets org.kde.StatusNotifierItem.Category property.
|
||||
func (o *StatusNotifierItem) GetCategory(ctx context.Context) (category string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Category").Store(&category)
|
||||
return
|
||||
}
|
||||
|
||||
// GetId gets org.kde.StatusNotifierItem.Id property.
|
||||
func (o *StatusNotifierItem) GetId(ctx context.Context) (id string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Id").Store(&id)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTitle gets org.kde.StatusNotifierItem.Title property.
|
||||
func (o *StatusNotifierItem) GetTitle(ctx context.Context) (title string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Title").Store(&title)
|
||||
return
|
||||
}
|
||||
|
||||
// GetStatus gets org.kde.StatusNotifierItem.Status property.
|
||||
func (o *StatusNotifierItem) GetStatus(ctx context.Context) (status string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Status").Store(&status)
|
||||
return
|
||||
}
|
||||
|
||||
// GetWindowId gets org.kde.StatusNotifierItem.WindowId property.
|
||||
func (o *StatusNotifierItem) GetWindowId(ctx context.Context) (windowId int32, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "WindowId").Store(&windowId)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconThemePath gets org.kde.StatusNotifierItem.IconThemePath property.
|
||||
func (o *StatusNotifierItem) GetIconThemePath(ctx context.Context) (iconThemePath string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconThemePath").Store(&iconThemePath)
|
||||
return
|
||||
}
|
||||
|
||||
// GetMenu gets org.kde.StatusNotifierItem.Menu property.
|
||||
func (o *StatusNotifierItem) GetMenu(ctx context.Context) (menu dbus.ObjectPath, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "Menu").Store(&menu)
|
||||
return
|
||||
}
|
||||
|
||||
// GetItemIsMenu gets org.kde.StatusNotifierItem.ItemIsMenu property.
|
||||
func (o *StatusNotifierItem) GetItemIsMenu(ctx context.Context) (itemIsMenu bool, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ItemIsMenu").Store(&itemIsMenu)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconName gets org.kde.StatusNotifierItem.IconName property.
|
||||
func (o *StatusNotifierItem) GetIconName(ctx context.Context) (iconName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconName").Store(&iconName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetIconPixmap gets org.kde.StatusNotifierItem.IconPixmap property.
|
||||
//
|
||||
// Annotations:
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
|
||||
func (o *StatusNotifierItem) GetIconPixmap(ctx context.Context) (iconPixmap []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "IconPixmap").Store(&iconPixmap)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOverlayIconName gets org.kde.StatusNotifierItem.OverlayIconName property.
|
||||
func (o *StatusNotifierItem) GetOverlayIconName(ctx context.Context) (overlayIconName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconName").Store(&overlayIconName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetOverlayIconPixmap gets org.kde.StatusNotifierItem.OverlayIconPixmap property.
|
||||
//
|
||||
// Annotations:
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
|
||||
func (o *StatusNotifierItem) GetOverlayIconPixmap(ctx context.Context) (overlayIconPixmap []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "OverlayIconPixmap").Store(&overlayIconPixmap)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAttentionIconName gets org.kde.StatusNotifierItem.AttentionIconName property.
|
||||
func (o *StatusNotifierItem) GetAttentionIconName(ctx context.Context) (attentionIconName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconName").Store(&attentionIconName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAttentionIconPixmap gets org.kde.StatusNotifierItem.AttentionIconPixmap property.
|
||||
//
|
||||
// Annotations:
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusImageVector
|
||||
func (o *StatusNotifierItem) GetAttentionIconPixmap(ctx context.Context) (attentionIconPixmap []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionIconPixmap").Store(&attentionIconPixmap)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAttentionMovieName gets org.kde.StatusNotifierItem.AttentionMovieName property.
|
||||
func (o *StatusNotifierItem) GetAttentionMovieName(ctx context.Context) (attentionMovieName string, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "AttentionMovieName").Store(&attentionMovieName)
|
||||
return
|
||||
}
|
||||
|
||||
// GetToolTip gets org.kde.StatusNotifierItem.ToolTip property.
|
||||
//
|
||||
// Annotations:
|
||||
// @org.qtproject.QtDBus.QtTypeName = KDbusToolTipStruct
|
||||
func (o *StatusNotifierItem) GetToolTip(ctx context.Context) (toolTip struct {
|
||||
V0 string
|
||||
V1 []struct {
|
||||
V0 int32
|
||||
V1 int32
|
||||
V2 []byte
|
||||
}
|
||||
V2 string
|
||||
V3 string
|
||||
}, err error) {
|
||||
err = o.object.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, InterfaceStatusNotifierItem, "ToolTip").Store(&toolTip)
|
||||
return
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewTitleSignal represents org.kde.StatusNotifierItem.NewTitle signal.
|
||||
type StatusNotifierItem_NewTitleSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewTitleSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewTitleSignal) Name() string {
|
||||
return "NewTitle"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewTitleSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewTitleSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewTitleSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewTitleSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewTitleSignalBody is body container.
|
||||
type StatusNotifierItem_NewTitleSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconSignal represents org.kde.StatusNotifierItem.NewIcon signal.
|
||||
type StatusNotifierItem_NewIconSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewIconSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewIconSignal) Name() string {
|
||||
return "NewIcon"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewIconSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewIconSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconSignalBody is body container.
|
||||
type StatusNotifierItem_NewIconSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewAttentionIconSignal represents org.kde.StatusNotifierItem.NewAttentionIcon signal.
|
||||
type StatusNotifierItem_NewAttentionIconSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewAttentionIconSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) Name() string {
|
||||
return "NewAttentionIcon"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewAttentionIconSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewAttentionIconSignalBody is body container.
|
||||
type StatusNotifierItem_NewAttentionIconSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewOverlayIconSignal represents org.kde.StatusNotifierItem.NewOverlayIcon signal.
|
||||
type StatusNotifierItem_NewOverlayIconSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewOverlayIconSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) Name() string {
|
||||
return "NewOverlayIcon"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewOverlayIconSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewOverlayIconSignalBody is body container.
|
||||
type StatusNotifierItem_NewOverlayIconSignalBody struct {
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewStatusSignal represents org.kde.StatusNotifierItem.NewStatus signal.
|
||||
type StatusNotifierItem_NewStatusSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewStatusSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewStatusSignal) Name() string {
|
||||
return "NewStatus"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewStatusSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewStatusSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewStatusSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewStatusSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.Status}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewStatusSignalBody is body container.
|
||||
type StatusNotifierItem_NewStatusSignalBody struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconThemePathSignal represents org.kde.StatusNotifierItem.NewIconThemePath signal.
|
||||
type StatusNotifierItem_NewIconThemePathSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewIconThemePathSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) Name() string {
|
||||
return "NewIconThemePath"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewIconThemePathSignal) values() []interface{} {
|
||||
return []interface{}{s.Body.IconThemePath}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewIconThemePathSignalBody is body container.
|
||||
type StatusNotifierItem_NewIconThemePathSignalBody struct {
|
||||
IconThemePath string
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewMenuSignal represents org.kde.StatusNotifierItem.NewMenu signal.
|
||||
type StatusNotifierItem_NewMenuSignal struct {
|
||||
sender string
|
||||
Path dbus.ObjectPath
|
||||
Body *StatusNotifierItem_NewMenuSignalBody
|
||||
}
|
||||
|
||||
// Name returns the signal's name.
|
||||
func (s *StatusNotifierItem_NewMenuSignal) Name() string {
|
||||
return "NewMenu"
|
||||
}
|
||||
|
||||
// Interface returns the signal's interface.
|
||||
func (s *StatusNotifierItem_NewMenuSignal) Interface() string {
|
||||
return InterfaceStatusNotifierItem
|
||||
}
|
||||
|
||||
// Sender returns the signal's sender unique name.
|
||||
func (s *StatusNotifierItem_NewMenuSignal) Sender() string {
|
||||
return s.sender
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewMenuSignal) path() dbus.ObjectPath {
|
||||
return s.Path
|
||||
}
|
||||
|
||||
func (s *StatusNotifierItem_NewMenuSignal) values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// StatusNotifierItem_NewMenuSignalBody is body container.
|
||||
type StatusNotifierItem_NewMenuSignalBody struct {
|
||||
}
|
||||
103
src/systray/systray.go → vendor/fyne.io/systray/systray.go
generated
vendored
103
src/systray/systray.go → vendor/fyne.io/systray/systray.go
generated
vendored
@@ -1,9 +1,4 @@
|
||||
//go:build darwin || windows
|
||||
// +build darwin windows
|
||||
|
||||
/*
|
||||
Package systray is a cross-platform Go library to place an icon and menu in the notification area.
|
||||
*/
|
||||
// Package systray is a cross-platform Go library to place an icon and menu in the notification area.
|
||||
package systray
|
||||
|
||||
import (
|
||||
@@ -15,15 +10,29 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
systrayReady func()
|
||||
systrayExit func()
|
||||
systrayReady, systrayExit func()
|
||||
tappedLeft, tappedRight func()
|
||||
systrayExitCalled bool
|
||||
menuItems = make(map[uint32]*MenuItem)
|
||||
menuItemsLock sync.RWMutex
|
||||
|
||||
currentID = uint32(0)
|
||||
initialMenuBuilt sync.WaitGroup
|
||||
currentID atomic.Uint32
|
||||
quitOnce sync.Once
|
||||
|
||||
// TrayOpenedCh receives an entry each time the system tray menu is opened.
|
||||
TrayOpenedCh = make(chan struct{})
|
||||
)
|
||||
|
||||
// This helper function allows us to call systrayExit only once,
|
||||
// without accidentally calling it twice in the same lifetime.
|
||||
func runSystrayExit() {
|
||||
if !systrayExitCalled {
|
||||
systrayExitCalled = true
|
||||
systrayExit()
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
@@ -61,7 +70,7 @@ func (item *MenuItem) String() string {
|
||||
func newMenuItem(title string, tooltip string, parent *MenuItem) *MenuItem {
|
||||
return &MenuItem{
|
||||
ClickedCh: make(chan struct{}),
|
||||
id: atomic.AddUint32(¤tID, 1),
|
||||
id: currentID.Add(1),
|
||||
title: title,
|
||||
tooltip: tooltip,
|
||||
disabled: false,
|
||||
@@ -73,11 +82,24 @@ func newMenuItem(title string, tooltip string, parent *MenuItem) *MenuItem {
|
||||
|
||||
// Run initializes GUI and starts the event loop, then invokes the onReady
|
||||
// callback. It blocks until systray.Quit() is called.
|
||||
func Run(onReady func(), onExit func()) {
|
||||
func Run(onReady, onExit func()) {
|
||||
setInternalLoop(true)
|
||||
Register(onReady, onExit)
|
||||
|
||||
nativeLoop()
|
||||
}
|
||||
|
||||
// RunWithExternalLoop allows the system tray module to operate with other toolkits.
|
||||
// The returned start and end functions should be called by the toolkit when the application has started and will end.
|
||||
func RunWithExternalLoop(onReady, onExit func()) (start, end func()) {
|
||||
Register(onReady, onExit)
|
||||
|
||||
return nativeStart, func() {
|
||||
nativeEnd()
|
||||
Quit()
|
||||
}
|
||||
}
|
||||
|
||||
// Register initializes GUI and registers the callbacks but relies on the
|
||||
// caller to run the event loop somewhere else. It's useful if the program
|
||||
// needs to show other UI elements, for example, webview.
|
||||
@@ -89,9 +111,11 @@ func Register(onReady func(), onExit func()) {
|
||||
} else {
|
||||
// Run onReady on separate goroutine to avoid blocking event loop
|
||||
readyCh := make(chan interface{})
|
||||
initialMenuBuilt.Add(1)
|
||||
go func() {
|
||||
<-readyCh
|
||||
onReady()
|
||||
initialMenuBuilt.Done()
|
||||
}()
|
||||
systrayReady = func() {
|
||||
close(readyCh)
|
||||
@@ -103,14 +127,36 @@ func Register(onReady func(), onExit func()) {
|
||||
onExit = func() {}
|
||||
}
|
||||
systrayExit = onExit
|
||||
systrayExitCalled = false
|
||||
registerSystray()
|
||||
}
|
||||
|
||||
// ResetMenu will remove all menu items
|
||||
func ResetMenu() {
|
||||
menuItemsLock.Lock()
|
||||
id := currentID.Load()
|
||||
menuItemsLock.Unlock()
|
||||
for i, item := range menuItems {
|
||||
if i < id && item.parent == nil {
|
||||
item.Remove()
|
||||
}
|
||||
}
|
||||
resetMenu()
|
||||
}
|
||||
|
||||
// Quit the systray
|
||||
func Quit() {
|
||||
quitOnce.Do(quit)
|
||||
}
|
||||
|
||||
func SetOnTapped(f func()) {
|
||||
tappedLeft = f
|
||||
}
|
||||
|
||||
func SetOnSecondaryTapped(f func()) {
|
||||
tappedRight = f
|
||||
}
|
||||
|
||||
// AddMenuItem adds a menu item with the designated title and tooltip.
|
||||
// It can be safely invoked from different goroutines.
|
||||
// Created menu items are checkable on Windows and OSX by default. For Linux you have to use AddMenuItemCheckbox
|
||||
@@ -121,8 +167,8 @@ func AddMenuItem(title string, tooltip string) *MenuItem {
|
||||
}
|
||||
|
||||
// AddMenuItemCheckbox adds a menu item with the designated title and tooltip and a checkbox for Linux.
|
||||
// On other platforms there will be a check indicated next to the item if `checked` is true.
|
||||
// It can be safely invoked from different goroutines.
|
||||
// On Windows and OSX this is the same as calling AddMenuItem
|
||||
func AddMenuItemCheckbox(title string, tooltip string, checked bool) *MenuItem {
|
||||
item := newMenuItem(title, tooltip, nil)
|
||||
item.isCheckable = true
|
||||
@@ -133,7 +179,12 @@ func AddMenuItemCheckbox(title string, tooltip string, checked bool) *MenuItem {
|
||||
|
||||
// AddSeparator adds a separator bar to the menu
|
||||
func AddSeparator() {
|
||||
addSeparator(atomic.AddUint32(¤tID, 1))
|
||||
addSeparator(currentID.Add(1), 0)
|
||||
}
|
||||
|
||||
// AddSeparator adds a separator bar to the submenu
|
||||
func (item *MenuItem) AddSeparator() {
|
||||
addSeparator(currentID.Add(1), item.id)
|
||||
}
|
||||
|
||||
// AddSubMenuItem adds a nested sub-menu item with the designated title and tooltip.
|
||||
@@ -190,6 +241,30 @@ func (item *MenuItem) Hide() {
|
||||
hideMenuItem(item)
|
||||
}
|
||||
|
||||
// Remove removes a menu item
|
||||
func (item *MenuItem) Remove() {
|
||||
menuItemsLock.RLock()
|
||||
var childList []*MenuItem
|
||||
for _, child := range menuItems {
|
||||
if child.parent == item {
|
||||
childList = append(childList, child)
|
||||
}
|
||||
}
|
||||
menuItemsLock.RUnlock()
|
||||
for _, child := range childList {
|
||||
child.Remove()
|
||||
}
|
||||
removeMenuItem(item)
|
||||
menuItemsLock.Lock()
|
||||
delete(menuItems, item.id)
|
||||
select {
|
||||
case <-item.ClickedCh:
|
||||
default:
|
||||
}
|
||||
close(item.ClickedCh)
|
||||
menuItemsLock.Unlock()
|
||||
}
|
||||
|
||||
// Show shows a previously hidden menu item
|
||||
func (item *MenuItem) Show() {
|
||||
showMenuItem(item)
|
||||
@@ -225,7 +300,7 @@ func systrayMenuItemSelected(id uint32) {
|
||||
item, ok := menuItems[id]
|
||||
menuItemsLock.RUnlock()
|
||||
if !ok {
|
||||
log.Printf("No menu item with ID %v", id)
|
||||
log.Printf("systray error: no menu item with ID %d\n", id)
|
||||
return
|
||||
}
|
||||
select {
|
||||
11
src/systray/systray.h → vendor/fyne.io/systray/systray.h
generated
vendored
11
src/systray/systray.h → vendor/fyne.io/systray/systray.h
generated
vendored
@@ -2,16 +2,25 @@
|
||||
|
||||
extern void systray_ready();
|
||||
extern void systray_on_exit();
|
||||
extern void systray_left_click();
|
||||
extern void systray_right_click();
|
||||
extern void systray_menu_item_selected(int menu_id);
|
||||
extern void systray_menu_will_open();
|
||||
void registerSystray(void);
|
||||
void nativeEnd(void);
|
||||
int nativeLoop(void);
|
||||
void nativeStart(void);
|
||||
|
||||
void setIcon(const char* iconBytes, int length, bool template);
|
||||
void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template);
|
||||
void setTitle(char* title);
|
||||
void setTooltip(char* tooltip);
|
||||
void setRemovalAllowed(bool allowed);
|
||||
void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable);
|
||||
void add_separator(int menuId);
|
||||
void add_separator(int menuId, int parentId);
|
||||
void hide_menu_item(int menuId);
|
||||
void remove_menu_item(int menuId);
|
||||
void show_menu_item(int menuId);
|
||||
void reset_menu();
|
||||
void show_menu();
|
||||
void quit();
|
||||
213
vendor/fyne.io/systray/systray_darwin.go
generated
vendored
Normal file
213
vendor/fyne.io/systray/systray_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
//go:build !ios
|
||||
|
||||
package systray
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DDARWIN -x objective-c -fobjc-arc
|
||||
#cgo darwin LDFLAGS: -framework Cocoa
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "systray.h"
|
||||
|
||||
void setInternalLoop(bool);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// SetTemplateIcon sets the systray icon as a template icon (on Mac), falling back
|
||||
// to a regular icon on other platforms.
|
||||
// templateIconBytes and regularIconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
|
||||
C.setIcon(cstr, (C.int)(len(templateIconBytes)), true)
|
||||
}
|
||||
|
||||
// SetIcon sets the icon of a menu item. Only works on macOS and Windows.
|
||||
// iconBytes should be the content of .ico/.jpg/.png
|
||||
func (item *MenuItem) SetIcon(iconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
|
||||
C.setMenuItemIcon(cstr, (C.int)(len(iconBytes)), C.int(item.id), false)
|
||||
}
|
||||
|
||||
// SetIconFromFilePath sets the icon of a menu item from a file path.
|
||||
// iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
|
||||
func (item *MenuItem) SetIconFromFilePath(iconFilePath string) error {
|
||||
iconBytes, err := os.ReadFile(iconFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read icon file: %v", err)
|
||||
}
|
||||
item.SetIcon(iconBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows, it
|
||||
// falls back to the regular icon bytes and on Linux it does nothing.
|
||||
// templateIconBytes and regularIconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&templateIconBytes[0]))
|
||||
C.setMenuItemIcon(cstr, (C.int)(len(templateIconBytes)), C.int(item.id), true)
|
||||
}
|
||||
|
||||
// SetRemovalAllowed sets whether a user can remove the systray icon or not.
|
||||
// This is only supported on macOS.
|
||||
func SetRemovalAllowed(allowed bool) {
|
||||
C.setRemovalAllowed((C.bool)(allowed))
|
||||
}
|
||||
|
||||
func registerSystray() {
|
||||
C.registerSystray()
|
||||
}
|
||||
|
||||
func nativeLoop() {
|
||||
C.nativeLoop()
|
||||
}
|
||||
|
||||
func nativeEnd() {
|
||||
C.nativeEnd()
|
||||
}
|
||||
|
||||
func nativeStart() {
|
||||
C.nativeStart()
|
||||
}
|
||||
|
||||
func quit() {
|
||||
C.quit()
|
||||
}
|
||||
|
||||
func setInternalLoop(internal bool) {
|
||||
C.setInternalLoop(C.bool(internal))
|
||||
}
|
||||
|
||||
// SetIcon sets the systray icon.
|
||||
// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
|
||||
// for other platforms.
|
||||
func SetIcon(iconBytes []byte) {
|
||||
cstr := (*C.char)(unsafe.Pointer(&iconBytes[0]))
|
||||
C.setIcon(cstr, (C.int)(len(iconBytes)), false)
|
||||
}
|
||||
|
||||
// SetIconFromFilePath sets the systray icon from a file path.
|
||||
// iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
|
||||
func SetIconFromFilePath(iconFilePath string) error {
|
||||
bytes, err := os.ReadFile(iconFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read icon file: %v", err)
|
||||
}
|
||||
SetIcon(bytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTitle sets the systray title, only available on Mac and Linux.
|
||||
func SetTitle(title string) {
|
||||
C.setTitle(C.CString(title))
|
||||
}
|
||||
|
||||
// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
|
||||
// only available on Mac and Windows.
|
||||
func SetTooltip(tooltip string) {
|
||||
C.setTooltip(C.CString(tooltip))
|
||||
}
|
||||
|
||||
func addOrUpdateMenuItem(item *MenuItem) {
|
||||
var disabled C.short
|
||||
if item.disabled {
|
||||
disabled = 1
|
||||
}
|
||||
var checked C.short
|
||||
if item.checked {
|
||||
checked = 1
|
||||
}
|
||||
var isCheckable C.short
|
||||
if item.isCheckable {
|
||||
isCheckable = 1
|
||||
}
|
||||
var parentID uint32 = 0
|
||||
if item.parent != nil {
|
||||
parentID = item.parent.id
|
||||
}
|
||||
C.add_or_update_menu_item(
|
||||
C.int(item.id),
|
||||
C.int(parentID),
|
||||
C.CString(item.title),
|
||||
C.CString(item.tooltip),
|
||||
disabled,
|
||||
checked,
|
||||
isCheckable,
|
||||
)
|
||||
}
|
||||
|
||||
func addSeparator(id uint32, parent uint32) {
|
||||
C.add_separator(C.int(id), C.int(parent))
|
||||
}
|
||||
|
||||
func hideMenuItem(item *MenuItem) {
|
||||
C.hide_menu_item(
|
||||
C.int(item.id),
|
||||
)
|
||||
}
|
||||
|
||||
func showMenuItem(item *MenuItem) {
|
||||
C.show_menu_item(
|
||||
C.int(item.id),
|
||||
)
|
||||
}
|
||||
|
||||
func removeMenuItem(item *MenuItem) {
|
||||
C.remove_menu_item(
|
||||
C.int(item.id),
|
||||
)
|
||||
}
|
||||
|
||||
func resetMenu() {
|
||||
C.reset_menu()
|
||||
}
|
||||
|
||||
//export systray_left_click
|
||||
func systray_left_click() {
|
||||
if fn := tappedLeft; fn != nil {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
C.show_menu()
|
||||
}
|
||||
|
||||
//export systray_right_click
|
||||
func systray_right_click() {
|
||||
if fn := tappedRight; fn != nil {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
C.show_menu()
|
||||
}
|
||||
|
||||
//export systray_ready
|
||||
func systray_ready() {
|
||||
systrayReady()
|
||||
}
|
||||
|
||||
//export systray_on_exit
|
||||
func systray_on_exit() {
|
||||
runSystrayExit()
|
||||
}
|
||||
|
||||
//export systray_menu_item_selected
|
||||
func systray_menu_item_selected(cID C.int) {
|
||||
systrayMenuItemSelected(uint32(cID))
|
||||
}
|
||||
|
||||
//export systray_menu_will_open
|
||||
func systray_menu_will_open() {
|
||||
select {
|
||||
case TrayOpenedCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
199
src/systray/systray_darwin.m → vendor/fyne.io/systray/systray_darwin.m
generated
vendored
199
src/systray/systray_darwin.m → vendor/fyne.io/systray/systray_darwin.m
generated
vendored
@@ -1,3 +1,5 @@
|
||||
//go:build !ios
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include "systray.h"
|
||||
|
||||
@@ -50,13 +52,33 @@ withParentMenuId: (int)theParentMenuId
|
||||
}
|
||||
@end
|
||||
|
||||
@interface AppDelegate: NSObject <NSApplicationDelegate>
|
||||
@interface RightClickDetector : NSView
|
||||
|
||||
@property (copy) void (^onRightClicked)(NSEvent *);
|
||||
|
||||
@end
|
||||
|
||||
@implementation RightClickDetector
|
||||
|
||||
- (void)rightMouseUp:(NSEvent *)theEvent {
|
||||
if (!self.onRightClicked) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.onRightClicked(theEvent);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface SystrayAppDelegate: NSObject <NSApplicationDelegate, NSMenuDelegate>
|
||||
- (void) add_or_update_menu_item:(MenuItem*) item;
|
||||
- (IBAction)menuHandler:(id)sender;
|
||||
- (void)menuWillOpen:(NSMenu*)menu;
|
||||
@property (assign) IBOutlet NSWindow *window;
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
@implementation SystrayAppDelegate
|
||||
{
|
||||
NSStatusItem *statusItem;
|
||||
NSMenu *menu;
|
||||
@@ -68,17 +90,73 @@ withParentMenuId: (int)theParentMenuId
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
self->statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
|
||||
|
||||
self->menu = [[NSMenu alloc] init];
|
||||
[self->menu setAutoenablesItems: FALSE];
|
||||
[self->statusItem setMenu:self->menu];
|
||||
self->menu.delegate = self;
|
||||
self->menu.autoenablesItems = FALSE;
|
||||
// Once the user has removed it, the item needs to be explicitly brought back,
|
||||
// even restarting the application is insufficient.
|
||||
// Since the interface from Go is relatively simple, for now we ensure it's
|
||||
// always visible at application startup.
|
||||
self->statusItem.visible = TRUE;
|
||||
|
||||
NSStatusBarButton *button = self->statusItem.button;
|
||||
button.action = @selector(leftMouseClicked);
|
||||
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask: (NSEventTypeLeftMouseDown|NSEventTypeRightMouseDown)
|
||||
handler: ^NSEvent *(NSEvent *event) {
|
||||
if (event.window != self->statusItem.button.window) {
|
||||
return event;
|
||||
}
|
||||
|
||||
[self leftMouseClicked];
|
||||
|
||||
return nil;
|
||||
}];
|
||||
|
||||
NSSize size = [button frame].size;
|
||||
NSRect frame = CGRectMake(0, 0, size.width, size.height);
|
||||
RightClickDetector *rightClicker = [[RightClickDetector alloc] initWithFrame:frame];
|
||||
rightClicker.onRightClicked = ^(NSEvent *event) {
|
||||
[self rightMouseClicked];
|
||||
};
|
||||
|
||||
rightClicker.autoresizingMask = (NSViewWidthSizable |
|
||||
NSViewHeightSizable);
|
||||
button.autoresizesSubviews = YES;
|
||||
[button addSubview:rightClicker];
|
||||
|
||||
systray_ready();
|
||||
}
|
||||
|
||||
- (void)rightMouseClicked {
|
||||
systray_right_click();
|
||||
}
|
||||
|
||||
- (void)leftMouseClicked {
|
||||
systray_left_click();
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
systray_on_exit();
|
||||
}
|
||||
|
||||
- (void)setRemovalAllowed {
|
||||
NSStatusItemBehavior behavior = [self->statusItem behavior];
|
||||
behavior |= NSStatusItemBehaviorRemovalAllowed;
|
||||
self->statusItem.behavior = behavior;
|
||||
}
|
||||
|
||||
- (void)setRemovalForbidden {
|
||||
NSStatusItemBehavior behavior = [self->statusItem behavior];
|
||||
behavior &= ~NSStatusItemBehaviorRemovalAllowed;
|
||||
// Ensure the menu item is visible if it was removed, since we're now
|
||||
// disallowing removal.
|
||||
self->statusItem.visible = TRUE;
|
||||
self->statusItem.behavior = behavior;
|
||||
}
|
||||
|
||||
- (void)setIcon:(NSImage *)image {
|
||||
statusItem.button.image = image;
|
||||
[self updateTitleButtonStyle];
|
||||
@@ -112,6 +190,10 @@ withParentMenuId: (int)theParentMenuId
|
||||
systray_menu_item_selected(menuId.intValue);
|
||||
}
|
||||
|
||||
- (void)menuWillOpen:(NSMenu *)menu {
|
||||
systray_menu_will_open();
|
||||
}
|
||||
|
||||
- (void)add_or_update_menu_item:(MenuItem *)item {
|
||||
NSMenu *theMenu = self->menu;
|
||||
NSMenuItem *parentItem;
|
||||
@@ -126,8 +208,7 @@ withParentMenuId: (int)theParentMenuId
|
||||
}
|
||||
}
|
||||
|
||||
NSMenuItem *menuItem;
|
||||
menuItem = find_menu_item(theMenu, item->menuId);
|
||||
NSMenuItem *menuItem = find_menu_item(theMenu, item->menuId);
|
||||
if (menuItem == NULL) {
|
||||
menuItem = [theMenu addItemWithTitle:item->title
|
||||
action:@selector(menuHandler:)
|
||||
@@ -170,8 +251,15 @@ NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) {
|
||||
return NULL;
|
||||
};
|
||||
|
||||
- (void) add_separator:(NSNumber*) menuId
|
||||
- (void) add_separator:(NSNumber*) parentMenuId
|
||||
{
|
||||
if (parentMenuId.integerValue != 0) {
|
||||
NSMenuItem* menuItem = find_menu_item(menu, parentMenuId);
|
||||
if (menuItem != NULL) {
|
||||
[menuItem.submenu addItem: [NSMenuItem separatorItem]];
|
||||
return;
|
||||
}
|
||||
}
|
||||
[menu addItem: [NSMenuItem separatorItem]];
|
||||
}
|
||||
|
||||
@@ -195,6 +283,13 @@ NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) {
|
||||
menuItem.image = image;
|
||||
}
|
||||
|
||||
- (void)show_menu
|
||||
{
|
||||
[self->menu popUpMenuPositioningItem:nil
|
||||
atLocation:NSMakePoint(0, self->statusItem.button.bounds.size.height+6)
|
||||
inView:self->statusItem.button];
|
||||
}
|
||||
|
||||
- (void) show_menu_item:(NSNumber*) menuId
|
||||
{
|
||||
NSMenuItem* menuItem = find_menu_item(menu, menuId);
|
||||
@@ -203,16 +298,55 @@ NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) {
|
||||
}
|
||||
}
|
||||
|
||||
- (void) remove_menu_item:(NSNumber*) menuId
|
||||
{
|
||||
NSMenuItem* menuItem = find_menu_item(menu, menuId);
|
||||
if (menuItem != NULL) {
|
||||
[menuItem.menu removeItem:menuItem];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) reset_menu
|
||||
{
|
||||
[self->menu removeAllItems];
|
||||
}
|
||||
|
||||
- (void) quit
|
||||
{
|
||||
[NSApp terminate:self];
|
||||
// This tells the app event loop to stop after processing remaining messages.
|
||||
[NSApp stop:self];
|
||||
// The event loop won't return until it processes another event.
|
||||
// https://stackoverflow.com/a/48064752/149482
|
||||
NSPoint eventLocation = NSMakePoint(0, 0);
|
||||
NSEvent *customEvent = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
|
||||
location:eventLocation
|
||||
modifierFlags:0
|
||||
timestamp:0
|
||||
windowNumber:0
|
||||
context:nil
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[NSApp postEvent:customEvent atStart:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
bool internalLoop = false;
|
||||
SystrayAppDelegate *owner;
|
||||
|
||||
void setInternalLoop(bool i) {
|
||||
internalLoop = i;
|
||||
}
|
||||
|
||||
void registerSystray(void) {
|
||||
AppDelegate *delegate = [[AppDelegate alloc] init];
|
||||
[[NSApplication sharedApplication] setDelegate:delegate];
|
||||
if (!internalLoop) { // with an external loop we don't take ownership of the app
|
||||
return;
|
||||
}
|
||||
|
||||
owner = [[SystrayAppDelegate alloc] init];
|
||||
[[NSApplication sharedApplication] setDelegate:owner];
|
||||
|
||||
// A workaround to avoid crashing on macOS versions before Catalina. Somehow
|
||||
// SIGSEGV would happen inside AppKit if [NSApp run] is called from a
|
||||
// different function, even if that function is called right after this.
|
||||
@@ -221,6 +355,10 @@ void registerSystray(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void nativeEnd(void) {
|
||||
systray_on_exit();
|
||||
}
|
||||
|
||||
int nativeLoop(void) {
|
||||
if (floor(NSAppKitVersionNumber) > /*NSAppKitVersionNumber10_14*/ 1671){
|
||||
[NSApp run];
|
||||
@@ -228,8 +366,16 @@ int nativeLoop(void) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void nativeStart(void) {
|
||||
owner = [[SystrayAppDelegate alloc] init];
|
||||
|
||||
NSNotification *launched = [NSNotification notificationWithName:NSApplicationDidFinishLaunchingNotification
|
||||
object:[NSApplication sharedApplication]];
|
||||
[owner applicationDidFinishLaunching:launched];
|
||||
}
|
||||
|
||||
void runInMainThread(SEL method, id object) {
|
||||
[(AppDelegate*)[NSApp delegate]
|
||||
[owner
|
||||
performSelectorOnMainThread:method
|
||||
withObject:object
|
||||
waitUntilDone: YES];
|
||||
@@ -237,20 +383,24 @@ void runInMainThread(SEL method, id object) {
|
||||
|
||||
void setIcon(const char* iconBytes, int length, bool template) {
|
||||
NSData* buffer = [NSData dataWithBytes: iconBytes length:length];
|
||||
@autoreleasepool {
|
||||
NSImage *image = [[NSImage alloc] initWithData:buffer];
|
||||
[image setSize:NSMakeSize(16, 16)];
|
||||
image.template = template;
|
||||
runInMainThread(@selector(setIcon:), (id)image);
|
||||
}
|
||||
}
|
||||
|
||||
void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template) {
|
||||
NSData* buffer = [NSData dataWithBytes: iconBytes length:length];
|
||||
@autoreleasepool {
|
||||
NSImage *image = [[NSImage alloc] initWithData:buffer];
|
||||
[image setSize:NSMakeSize(16, 16)];
|
||||
image.template = template;
|
||||
NSNumber *mId = [NSNumber numberWithInt:menuId];
|
||||
runInMainThread(@selector(setMenuItemIcon:), @[image, (id)mId]);
|
||||
}
|
||||
}
|
||||
|
||||
void setTitle(char* ctitle) {
|
||||
NSString* title = [[NSString alloc] initWithCString:ctitle
|
||||
@@ -266,6 +416,14 @@ void setTooltip(char* ctooltip) {
|
||||
runInMainThread(@selector(setTooltip:), (id)tooltip);
|
||||
}
|
||||
|
||||
void setRemovalAllowed(bool allowed) {
|
||||
if (allowed) {
|
||||
runInMainThread(@selector(setRemovalAllowed), nil);
|
||||
} else {
|
||||
runInMainThread(@selector(setRemovalForbidden), nil);
|
||||
}
|
||||
}
|
||||
|
||||
void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable) {
|
||||
MenuItem* item = [[MenuItem alloc] initWithId: menuId withParentMenuId: parentMenuId withTitle: title withTooltip: tooltip withDisabled: disabled withChecked: checked];
|
||||
free(title);
|
||||
@@ -273,9 +431,9 @@ void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* to
|
||||
runInMainThread(@selector(add_or_update_menu_item:), (id)item);
|
||||
}
|
||||
|
||||
void add_separator(int menuId) {
|
||||
NSNumber *mId = [NSNumber numberWithInt:menuId];
|
||||
runInMainThread(@selector(add_separator:), (id)mId);
|
||||
void add_separator(int menuId, int parentId) {
|
||||
NSNumber *pId = [NSNumber numberWithInt:parentId];
|
||||
runInMainThread(@selector(add_separator:), (id)pId);
|
||||
}
|
||||
|
||||
void hide_menu_item(int menuId) {
|
||||
@@ -283,11 +441,24 @@ void hide_menu_item(int menuId) {
|
||||
runInMainThread(@selector(hide_menu_item:), (id)mId);
|
||||
}
|
||||
|
||||
void remove_menu_item(int menuId) {
|
||||
NSNumber *mId = [NSNumber numberWithInt:menuId];
|
||||
runInMainThread(@selector(remove_menu_item:), (id)mId);
|
||||
}
|
||||
|
||||
void show_menu() {
|
||||
runInMainThread(@selector(show_menu), nil);
|
||||
}
|
||||
|
||||
void show_menu_item(int menuId) {
|
||||
NSNumber *mId = [NSNumber numberWithInt:menuId];
|
||||
runInMainThread(@selector(show_menu_item:), (id)mId);
|
||||
}
|
||||
|
||||
void reset_menu() {
|
||||
runInMainThread(@selector(reset_menu), nil);
|
||||
}
|
||||
|
||||
void quit() {
|
||||
runInMainThread(@selector(quit), nil);
|
||||
}
|
||||
370
vendor/fyne.io/systray/systray_menu_unix.go
generated
vendored
Normal file
370
vendor/fyne.io/systray/systray_menu_unix.go
generated
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
//go:build (linux || freebsd || openbsd || netbsd) && !android
|
||||
|
||||
package systray
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/godbus/dbus/v5/prop"
|
||||
|
||||
"fyne.io/systray/internal/generated/menu"
|
||||
)
|
||||
|
||||
// SetIcon sets the icon of a menu item.
|
||||
// iconBytes should be the content of .ico/.jpg/.png
|
||||
func (item *MenuItem) SetIcon(iconBytes []byte) {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
m, exists := findLayout(int32(item.id))
|
||||
if exists {
|
||||
m.V1["icon-data"] = dbus.MakeVariant(iconBytes)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// SetIconFromFilePath sets the icon of a menu item from a file path.
|
||||
// iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
|
||||
func (item *MenuItem) SetIconFromFilePath(iconFilePath string) error {
|
||||
iconBytes, err := os.ReadFile(iconFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read icon file: %v", err)
|
||||
}
|
||||
item.SetIcon(iconBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyLayout makes full copy of layout
|
||||
func copyLayout(in *menuLayout, depth int32) *menuLayout {
|
||||
out := menuLayout{
|
||||
V0: in.V0,
|
||||
V1: make(map[string]dbus.Variant, len(in.V1)),
|
||||
}
|
||||
for k, v := range in.V1 {
|
||||
out.V1[k] = v
|
||||
}
|
||||
if depth != 0 {
|
||||
depth--
|
||||
out.V2 = make([]dbus.Variant, len(in.V2))
|
||||
for i, v := range in.V2 {
|
||||
out.V2[i] = dbus.MakeVariant(copyLayout(v.Value().(*menuLayout), depth))
|
||||
}
|
||||
} else {
|
||||
out.V2 = []dbus.Variant{}
|
||||
}
|
||||
return &out
|
||||
}
|
||||
|
||||
// GetLayout is com.canonical.dbusmenu.GetLayout method.
|
||||
func (t *tray) GetLayout(parentID int32, recursionDepth int32, propertyNames []string) (revision uint32, layout menuLayout, err *dbus.Error) {
|
||||
initialMenuBuilt.Wait()
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
if m, ok := findLayout(parentID); ok {
|
||||
// return copy of menu layout to prevent panic from cuncurrent access to layout
|
||||
return instance.menuVersion, *copyLayout(m, recursionDepth), nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetGroupProperties is com.canonical.dbusmenu.GetGroupProperties method.
|
||||
func (t *tray) GetGroupProperties(ids []int32, propertyNames []string) (properties []struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}, err *dbus.Error) {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
for _, id := range ids {
|
||||
if m, ok := findLayout(id); ok {
|
||||
p := struct {
|
||||
V0 int32
|
||||
V1 map[string]dbus.Variant
|
||||
}{
|
||||
V0: m.V0,
|
||||
V1: make(map[string]dbus.Variant, len(m.V1)),
|
||||
}
|
||||
for k, v := range m.V1 {
|
||||
p.V1[k] = v
|
||||
}
|
||||
properties = append(properties, p)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetProperty is com.canonical.dbusmenu.GetProperty method.
|
||||
func (t *tray) GetProperty(id int32, name string) (value dbus.Variant, err *dbus.Error) {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
if m, ok := findLayout(id); ok {
|
||||
if p, ok := m.V1[name]; ok {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Event is com.canonical.dbusmenu.Event method.
|
||||
func (t *tray) Event(id int32, eventID string, data dbus.Variant, timestamp uint32) (err *dbus.Error) {
|
||||
switch eventID {
|
||||
case "clicked":
|
||||
systrayMenuItemSelected(uint32(id))
|
||||
case "opened":
|
||||
t.menuLock.RLock()
|
||||
rootMenuID := t.menu.V0
|
||||
t.menuLock.RUnlock()
|
||||
|
||||
if id == rootMenuID {
|
||||
select {
|
||||
case TrayOpenedCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EventGroup is com.canonical.dbusmenu.EventGroup method.
|
||||
func (t *tray) EventGroup(events []struct {
|
||||
V0 int32
|
||||
V1 string
|
||||
V2 dbus.Variant
|
||||
V3 uint32
|
||||
}) (idErrors []int32, err *dbus.Error) {
|
||||
for _, event := range events {
|
||||
if event.V1 == "clicked" {
|
||||
systrayMenuItemSelected(uint32(event.V0))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AboutToShow is com.canonical.dbusmenu.AboutToShow method.
|
||||
func (t *tray) AboutToShow(id int32) (needUpdate bool, err *dbus.Error) {
|
||||
return
|
||||
}
|
||||
|
||||
// AboutToShowGroup is com.canonical.dbusmenu.AboutToShowGroup method.
|
||||
func (t *tray) AboutToShowGroup(ids []int32) (updatesNeeded []int32, idErrors []int32, err *dbus.Error) {
|
||||
return
|
||||
}
|
||||
|
||||
func createMenuPropSpec() map[string]map[string]*prop.Prop {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
return map[string]map[string]*prop.Prop{
|
||||
"com.canonical.dbusmenu": {
|
||||
"Version": {
|
||||
Value: instance.menuVersion,
|
||||
Writable: true,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"TextDirection": {
|
||||
Value: "ltr",
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"Status": {
|
||||
Value: "normal",
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"IconThemePath": {
|
||||
Value: []string{},
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// menuLayout is a named struct to map into generated bindings. It represents the layout of a menu item
|
||||
type menuLayout = struct {
|
||||
V0 int32 // the unique ID of this item
|
||||
V1 map[string]dbus.Variant // properties for this menu item layout
|
||||
V2 []dbus.Variant // child menu item layouts
|
||||
}
|
||||
|
||||
func addOrUpdateMenuItem(item *MenuItem) {
|
||||
var layout *menuLayout
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
m, exists := findLayout(int32(item.id))
|
||||
if exists {
|
||||
layout = m
|
||||
} else {
|
||||
layout = &menuLayout{
|
||||
V0: int32(item.id),
|
||||
V1: map[string]dbus.Variant{},
|
||||
V2: []dbus.Variant{},
|
||||
}
|
||||
|
||||
parent := instance.menu
|
||||
if item.parent != nil {
|
||||
m, ok := findLayout(int32(item.parent.id))
|
||||
if ok {
|
||||
parent = m
|
||||
parent.V1["children-display"] = dbus.MakeVariant("submenu")
|
||||
}
|
||||
}
|
||||
parent.V2 = append(parent.V2, dbus.MakeVariant(layout))
|
||||
}
|
||||
|
||||
applyItemToLayout(item, layout)
|
||||
if exists {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func addSeparator(id uint32, parent uint32) {
|
||||
menu, _ := findLayout(int32(parent))
|
||||
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
layout := &menuLayout{
|
||||
V0: int32(id),
|
||||
V1: map[string]dbus.Variant{
|
||||
"type": dbus.MakeVariant("separator"),
|
||||
},
|
||||
V2: []dbus.Variant{},
|
||||
}
|
||||
menu.V2 = append(menu.V2, dbus.MakeVariant(layout))
|
||||
refresh()
|
||||
}
|
||||
|
||||
func applyItemToLayout(in *MenuItem, out *menuLayout) {
|
||||
out.V1["enabled"] = dbus.MakeVariant(!in.disabled)
|
||||
out.V1["label"] = dbus.MakeVariant(in.title)
|
||||
|
||||
if in.isCheckable {
|
||||
out.V1["toggle-type"] = dbus.MakeVariant("checkmark")
|
||||
if in.checked {
|
||||
out.V1["toggle-state"] = dbus.MakeVariant(1)
|
||||
} else {
|
||||
out.V1["toggle-state"] = dbus.MakeVariant(0)
|
||||
}
|
||||
} else {
|
||||
out.V1["toggle-type"] = dbus.MakeVariant("")
|
||||
out.V1["toggle-state"] = dbus.MakeVariant(0)
|
||||
}
|
||||
}
|
||||
|
||||
func findLayout(id int32) (*menuLayout, bool) {
|
||||
if id == 0 {
|
||||
return instance.menu, true
|
||||
}
|
||||
return findSubLayout(id, instance.menu.V2)
|
||||
}
|
||||
|
||||
func findSubLayout(id int32, vals []dbus.Variant) (*menuLayout, bool) {
|
||||
for _, i := range vals {
|
||||
item := i.Value().(*menuLayout)
|
||||
if item.V0 == id {
|
||||
return item, true
|
||||
}
|
||||
|
||||
if len(item.V2) > 0 {
|
||||
child, ok := findSubLayout(id, item.V2)
|
||||
if ok {
|
||||
return child, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func removeSubLayout(id int32, vals []dbus.Variant) ([]dbus.Variant, bool) {
|
||||
for idx, i := range vals {
|
||||
item := i.Value().(*menuLayout)
|
||||
if item.V0 == id {
|
||||
return append(vals[:idx], vals[idx+1:]...), true
|
||||
}
|
||||
|
||||
if len(item.V2) > 0 {
|
||||
if child, removed := removeSubLayout(id, item.V2); removed {
|
||||
return child, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vals, false
|
||||
}
|
||||
|
||||
func removeMenuItem(item *MenuItem) {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
|
||||
parent := instance.menu
|
||||
if item.parent != nil {
|
||||
m, ok := findLayout(int32(item.parent.id))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
parent = m
|
||||
}
|
||||
|
||||
if items, removed := removeSubLayout(int32(item.id), parent.V2); removed {
|
||||
parent.V2 = items
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func hideMenuItem(item *MenuItem) {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
m, exists := findLayout(int32(item.id))
|
||||
if exists {
|
||||
m.V1["visible"] = dbus.MakeVariant(false)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func showMenuItem(item *MenuItem) {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
m, exists := findLayout(int32(item.id))
|
||||
if exists {
|
||||
m.V1["visible"] = dbus.MakeVariant(true)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
instance.lock.Lock()
|
||||
defer instance.lock.Unlock()
|
||||
if instance.conn == nil || instance.menuProps == nil {
|
||||
return
|
||||
}
|
||||
instance.menuVersion++
|
||||
dbusErr := instance.menuProps.Set("com.canonical.dbusmenu", "Version",
|
||||
dbus.MakeVariant(instance.menuVersion))
|
||||
if dbusErr != nil {
|
||||
log.Printf("systray error: failed to update menu version: %v\n", dbusErr)
|
||||
return
|
||||
}
|
||||
err := menu.Emit(instance.conn, &menu.Dbusmenu_LayoutUpdatedSignal{
|
||||
Path: menuPath,
|
||||
Body: &menu.Dbusmenu_LayoutUpdatedSignalBody{
|
||||
Revision: instance.menuVersion,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to emit layout updated signal: %v\n", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func resetMenu() {
|
||||
instance.menuLock.Lock()
|
||||
defer instance.menuLock.Unlock()
|
||||
instance.menu = &menuLayout{}
|
||||
instance.menuVersion++
|
||||
refresh()
|
||||
}
|
||||
44
vendor/fyne.io/systray/systray_notifier_unix.go
generated
vendored
Normal file
44
vendor/fyne.io/systray/systray_notifier_unix.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package systray
|
||||
|
||||
import (
|
||||
"fyne.io/systray/internal/generated/notifier"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
type leftRightNotifierItem struct {
|
||||
}
|
||||
|
||||
func newLeftRightNotifierItem() notifier.StatusNotifierItemer {
|
||||
return &leftRightNotifierItem{}
|
||||
}
|
||||
|
||||
func (i *leftRightNotifierItem) Activate(_, _ int32) *dbus.Error {
|
||||
if f := tappedLeft; f == nil {
|
||||
return &dbus.ErrMsgUnknownMethod
|
||||
}
|
||||
|
||||
tappedLeft()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *leftRightNotifierItem) ContextMenu(_, _ int32) *dbus.Error {
|
||||
if f := tappedRight; f == nil {
|
||||
return &dbus.ErrMsgUnknownMethod
|
||||
}
|
||||
|
||||
tappedRight()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *leftRightNotifierItem) SecondaryActivate(_, _ int32) *dbus.Error {
|
||||
if f := tappedRight; f == nil {
|
||||
return &dbus.ErrMsgUnknownMethod
|
||||
}
|
||||
|
||||
tappedRight()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *leftRightNotifierItem) Scroll(_ int32, _ string) *dbus.Error {
|
||||
return &dbus.ErrMsgUnknownMethod
|
||||
}
|
||||
435
vendor/fyne.io/systray/systray_unix.go
generated
vendored
Normal file
435
vendor/fyne.io/systray/systray_unix.go
generated
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
//go:build (linux || freebsd || openbsd || netbsd) && !android
|
||||
|
||||
//Note that you need to have github.com/knightpp/dbus-codegen-go installed from "custom" branch
|
||||
//go:generate dbus-codegen-go -prefix org.kde -package notifier -output internal/generated/notifier/status_notifier_item.go internal/StatusNotifierItem.xml
|
||||
//go:generate dbus-codegen-go -prefix com.canonical -package menu -output internal/generated/menu/dbus_menu.go internal/DbusMenu.xml
|
||||
|
||||
package systray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/png" // used only here
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/godbus/dbus/v5/introspect"
|
||||
"github.com/godbus/dbus/v5/prop"
|
||||
|
||||
"fyne.io/systray/internal/generated/menu"
|
||||
"fyne.io/systray/internal/generated/notifier"
|
||||
)
|
||||
|
||||
const (
|
||||
path = "/StatusNotifierItem"
|
||||
menuPath = "/StatusNotifierItem/menu"
|
||||
)
|
||||
|
||||
var (
|
||||
// to signal quitting the internal main loop
|
||||
quitChan = make(chan struct{})
|
||||
|
||||
// instance is the current instance of our DBus tray server
|
||||
instance = &tray{menu: &menuLayout{}, menuVersion: 1}
|
||||
)
|
||||
|
||||
// SetTemplateIcon sets the systray icon as a template icon (on macOS), falling back
|
||||
// to a regular icon on other platforms.
|
||||
// templateIconBytes and iconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
// TODO handle the templateIconBytes?
|
||||
SetIcon(regularIconBytes)
|
||||
}
|
||||
|
||||
// SetIcon sets the systray icon.
|
||||
// iconBytes should be the content of .ico for windows and .ico/.jpg/.png
|
||||
// for other platforms.
|
||||
func SetIcon(iconBytes []byte) {
|
||||
instance.lock.Lock()
|
||||
instance.iconData = iconBytes
|
||||
props := instance.props
|
||||
conn := instance.conn
|
||||
defer instance.lock.Unlock()
|
||||
|
||||
if props == nil {
|
||||
return
|
||||
}
|
||||
|
||||
props.SetMust("org.kde.StatusNotifierItem", "IconPixmap",
|
||||
[]PX{convertToPixels(iconBytes)})
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := notifier.Emit(conn, ¬ifier.StatusNotifierItem_NewIconSignal{
|
||||
Path: path,
|
||||
Body: ¬ifier.StatusNotifierItem_NewIconSignalBody{},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to emit new icon signal: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SetIconFromFilePath sets the systray icon from a file path.
|
||||
// iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
|
||||
func SetIconFromFilePath(iconFilePath string) error {
|
||||
bytes, err := os.ReadFile(iconFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read icon file: %v", err)
|
||||
}
|
||||
SetIcon(bytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTitle sets the systray title, only available on Mac and Linux.
|
||||
func SetTitle(t string) {
|
||||
instance.lock.Lock()
|
||||
instance.title = t
|
||||
props := instance.props
|
||||
conn := instance.conn
|
||||
defer instance.lock.Unlock()
|
||||
|
||||
if props == nil {
|
||||
return
|
||||
}
|
||||
dbusErr := props.Set("org.kde.StatusNotifierItem", "Title",
|
||||
dbus.MakeVariant(t))
|
||||
if dbusErr != nil {
|
||||
log.Printf("systray error: failed to set Title prop: %s\n", dbusErr)
|
||||
return
|
||||
}
|
||||
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := notifier.Emit(conn, ¬ifier.StatusNotifierItem_NewTitleSignal{
|
||||
Path: path,
|
||||
Body: ¬ifier.StatusNotifierItem_NewTitleSignalBody{},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to emit new title signal: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
|
||||
// only available on Mac and Windows.
|
||||
func SetTooltip(tooltipTitle string) {
|
||||
instance.lock.Lock()
|
||||
instance.tooltipTitle = tooltipTitle
|
||||
props := instance.props
|
||||
defer instance.lock.Unlock()
|
||||
|
||||
if props == nil {
|
||||
return
|
||||
}
|
||||
dbusErr := props.Set("org.kde.StatusNotifierItem", "ToolTip",
|
||||
dbus.MakeVariant(tooltip{V2: tooltipTitle}))
|
||||
if dbusErr != nil {
|
||||
log.Printf("systray error: failed to set ToolTip prop: %s\n", dbusErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SetTemplateIcon sets the icon of a menu item as a template icon (on macOS). On Windows and
|
||||
// Linux, it falls back to the regular icon bytes.
|
||||
// templateIconBytes and regularIconBytes should be the content of .ico for windows and
|
||||
// .ico/.jpg/.png for other platforms.
|
||||
func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes []byte) {
|
||||
item.SetIcon(regularIconBytes)
|
||||
}
|
||||
|
||||
// SetRemovalAllowed sets whether a user can remove the systray icon or not.
|
||||
// This is only supported on macOS.
|
||||
func SetRemovalAllowed(allowed bool) {
|
||||
}
|
||||
|
||||
func setInternalLoop(_ bool) {
|
||||
// nothing to action on Linux
|
||||
}
|
||||
|
||||
func registerSystray() {
|
||||
}
|
||||
|
||||
func nativeLoop() int {
|
||||
nativeStart()
|
||||
<-quitChan
|
||||
nativeEnd()
|
||||
return 0
|
||||
}
|
||||
|
||||
func nativeEnd() {
|
||||
runSystrayExit()
|
||||
instance.conn.Close()
|
||||
}
|
||||
|
||||
func quit() {
|
||||
close(quitChan)
|
||||
}
|
||||
|
||||
func nativeStart() {
|
||||
systrayReady()
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to connect to DBus: %v\n", err)
|
||||
return
|
||||
}
|
||||
err = notifier.ExportStatusNotifierItem(conn, path, newLeftRightNotifierItem())
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to export status notifier item: %v\n", err)
|
||||
}
|
||||
err = menu.ExportDbusmenu(conn, menuPath, instance)
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to export status notifier menu: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("org.kde.StatusNotifierItem-%d-1", os.Getpid()) // register id 1 for this process
|
||||
_, err = conn.RequestName(name, dbus.NameFlagDoNotQueue)
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to request name: %s\n", err)
|
||||
// it's not critical error: continue
|
||||
}
|
||||
props, err := prop.Export(conn, path, instance.createPropSpec())
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to export notifier item properties to bus: %s\n", err)
|
||||
return
|
||||
}
|
||||
menuProps, err := prop.Export(conn, menuPath, createMenuPropSpec())
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to export notifier menu properties to bus: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
node := introspect.Node{
|
||||
Name: path,
|
||||
Interfaces: []introspect.Interface{
|
||||
introspect.IntrospectData,
|
||||
prop.IntrospectData,
|
||||
notifier.IntrospectDataStatusNotifierItem,
|
||||
},
|
||||
}
|
||||
err = conn.Export(introspect.NewIntrospectable(&node), path,
|
||||
"org.freedesktop.DBus.Introspectable")
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to export node introspection: %s\n", err)
|
||||
return
|
||||
}
|
||||
menuNode := introspect.Node{
|
||||
Name: menuPath,
|
||||
Interfaces: []introspect.Interface{
|
||||
introspect.IntrospectData,
|
||||
prop.IntrospectData,
|
||||
menu.IntrospectDataDbusmenu,
|
||||
},
|
||||
}
|
||||
err = conn.Export(introspect.NewIntrospectable(&menuNode), menuPath,
|
||||
"org.freedesktop.DBus.Introspectable")
|
||||
if err != nil {
|
||||
log.Printf("systray error: failed to export menu node introspection: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
instance.lock.Lock()
|
||||
instance.conn = conn
|
||||
instance.props = props
|
||||
instance.menuProps = menuProps
|
||||
instance.lock.Unlock()
|
||||
|
||||
go stayRegistered()
|
||||
}
|
||||
|
||||
func register() bool {
|
||||
obj := instance.conn.Object("org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher")
|
||||
call := obj.Call("org.kde.StatusNotifierWatcher.RegisterStatusNotifierItem", 0, path)
|
||||
if call.Err != nil {
|
||||
log.Printf("systray error: failed to register: %v\n", call.Err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func stayRegistered() {
|
||||
register()
|
||||
|
||||
conn := instance.conn
|
||||
if err := conn.AddMatchSignal(
|
||||
dbus.WithMatchObjectPath("/org/freedesktop/DBus"),
|
||||
dbus.WithMatchInterface("org.freedesktop.DBus"),
|
||||
dbus.WithMatchSender("org.freedesktop.DBus"),
|
||||
dbus.WithMatchMember("NameOwnerChanged"),
|
||||
dbus.WithMatchArg(0, "org.kde.StatusNotifierWatcher"),
|
||||
); err != nil {
|
||||
log.Printf("systray error: failed to register signal matching: %v\n", err)
|
||||
// If we can't monitor signals, there is no point in
|
||||
// us being here. we're either registered or not (per
|
||||
// above) and will roll the dice from here...
|
||||
return
|
||||
}
|
||||
|
||||
sc := make(chan *dbus.Signal, 10)
|
||||
conn.Signal(sc)
|
||||
|
||||
for {
|
||||
select {
|
||||
case sig := <-sc:
|
||||
if sig == nil {
|
||||
return // We get a nil signal when closing the window.
|
||||
} else if len(sig.Body) < 3 {
|
||||
return // malformed signal?
|
||||
}
|
||||
|
||||
// sig.Body has the args, which are [name old_owner new_owner]
|
||||
if s, ok := sig.Body[2].(string); ok && s != "" {
|
||||
register()
|
||||
}
|
||||
case <-quitChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tray is a basic type that handles the dbus functionality
|
||||
type tray struct {
|
||||
// the DBus connection that we will use
|
||||
conn *dbus.Conn
|
||||
|
||||
// icon data for the main systray icon
|
||||
iconData []byte
|
||||
// title and tooltip state
|
||||
title, tooltipTitle string
|
||||
|
||||
lock sync.Mutex
|
||||
menu *menuLayout
|
||||
menuLock sync.RWMutex
|
||||
props, menuProps *prop.Properties
|
||||
menuVersion uint32
|
||||
}
|
||||
|
||||
func (t *tray) createPropSpec() map[string]map[string]*prop.Prop {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
id := t.title
|
||||
if id == "" {
|
||||
id = fmt.Sprintf("systray_%d", os.Getpid())
|
||||
}
|
||||
return map[string]map[string]*prop.Prop{
|
||||
"org.kde.StatusNotifierItem": {
|
||||
"Status": {
|
||||
Value: "Active", // Passive, Active or NeedsAttention
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"Title": {
|
||||
Value: t.title,
|
||||
Writable: true,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"Id": {
|
||||
Value: id,
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"Category": {
|
||||
Value: "ApplicationStatus",
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"IconName": {
|
||||
Value: "",
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"IconPixmap": {
|
||||
Value: []PX{convertToPixels(t.iconData)},
|
||||
Writable: true,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"IconThemePath": {
|
||||
Value: "",
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"ItemIsMenu": {
|
||||
Value: tappedLeft == nil && tappedRight == nil,
|
||||
Writable: false,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"Menu": {
|
||||
Value: dbus.ObjectPath(menuPath),
|
||||
Writable: true,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
"ToolTip": {
|
||||
Value: tooltip{V2: t.tooltipTitle},
|
||||
Writable: true,
|
||||
Emit: prop.EmitTrue,
|
||||
Callback: nil,
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
// PX is picture pix map structure with width and high
|
||||
type PX struct {
|
||||
W, H int
|
||||
Pix []byte
|
||||
}
|
||||
|
||||
// tooltip is our data for a tooltip property.
|
||||
// Param names need to match the generated code...
|
||||
type tooltip = struct {
|
||||
V0 string // name
|
||||
V1 []PX // icons
|
||||
V2 string // title
|
||||
V3 string // description
|
||||
}
|
||||
|
||||
func convertToPixels(data []byte) PX {
|
||||
if len(data) == 0 {
|
||||
return PX{}
|
||||
}
|
||||
|
||||
img, _, err := image.Decode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
log.Printf("Failed to read icon format %v", err)
|
||||
return PX{}
|
||||
}
|
||||
|
||||
return PX{
|
||||
img.Bounds().Dx(), img.Bounds().Dy(),
|
||||
argbForImage(img),
|
||||
}
|
||||
}
|
||||
|
||||
func argbForImage(img image.Image) []byte {
|
||||
w, h := img.Bounds().Dx(), img.Bounds().Dy()
|
||||
data := make([]byte, w*h*4)
|
||||
i := 0
|
||||
for y := 0; y < h; y++ {
|
||||
for x := 0; x < w; x++ {
|
||||
r, g, b, a := img.At(x, y).RGBA()
|
||||
data[i] = byte(a)
|
||||
data[i+1] = byte(r)
|
||||
data[i+2] = byte(g)
|
||||
data[i+3] = byte(b)
|
||||
i += 4
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
321
src/systray/systray_windows.go → vendor/fyne.io/systray/systray_windows.go
generated
vendored
321
src/systray/systray_windows.go → vendor/fyne.io/systray/systray_windows.go
generated
vendored
@@ -1,17 +1,19 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package systray
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
@@ -24,6 +26,7 @@ var (
|
||||
g32 = windows.NewLazySystemDLL("Gdi32.dll")
|
||||
pCreateCompatibleBitmap = g32.NewProc("CreateCompatibleBitmap")
|
||||
pCreateCompatibleDC = g32.NewProc("CreateCompatibleDC")
|
||||
pCreateDIBSection = g32.NewProc("CreateDIBSection")
|
||||
pDeleteDC = g32.NewProc("DeleteDC")
|
||||
pSelectObject = g32.NewProc("SelectObject")
|
||||
|
||||
@@ -39,12 +42,13 @@ var (
|
||||
pCreateWindowEx = u32.NewProc("CreateWindowExW")
|
||||
pDefWindowProc = u32.NewProc("DefWindowProcW")
|
||||
pDeleteMenu = u32.NewProc("DeleteMenu")
|
||||
pDestroyMenu = u32.NewProc("DestroyMenu")
|
||||
pRemoveMenu = u32.NewProc("RemoveMenu")
|
||||
pDestroyWindow = u32.NewProc("DestroyWindow")
|
||||
pDispatchMessage = u32.NewProc("DispatchMessageW")
|
||||
pDrawIconEx = u32.NewProc("DrawIconEx")
|
||||
pGetCursorPos = u32.NewProc("GetCursorPos")
|
||||
pGetDC = u32.NewProc("GetDC")
|
||||
pGetMenuItemID = u32.NewProc("GetMenuItemID")
|
||||
pGetMessage = u32.NewProc("GetMessageW")
|
||||
pGetSystemMetrics = u32.NewProc("GetSystemMetrics")
|
||||
pInsertMenuItem = u32.NewProc("InsertMenuItemW")
|
||||
@@ -64,6 +68,9 @@ var (
|
||||
pTranslateMessage = u32.NewProc("TranslateMessage")
|
||||
pUnregisterClass = u32.NewProc("UnregisterClassW")
|
||||
pUpdateWindow = u32.NewProc("UpdateWindow")
|
||||
|
||||
// ErrTrayNotReadyYet is returned by functions when they are called before the tray has been initialized.
|
||||
ErrTrayNotReadyYet = errors.New("tray not ready yet")
|
||||
)
|
||||
|
||||
// Contains window class information.
|
||||
@@ -175,6 +182,30 @@ type point struct {
|
||||
X, Y int32
|
||||
}
|
||||
|
||||
// The BITMAPINFO structure defines the dimensions and color information for a DIB.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo
|
||||
type bitmapInfo struct {
|
||||
BmiHeader bitmapInfoHeader
|
||||
BmiColors windows.Handle
|
||||
}
|
||||
|
||||
// The BITMAPINFOHEADER structure contains information about the dimensions and color format of a device-independent bitmap (DIB).
|
||||
// https://learn.microsoft.com/en-us/previous-versions/dd183376(v=vs.85)
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
|
||||
type bitmapInfoHeader struct {
|
||||
BiSize uint32
|
||||
BiWidth int32
|
||||
BiHeight int32
|
||||
BiPlanes uint16
|
||||
BiBitCount uint16
|
||||
BiCompression uint32
|
||||
BiSizeImage uint32
|
||||
BiXPelsPerMeter int32
|
||||
BiYPelsPerMeter int32
|
||||
BiClrUsed uint32
|
||||
BiClrImportant uint32
|
||||
}
|
||||
|
||||
// Contains information about loaded resources
|
||||
type winTray struct {
|
||||
instance,
|
||||
@@ -205,11 +236,22 @@ type winTray struct {
|
||||
|
||||
wmSystrayMessage,
|
||||
wmTaskbarCreated uint32
|
||||
|
||||
initialized atomic.Bool
|
||||
}
|
||||
|
||||
// isReady checks if the tray as already been initialized. It is not goroutine safe with in regard to the initialization function, but prevents a panic when functions are called too early.
|
||||
func (t *winTray) isReady() bool {
|
||||
return t.initialized.Load()
|
||||
}
|
||||
|
||||
// Loads an image from file and shows it in tray.
|
||||
// Shell_NotifyIcon: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159(v=vs.85).aspx
|
||||
func (t *winTray) setIcon(src string) error {
|
||||
if !wt.isReady() {
|
||||
return ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
const NIF_ICON = 0x00000002
|
||||
|
||||
h, err := t.loadIconFrom(src)
|
||||
@@ -229,6 +271,10 @@ func (t *winTray) setIcon(src string) error {
|
||||
// Sets tooltip on icon.
|
||||
// Shell_NotifyIcon: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762159(v=vs.85).aspx
|
||||
func (t *winTray) setTooltip(src string) error {
|
||||
if !wt.isReady() {
|
||||
return ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
const NIF_TIP = 0x00000004
|
||||
b, err := windows.UTF16FromString(src)
|
||||
if err != nil {
|
||||
@@ -244,15 +290,11 @@ func (t *winTray) setTooltip(src string) error {
|
||||
return t.nid.modify()
|
||||
}
|
||||
|
||||
var wt winTray
|
||||
var wt = winTray{}
|
||||
|
||||
// WindowProc callback function that processes messages sent to a window.
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx
|
||||
func (t *winTray) wndProc(
|
||||
hWnd windows.Handle,
|
||||
message uint32,
|
||||
wParam, lParam uintptr,
|
||||
) (lResult uintptr) {
|
||||
func (t *winTray) wndProc(hWnd windows.Handle, message uint32, wParam, lParam uintptr) (lResult uintptr) {
|
||||
const (
|
||||
WM_RBUTTONUP = 0x0205
|
||||
WM_LBUTTONUP = 0x0202
|
||||
@@ -260,11 +302,8 @@ func (t *winTray) wndProc(
|
||||
WM_ENDSESSION = 0x0016
|
||||
WM_CLOSE = 0x0010
|
||||
WM_DESTROY = 0x0002
|
||||
WM_CREATE = 0x0001
|
||||
)
|
||||
switch message {
|
||||
case WM_CREATE:
|
||||
systrayReady()
|
||||
case WM_COMMAND:
|
||||
menuItemId := int32(wParam)
|
||||
// https://docs.microsoft.com/en-us/windows/win32/menurc/wm-command#menus
|
||||
@@ -284,11 +323,13 @@ func (t *winTray) wndProc(
|
||||
t.nid.delete()
|
||||
}
|
||||
t.muNID.Unlock()
|
||||
systrayExit()
|
||||
runSystrayExit()
|
||||
case t.wmSystrayMessage:
|
||||
switch lParam {
|
||||
case WM_RBUTTONUP, WM_LBUTTONUP:
|
||||
t.showMenu()
|
||||
case WM_LBUTTONUP:
|
||||
systrayLeftClick()
|
||||
case WM_RBUTTONUP:
|
||||
systrayRightClick()
|
||||
}
|
||||
case t.wmTaskbarCreated: // on explorer.exe restarts
|
||||
t.muNID.Lock()
|
||||
@@ -498,12 +539,16 @@ func (t *winTray) convertToSubMenu(menuItemId uint32) (windows.Handle, error) {
|
||||
return menu, nil
|
||||
}
|
||||
|
||||
func (t *winTray) addOrUpdateMenuItem(
|
||||
menuItemId uint32,
|
||||
parentId uint32,
|
||||
title string,
|
||||
disabled, checked bool,
|
||||
) error {
|
||||
// SetRemovalAllowed sets whether a user can remove the systray icon or not.
|
||||
// This is only supported on macOS.
|
||||
func SetRemovalAllowed(allowed bool) {
|
||||
}
|
||||
|
||||
func (t *winTray) addOrUpdateMenuItem(menuItemId uint32, parentId uint32, title string, disabled, checked bool) error {
|
||||
if !wt.isReady() {
|
||||
return ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647578(v=vs.85).aspx
|
||||
const (
|
||||
MIIM_FTYPE = 0x00000100
|
||||
@@ -568,6 +613,14 @@ func (t *winTray) addOrUpdateMenuItem(
|
||||
}
|
||||
|
||||
if res == 0 {
|
||||
// Menu item does not already exist, create it
|
||||
t.muMenus.RLock()
|
||||
submenu, exists := t.menus[menuItemId]
|
||||
t.muMenus.RUnlock()
|
||||
if exists {
|
||||
mi.Mask |= MIIM_SUBMENU
|
||||
mi.SubMenu = submenu
|
||||
}
|
||||
t.addToVisibleItems(parentId, menuItemId)
|
||||
position := t.getVisibleItemIndex(parentId, menuItemId)
|
||||
res, _, err = pInsertMenuItem.Call(
|
||||
@@ -589,6 +642,10 @@ func (t *winTray) addOrUpdateMenuItem(
|
||||
}
|
||||
|
||||
func (t *winTray) addSeparatorMenuItem(menuItemId, parentId uint32) error {
|
||||
if !wt.isReady() {
|
||||
return ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647578(v=vs.85).aspx
|
||||
const (
|
||||
MIIM_FTYPE = 0x00000100
|
||||
@@ -623,8 +680,11 @@ func (t *winTray) addSeparatorMenuItem(menuItemId, parentId uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *winTray) hideMenuItem(menuItemId, parentId uint32) error {
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647629(v=vs.85).aspx
|
||||
func (t *winTray) removeMenuItem(menuItemId, parentId uint32) error {
|
||||
if !wt.isReady() {
|
||||
return ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
const MF_BYCOMMAND = 0x00000000
|
||||
const ERROR_SUCCESS syscall.Errno = 0
|
||||
|
||||
@@ -644,7 +704,35 @@ func (t *winTray) hideMenuItem(menuItemId, parentId uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *winTray) hideMenuItem(menuItemId, parentId uint32) error {
|
||||
if !wt.isReady() {
|
||||
return ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
const MF_BYCOMMAND = 0x00000000
|
||||
const ERROR_SUCCESS syscall.Errno = 0
|
||||
|
||||
t.muMenus.RLock()
|
||||
menu := uintptr(t.menus[parentId])
|
||||
t.muMenus.RUnlock()
|
||||
res, _, err := pRemoveMenu.Call(
|
||||
menu,
|
||||
uintptr(menuItemId),
|
||||
MF_BYCOMMAND,
|
||||
)
|
||||
if res == 0 && err.(syscall.Errno) != ERROR_SUCCESS {
|
||||
return err
|
||||
}
|
||||
t.delFromVisibleItems(parentId, menuItemId)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *winTray) showMenu() error {
|
||||
if !wt.isReady() {
|
||||
return ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
const (
|
||||
TPM_BOTTOMALIGN = 0x0020
|
||||
TPM_LEFTALIGN = 0x0000
|
||||
@@ -678,7 +766,7 @@ func (t *winTray) delFromVisibleItems(parent, val uint32) {
|
||||
visibleItems := t.visibleItems[parent]
|
||||
for i, itemval := range visibleItems {
|
||||
if val == itemval {
|
||||
visibleItems = append(visibleItems[:i], visibleItems[i+1:]...)
|
||||
t.visibleItems[parent] = append(visibleItems[:i], visibleItems[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -710,6 +798,10 @@ func (t *winTray) getVisibleItemIndex(parent, val uint32) int {
|
||||
// Loads an image from file to be shown in tray or menu item.
|
||||
// LoadImage: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648045(v=vs.85).aspx
|
||||
func (t *winTray) loadIconFrom(src string) (windows.Handle, error) {
|
||||
if !wt.isReady() {
|
||||
return 0, ErrTrayNotReadyYet
|
||||
}
|
||||
|
||||
const IMAGE_ICON = 1 // Loads an icon
|
||||
const LR_LOADFROMFILE = 0x00000010 // Loads the stand-alone image from the file
|
||||
const LR_DEFAULTSIZE = 0x00000040 // Loads default-size icon for windows(SM_CXICON x SM_CYICON) if cx, cy are set to zero
|
||||
@@ -742,7 +834,7 @@ func (t *winTray) loadIconFrom(src string) (windows.Handle, error) {
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
|
||||
func iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
|
||||
const SM_CXSMICON = 49
|
||||
const SM_CYSMICON = 50
|
||||
const DI_NORMAL = 0x3
|
||||
@@ -758,10 +850,7 @@ func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
|
||||
defer pDeleteDC.Call(hMemDC)
|
||||
cx, _, _ := pGetSystemMetrics.Call(SM_CXSMICON)
|
||||
cy, _, _ := pGetSystemMetrics.Call(SM_CYSMICON)
|
||||
hMemBmp, _, err := pCreateCompatibleBitmap.Call(hDC, cx, cy)
|
||||
if hMemBmp == 0 {
|
||||
return 0, err
|
||||
}
|
||||
hMemBmp, err := create32BitHBitmap(hMemDC, int32(cx), int32(cy))
|
||||
hOriginalBmp, _, _ := pSelectObject.Call(hMemDC, hMemBmp)
|
||||
defer pSelectObject.Call(hMemDC, hOriginalBmp)
|
||||
res, _, err := pDrawIconEx.Call(hMemDC, 0, 0, uintptr(hIcon), cx, cy, 0, uintptr(0), DI_NORMAL)
|
||||
@@ -771,22 +860,51 @@ func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) {
|
||||
return windows.Handle(hMemBmp), nil
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection
|
||||
func create32BitHBitmap(hDC uintptr, cx, cy int32) (uintptr, error) {
|
||||
const BI_RGB uint32 = 0
|
||||
const DIB_RGB_COLORS = 0
|
||||
bmi := bitmapInfo{
|
||||
BmiHeader: bitmapInfoHeader{
|
||||
BiPlanes: 1,
|
||||
BiCompression: BI_RGB,
|
||||
BiWidth: cx,
|
||||
BiHeight: cy,
|
||||
BiBitCount: 32,
|
||||
},
|
||||
}
|
||||
bmi.BmiHeader.BiSize = uint32(unsafe.Sizeof(bmi.BmiHeader))
|
||||
var bits uintptr
|
||||
hBitmap, _, err := pCreateDIBSection.Call(
|
||||
hDC,
|
||||
uintptr(unsafe.Pointer(&bmi)),
|
||||
DIB_RGB_COLORS,
|
||||
uintptr(unsafe.Pointer(&bits)),
|
||||
uintptr(0),
|
||||
0,
|
||||
)
|
||||
if hBitmap == 0 {
|
||||
return 0, err
|
||||
}
|
||||
return hBitmap, nil
|
||||
}
|
||||
|
||||
func registerSystray() {
|
||||
if err := wt.initInstance(); err != nil {
|
||||
log.Printf("Unable to init instance: %v", err)
|
||||
log.Printf("systray error: unable to init instance: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := wt.createMenu(); err != nil {
|
||||
log.Printf("Unable to create menu: %v", err)
|
||||
log.Printf("systray error: unable to create menu: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
wt.initialized.Store(true)
|
||||
systrayReady()
|
||||
}
|
||||
|
||||
func nativeLoop() {
|
||||
// Main message pump.
|
||||
m := &struct {
|
||||
var m = &struct {
|
||||
WindowHandle windows.Handle
|
||||
Message uint32
|
||||
Wparam uintptr
|
||||
@@ -794,7 +912,23 @@ func nativeLoop() {
|
||||
Time uint32
|
||||
Pt point
|
||||
}{}
|
||||
for {
|
||||
|
||||
func nativeLoop() {
|
||||
for doNativeTick() {
|
||||
}
|
||||
}
|
||||
|
||||
func nativeEnd() {
|
||||
}
|
||||
|
||||
func nativeStart() {
|
||||
go func() {
|
||||
for doNativeTick() {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func doNativeTick() bool {
|
||||
ret, _, err := pGetMessage.Call(uintptr(unsafe.Pointer(m)), 0, 0, 0)
|
||||
|
||||
// If the function retrieves a message other than WM_QUIT, the return value is nonzero.
|
||||
@@ -803,15 +937,15 @@ func nativeLoop() {
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms644936(v=vs.85).aspx
|
||||
switch int32(ret) {
|
||||
case -1:
|
||||
log.Printf("Error at message loop: %v", err)
|
||||
return
|
||||
log.Printf("systray error: message loop failure: %s\n", err)
|
||||
return false
|
||||
case 0:
|
||||
return
|
||||
return false
|
||||
default:
|
||||
pTranslateMessage.Call(uintptr(unsafe.Pointer(m)))
|
||||
pDispatchMessage.Call(uintptr(unsafe.Pointer(m)))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func quit() {
|
||||
@@ -823,6 +957,16 @@ func quit() {
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
wt.muNID.Lock()
|
||||
if wt.nid != nil {
|
||||
wt.nid.delete()
|
||||
}
|
||||
wt.muNID.Unlock()
|
||||
runSystrayExit()
|
||||
}
|
||||
|
||||
func setInternalLoop(bool) {
|
||||
}
|
||||
|
||||
func iconBytesToFilePath(iconBytes []byte) (string, error) {
|
||||
@@ -844,15 +988,25 @@ func iconBytesToFilePath(iconBytes []byte) (string, error) {
|
||||
func SetIcon(iconBytes []byte) {
|
||||
iconFilePath, err := iconBytesToFilePath(iconBytes)
|
||||
if err != nil {
|
||||
log.Printf("Unable to write icon data to temp file: %v", err)
|
||||
log.Printf("systray error: unable to write icon data to temp file: %s\n", err)
|
||||
return
|
||||
}
|
||||
if err := wt.setIcon(iconFilePath); err != nil {
|
||||
log.Printf("Unable to set icon: %v", err)
|
||||
log.Printf("systray error: unable to set icon: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SetIconFromFilePath sets the systray icon from a file path.
|
||||
// iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
|
||||
func SetIconFromFilePath(iconFilePath string) error {
|
||||
err := wt.setIcon(iconFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set icon: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTemplateIcon sets the systray icon as a template icon (on macOS), falling back
|
||||
// to a regular icon on other platforms.
|
||||
// templateIconBytes and iconBytes should be the content of .ico for windows and
|
||||
@@ -878,57 +1032,53 @@ func (item *MenuItem) parentId() uint32 {
|
||||
func (item *MenuItem) SetIcon(iconBytes []byte) {
|
||||
iconFilePath, err := iconBytesToFilePath(iconBytes)
|
||||
if err != nil {
|
||||
log.Printf("Unable to write icon data to temp file: %v", err)
|
||||
log.Printf("systray error: unable to write icon data to temp file: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = item.SetIconFromFilePath(iconFilePath)
|
||||
if err != nil {
|
||||
log.Printf("systray error: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SetIconFromFilePath sets the icon of a menu item from a file path.
|
||||
// iconFilePath should be the path to a .ico for windows and .ico/.jpg/.png for other platforms.
|
||||
func (item *MenuItem) SetIconFromFilePath(iconFilePath string) error {
|
||||
h, err := wt.loadIconFrom(iconFilePath)
|
||||
if err != nil {
|
||||
log.Printf("Unable to load icon from temp file: %v", err)
|
||||
return
|
||||
return fmt.Errorf("unable to load icon from file: %s", err)
|
||||
}
|
||||
|
||||
h, err = wt.iconToBitmap(h)
|
||||
h, err = iconToBitmap(h)
|
||||
if err != nil {
|
||||
log.Printf("Unable to convert icon to bitmap: %v", err)
|
||||
return
|
||||
return fmt.Errorf("unable to convert icon to bitmap: %s", err)
|
||||
}
|
||||
wt.muMenuItemIcons.Lock()
|
||||
wt.menuItemIcons[uint32(item.id)] = h
|
||||
wt.muMenuItemIcons.Unlock()
|
||||
|
||||
err = wt.addOrUpdateMenuItem(
|
||||
uint32(item.id),
|
||||
item.parentId(),
|
||||
item.title,
|
||||
item.disabled,
|
||||
item.checked,
|
||||
)
|
||||
err = wt.addOrUpdateMenuItem(uint32(item.id), item.parentId(), item.title, item.disabled, item.checked)
|
||||
if err != nil {
|
||||
log.Printf("Unable to addOrUpdateMenuItem: %v", err)
|
||||
return
|
||||
return fmt.Errorf("unable to addOrUpdateMenuItem: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTooltip sets the systray tooltip to display on mouse hover of the tray icon,
|
||||
// only available on Mac and Windows.
|
||||
func SetTooltip(tooltip string) {
|
||||
if err := wt.setTooltip(tooltip); err != nil {
|
||||
log.Printf("Unable to set tooltip: %v", err)
|
||||
log.Printf("systray error: unable to set tooltip: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func addOrUpdateMenuItem(item *MenuItem) {
|
||||
err := wt.addOrUpdateMenuItem(
|
||||
uint32(item.id),
|
||||
item.parentId(),
|
||||
item.title,
|
||||
item.disabled,
|
||||
item.checked,
|
||||
)
|
||||
err := wt.addOrUpdateMenuItem(uint32(item.id), item.parentId(), item.title, item.disabled, item.checked)
|
||||
if err != nil {
|
||||
log.Printf("Unable to addOrUpdateMenuItem: %v", err)
|
||||
log.Printf("systray error: unable to addOrUpdateMenuItem: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -941,10 +1091,10 @@ func (item *MenuItem) SetTemplateIcon(templateIconBytes []byte, regularIconBytes
|
||||
item.SetIcon(regularIconBytes)
|
||||
}
|
||||
|
||||
func addSeparator(id uint32) {
|
||||
err := wt.addSeparatorMenuItem(id, 0)
|
||||
func addSeparator(id uint32, parent uint32) {
|
||||
err := wt.addSeparatorMenuItem(id, parent)
|
||||
if err != nil {
|
||||
log.Printf("Unable to addSeparator: %v", err)
|
||||
log.Printf("systray error: unable to addSeparator: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -952,7 +1102,15 @@ func addSeparator(id uint32) {
|
||||
func hideMenuItem(item *MenuItem) {
|
||||
err := wt.hideMenuItem(uint32(item.id), item.parentId())
|
||||
if err != nil {
|
||||
log.Printf("Unable to hideMenuItem: %v", err)
|
||||
log.Printf("systray error: unable to hideMenuItem: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func removeMenuItem(item *MenuItem) {
|
||||
err := wt.removeMenuItem(uint32(item.id), item.parentId())
|
||||
if err != nil {
|
||||
log.Printf("systray error: unable to removeMenuItem: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -960,3 +1118,30 @@ func hideMenuItem(item *MenuItem) {
|
||||
func showMenuItem(item *MenuItem) {
|
||||
addOrUpdateMenuItem(item)
|
||||
}
|
||||
|
||||
func resetMenu() {
|
||||
_, _, _ = pDestroyMenu.Call(uintptr(wt.menus[0]))
|
||||
wt.visibleItems = make(map[uint32][]uint32)
|
||||
wt.menus = make(map[uint32]windows.Handle)
|
||||
wt.menuOf = make(map[uint32]windows.Handle)
|
||||
wt.menuItemIcons = make(map[uint32]windows.Handle)
|
||||
wt.createMenu()
|
||||
}
|
||||
|
||||
func systrayLeftClick() {
|
||||
if fn := tappedLeft; fn != nil {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
wt.showMenu()
|
||||
}
|
||||
|
||||
func systrayRightClick() {
|
||||
if fn := tappedRight; fn != nil {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
wt.showMenu()
|
||||
}
|
||||
50
vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md
generated
vendored
Normal file
50
vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# How to Contribute
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Fork the repository on GitHub
|
||||
- Read the [README](README.markdown) for build and test instructions
|
||||
- Play with the project, submit bugs, submit patches!
|
||||
|
||||
## Contribution Flow
|
||||
|
||||
This is a rough outline of what a contributor's workflow looks like:
|
||||
|
||||
- Create a topic branch from where you want to base your work (usually master).
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- Make sure the tests pass, and add any new tests as appropriate.
|
||||
- Submit a pull request to the original repository.
|
||||
|
||||
Thanks for your contributions!
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow a rough convention for commit messages that is designed to answer two
|
||||
questions: what changed and why. The subject line should feature the what and
|
||||
the body of the commit should describe the why.
|
||||
|
||||
```
|
||||
scripts: add the test-cluster command
|
||||
|
||||
this uses tmux to setup a test cluster that you can easily kill and
|
||||
start for debugging.
|
||||
|
||||
Fixes #38
|
||||
```
|
||||
|
||||
The format can be described more formally as follows:
|
||||
|
||||
```
|
||||
<subsystem>: <what changed>
|
||||
<BLANK LINE>
|
||||
<why this change was made>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The first line is the subject and should be no longer than 70 characters, the
|
||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||
This allows the message to be easier to read on GitHub as well as in various
|
||||
git tools.
|
||||
25
vendor/github.com/godbus/dbus/v5/LICENSE
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/v5/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
3
vendor/github.com/godbus/dbus/v5/MAINTAINERS
generated
vendored
Normal file
3
vendor/github.com/godbus/dbus/v5/MAINTAINERS
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
Brandon Philips <brandon@ifup.org> (@philips)
|
||||
Brian Waldon <brian@waldon.cc> (@bcwaldon)
|
||||
John Southworth <jsouthwo@brocade.com> (@jsouthworth)
|
||||
46
vendor/github.com/godbus/dbus/v5/README.md
generated
vendored
Normal file
46
vendor/github.com/godbus/dbus/v5/README.md
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||

|
||||
|
||||
dbus
|
||||
----
|
||||
|
||||
dbus is a simple library that implements native Go client bindings for the
|
||||
D-Bus message bus system.
|
||||
|
||||
### Features
|
||||
|
||||
* Complete native implementation of the D-Bus message protocol
|
||||
* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
|
||||
* Subpackages that help with the introspection / property interfaces
|
||||
|
||||
### Installation
|
||||
|
||||
This packages requires Go 1.12 or later. It can be installed by running the command below:
|
||||
|
||||
```
|
||||
go get github.com/godbus/dbus/v5
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
The complete package documentation and some simple examples are available at
|
||||
[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the
|
||||
[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory
|
||||
gives a short overview over the basic usage.
|
||||
|
||||
#### Projects using godbus
|
||||
- [fyne](https://github.com/fyne-io/fyne) a cross platform GUI in Go inspired by Material Design.
|
||||
- [fynedesk](https://github.com/fyne-io/fynedesk) a full desktop environment for Linux/Unix using Fyne.
|
||||
- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API.
|
||||
- [iwd](https://github.com/shibumi/iwd) go bindings for the internet wireless daemon "iwd".
|
||||
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
|
||||
- [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players.
|
||||
|
||||
Please note that the API is considered unstable for now and may change without
|
||||
further notice.
|
||||
|
||||
### License
|
||||
|
||||
go.dbus is available under the Simplified BSD License; see LICENSE for the full
|
||||
text.
|
||||
|
||||
Nearly all of the credit for this library goes to github.com/guelfey/go.dbus.
|
||||
257
vendor/github.com/godbus/dbus/v5/auth.go
generated
vendored
Normal file
257
vendor/github.com/godbus/dbus/v5/auth.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// AuthStatus represents the Status of an authentication mechanism.
|
||||
type AuthStatus byte
|
||||
|
||||
const (
|
||||
// AuthOk signals that authentication is finished; the next command
|
||||
// from the server should be an OK.
|
||||
AuthOk AuthStatus = iota
|
||||
|
||||
// AuthContinue signals that additional data is needed; the next command
|
||||
// from the server should be a DATA.
|
||||
AuthContinue
|
||||
|
||||
// AuthError signals an error; the server sent invalid data or some
|
||||
// other unexpected thing happened and the current authentication
|
||||
// process should be aborted.
|
||||
AuthError
|
||||
)
|
||||
|
||||
type authState byte
|
||||
|
||||
const (
|
||||
waitingForData authState = iota
|
||||
waitingForOk
|
||||
waitingForReject
|
||||
)
|
||||
|
||||
// Auth defines the behaviour of an authentication mechanism.
|
||||
type Auth interface {
|
||||
// Return the name of the mechanism, the argument to the first AUTH command
|
||||
// and the next status.
|
||||
FirstData() (name, resp []byte, status AuthStatus)
|
||||
|
||||
// Process the given DATA command, and return the argument to the DATA
|
||||
// command and the next status. If len(resp) == 0, no DATA command is sent.
|
||||
HandleData(data []byte) (resp []byte, status AuthStatus)
|
||||
}
|
||||
|
||||
// Auth authenticates the connection, trying the given list of authentication
|
||||
// mechanisms (in that order). If nil is passed, the EXTERNAL and
|
||||
// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
|
||||
// connections, this method must be called before sending any messages to the
|
||||
// bus. Auth must not be called on shared connections.
|
||||
func (conn *Conn) Auth(methods []Auth) error {
|
||||
if methods == nil {
|
||||
uid := strconv.Itoa(os.Geteuid())
|
||||
methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
|
||||
}
|
||||
in := bufio.NewReader(conn.transport)
|
||||
err := conn.transport.SendNullByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = authWriteLine(conn.transport, []byte("AUTH"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := authReadLine(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) {
|
||||
return errors.New("dbus: authentication protocol error")
|
||||
}
|
||||
s = s[1:]
|
||||
for _, v := range s {
|
||||
for _, m := range methods {
|
||||
if name, _, status := m.FirstData(); bytes.Equal(v, name) {
|
||||
var ok bool
|
||||
err = authWriteLine(conn.transport, []byte("AUTH"), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch status {
|
||||
case AuthOk:
|
||||
err, ok = conn.tryAuth(m, waitingForOk, in)
|
||||
case AuthContinue:
|
||||
err, ok = conn.tryAuth(m, waitingForData, in)
|
||||
default:
|
||||
panic("dbus: invalid authentication status")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if conn.transport.SupportsUnixFDs() {
|
||||
err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
line, err := authReadLine(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
|
||||
conn.EnableUnixFDs()
|
||||
conn.unixFD = true
|
||||
case bytes.Equal(line[0], []byte("ERROR")):
|
||||
default:
|
||||
return errors.New("dbus: authentication protocol error")
|
||||
}
|
||||
}
|
||||
err = authWriteLine(conn.transport, []byte("BEGIN"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go conn.inWorker()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("dbus: authentication failed")
|
||||
}
|
||||
|
||||
// tryAuth tries to authenticate with m as the mechanism, using state as the
|
||||
// initial authState and in for reading input. It returns (nil, true) on
|
||||
// success, (nil, false) on a REJECTED and (someErr, false) if some other
|
||||
// error occurred.
|
||||
func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
|
||||
for {
|
||||
s, err := authReadLine(in)
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
switch {
|
||||
case state == waitingForData && string(s[0]) == "DATA":
|
||||
if len(s) != 2 {
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
continue
|
||||
}
|
||||
data, status := m.HandleData(s[1])
|
||||
switch status {
|
||||
case AuthOk, AuthContinue:
|
||||
if len(data) != 0 {
|
||||
err = authWriteLine(conn.transport, []byte("DATA"), data)
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
}
|
||||
if status == AuthOk {
|
||||
state = waitingForOk
|
||||
}
|
||||
case AuthError:
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
}
|
||||
case state == waitingForData && string(s[0]) == "REJECTED":
|
||||
return nil, false
|
||||
case state == waitingForData && string(s[0]) == "ERROR":
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
case state == waitingForData && string(s[0]) == "OK":
|
||||
if len(s) != 2 {
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
} else {
|
||||
conn.uuid = string(s[1])
|
||||
return nil, true
|
||||
}
|
||||
case state == waitingForData:
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
case state == waitingForOk && string(s[0]) == "OK":
|
||||
if len(s) != 2 {
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
} else {
|
||||
conn.uuid = string(s[1])
|
||||
return nil, true
|
||||
}
|
||||
case state == waitingForOk && string(s[0]) == "DATA":
|
||||
err = authWriteLine(conn.transport, []byte("DATA"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
case state == waitingForOk && string(s[0]) == "REJECTED":
|
||||
return nil, false
|
||||
case state == waitingForOk && string(s[0]) == "ERROR":
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
case state == waitingForOk:
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
case state == waitingForReject && string(s[0]) == "REJECTED":
|
||||
return nil, false
|
||||
case state == waitingForReject:
|
||||
return errors.New("dbus: authentication protocol error"), false
|
||||
default:
|
||||
panic("dbus: invalid auth state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// authReadLine reads a line and separates it into its fields.
|
||||
func authReadLine(in *bufio.Reader) ([][]byte, error) {
|
||||
data, err := in.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = bytes.TrimSuffix(data, []byte("\r\n"))
|
||||
return bytes.Split(data, []byte{' '}), nil
|
||||
}
|
||||
|
||||
// authWriteLine writes the given line in the authentication protocol format
|
||||
// (elements of data separated by a " " and terminated by "\r\n").
|
||||
func authWriteLine(out io.Writer, data ...[]byte) error {
|
||||
buf := make([]byte, 0)
|
||||
for i, v := range data {
|
||||
buf = append(buf, v...)
|
||||
if i != len(data)-1 {
|
||||
buf = append(buf, ' ')
|
||||
}
|
||||
}
|
||||
buf = append(buf, '\r')
|
||||
buf = append(buf, '\n')
|
||||
n, err := out.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(buf) {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
16
vendor/github.com/godbus/dbus/v5/auth_anonymous.go
generated
vendored
Normal file
16
vendor/github.com/godbus/dbus/v5/auth_anonymous.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package dbus
|
||||
|
||||
// AuthAnonymous returns an Auth that uses the ANONYMOUS mechanism.
|
||||
func AuthAnonymous() Auth {
|
||||
return &authAnonymous{}
|
||||
}
|
||||
|
||||
type authAnonymous struct{}
|
||||
|
||||
func (a *authAnonymous) FirstData() (name, resp []byte, status AuthStatus) {
|
||||
return []byte("ANONYMOUS"), nil, AuthOk
|
||||
}
|
||||
|
||||
func (a *authAnonymous) HandleData(data []byte) (resp []byte, status AuthStatus) {
|
||||
return nil, AuthError
|
||||
}
|
||||
26
vendor/github.com/godbus/dbus/v5/auth_external.go
generated
vendored
Normal file
26
vendor/github.com/godbus/dbus/v5/auth_external.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// AuthExternal returns an Auth that authenticates as the given user with the
|
||||
// EXTERNAL mechanism.
|
||||
func AuthExternal(user string) Auth {
|
||||
return authExternal{user}
|
||||
}
|
||||
|
||||
// AuthExternal implements the EXTERNAL authentication mechanism.
|
||||
type authExternal struct {
|
||||
user string
|
||||
}
|
||||
|
||||
func (a authExternal) FirstData() ([]byte, []byte, AuthStatus) {
|
||||
b := make([]byte, 2*len(a.user))
|
||||
hex.Encode(b, []byte(a.user))
|
||||
return []byte("EXTERNAL"), b, AuthOk
|
||||
}
|
||||
|
||||
func (a authExternal) HandleData(b []byte) ([]byte, AuthStatus) {
|
||||
return nil, AuthError
|
||||
}
|
||||
102
vendor/github.com/godbus/dbus/v5/auth_sha1.go
generated
vendored
Normal file
102
vendor/github.com/godbus/dbus/v5/auth_sha1.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
)
|
||||
|
||||
// AuthCookieSha1 returns an Auth that authenticates as the given user with the
|
||||
// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home
|
||||
// directory of the user.
|
||||
func AuthCookieSha1(user, home string) Auth {
|
||||
return authCookieSha1{user, home}
|
||||
}
|
||||
|
||||
type authCookieSha1 struct {
|
||||
user, home string
|
||||
}
|
||||
|
||||
func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) {
|
||||
b := make([]byte, 2*len(a.user))
|
||||
hex.Encode(b, []byte(a.user))
|
||||
return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue
|
||||
}
|
||||
|
||||
func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
|
||||
challenge := make([]byte, len(data)/2)
|
||||
_, err := hex.Decode(challenge, data)
|
||||
if err != nil {
|
||||
return nil, AuthError
|
||||
}
|
||||
b := bytes.Split(challenge, []byte{' '})
|
||||
if len(b) != 3 {
|
||||
return nil, AuthError
|
||||
}
|
||||
context := b[0]
|
||||
id := b[1]
|
||||
svchallenge := b[2]
|
||||
cookie := a.getCookie(context, id)
|
||||
if cookie == nil {
|
||||
return nil, AuthError
|
||||
}
|
||||
clchallenge := a.generateChallenge()
|
||||
if clchallenge == nil {
|
||||
return nil, AuthError
|
||||
}
|
||||
hash := sha1.New()
|
||||
hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'}))
|
||||
hexhash := make([]byte, 2*hash.Size())
|
||||
hex.Encode(hexhash, hash.Sum(nil))
|
||||
data = append(clchallenge, ' ')
|
||||
data = append(data, hexhash...)
|
||||
resp := make([]byte, 2*len(data))
|
||||
hex.Encode(resp, data)
|
||||
return resp, AuthOk
|
||||
}
|
||||
|
||||
// getCookie searches for the cookie identified by id in context and returns
|
||||
// the cookie content or nil. (Since HandleData can't return a specific error,
|
||||
// but only whether an error occurred, this function also doesn't bother to
|
||||
// return an error.)
|
||||
func (a authCookieSha1) getCookie(context, id []byte) []byte {
|
||||
file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer file.Close()
|
||||
rd := bufio.NewReader(file)
|
||||
for {
|
||||
line, err := rd.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
line = line[:len(line)-1]
|
||||
b := bytes.Split(line, []byte{' '})
|
||||
if len(b) != 3 {
|
||||
return nil
|
||||
}
|
||||
if bytes.Equal(b[0], id) {
|
||||
return b[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generateChallenge returns a random, hex-encoded challenge, or nil on error
|
||||
// (see above).
|
||||
func (a authCookieSha1) generateChallenge() []byte {
|
||||
b := make([]byte, 16)
|
||||
n, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if n != 16 {
|
||||
return nil
|
||||
}
|
||||
enc := make([]byte, 32)
|
||||
hex.Encode(enc, b)
|
||||
return enc
|
||||
}
|
||||
69
vendor/github.com/godbus/dbus/v5/call.go
generated
vendored
Normal file
69
vendor/github.com/godbus/dbus/v5/call.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var errSignature = errors.New("dbus: mismatched signature")
|
||||
|
||||
// Call represents a pending or completed method call.
|
||||
type Call struct {
|
||||
Destination string
|
||||
Path ObjectPath
|
||||
Method string
|
||||
Args []interface{}
|
||||
|
||||
// Strobes when the call is complete.
|
||||
Done chan *Call
|
||||
|
||||
// After completion, the error status. If this is non-nil, it may be an
|
||||
// error message from the peer (with Error as its type) or some other error.
|
||||
Err error
|
||||
|
||||
// Holds the response once the call is done.
|
||||
Body []interface{}
|
||||
|
||||
// ResponseSequence stores the sequence number of the DBus message containing
|
||||
// the call response (or error). This can be compared to the sequence number
|
||||
// of other call responses and signals on this connection to determine their
|
||||
// relative ordering on the underlying DBus connection.
|
||||
// For errors, ResponseSequence is populated only if the error came from a
|
||||
// DBusMessage that was received or if there was an error receiving. In case of
|
||||
// failure to make the call, ResponseSequence will be NoSequence.
|
||||
ResponseSequence Sequence
|
||||
|
||||
// tracks context and canceler
|
||||
ctx context.Context
|
||||
ctxCanceler context.CancelFunc
|
||||
}
|
||||
|
||||
func (c *Call) Context() context.Context {
|
||||
if c.ctx == nil {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
return c.ctx
|
||||
}
|
||||
|
||||
func (c *Call) ContextCancel() {
|
||||
if c.ctxCanceler != nil {
|
||||
c.ctxCanceler()
|
||||
}
|
||||
}
|
||||
|
||||
// Store stores the body of the reply into the provided pointers. It returns
|
||||
// an error if the signatures of the body and retvalues don't match, or if
|
||||
// the error status is not nil.
|
||||
func (c *Call) Store(retvalues ...interface{}) error {
|
||||
if c.Err != nil {
|
||||
return c.Err
|
||||
}
|
||||
|
||||
return Store(c.Body, retvalues...)
|
||||
}
|
||||
|
||||
func (c *Call) done() {
|
||||
c.Done <- c
|
||||
c.ContextCancel()
|
||||
}
|
||||
996
vendor/github.com/godbus/dbus/v5/conn.go
generated
vendored
Normal file
996
vendor/github.com/godbus/dbus/v5/conn.go
generated
vendored
Normal file
@@ -0,0 +1,996 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
systemBus *Conn
|
||||
systemBusLck sync.Mutex
|
||||
sessionBus *Conn
|
||||
sessionBusLck sync.Mutex
|
||||
)
|
||||
|
||||
// ErrClosed is the error returned by calls on a closed connection.
|
||||
var ErrClosed = errors.New("dbus: connection closed by user")
|
||||
|
||||
// Conn represents a connection to a message bus (usually, the system or
|
||||
// session bus).
|
||||
//
|
||||
// Connections are either shared or private. Shared connections
|
||||
// are shared between calls to the functions that return them. As a result,
|
||||
// the methods Close, Auth and Hello must not be called on them.
|
||||
//
|
||||
// Multiple goroutines may invoke methods on a connection simultaneously.
|
||||
type Conn struct {
|
||||
transport
|
||||
|
||||
ctx context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
|
||||
closeOnce sync.Once
|
||||
closeErr error
|
||||
|
||||
busObj BusObject
|
||||
unixFD bool
|
||||
uuid string
|
||||
|
||||
handler Handler
|
||||
signalHandler SignalHandler
|
||||
serialGen SerialGenerator
|
||||
inInt Interceptor
|
||||
outInt Interceptor
|
||||
auth []Auth
|
||||
|
||||
names *nameTracker
|
||||
calls *callTracker
|
||||
outHandler *outputHandler
|
||||
|
||||
eavesdropped chan<- *Message
|
||||
eavesdroppedLck sync.Mutex
|
||||
}
|
||||
|
||||
// SessionBus returns a shared connection to the session bus, connecting to it
|
||||
// if not already done.
|
||||
func SessionBus() (conn *Conn, err error) {
|
||||
sessionBusLck.Lock()
|
||||
defer sessionBusLck.Unlock()
|
||||
if sessionBus != nil &&
|
||||
sessionBus.Connected() {
|
||||
return sessionBus, nil
|
||||
}
|
||||
defer func() {
|
||||
if conn != nil {
|
||||
sessionBus = conn
|
||||
}
|
||||
}()
|
||||
conn, err = ConnectSessionBus()
|
||||
return
|
||||
}
|
||||
|
||||
func getSessionBusAddress(autolaunch bool) (string, error) {
|
||||
if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" {
|
||||
return address, nil
|
||||
|
||||
} else if address := tryDiscoverDbusSessionBusAddress(); address != "" {
|
||||
os.Setenv("DBUS_SESSION_BUS_ADDRESS", address)
|
||||
return address, nil
|
||||
}
|
||||
if !autolaunch {
|
||||
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||
}
|
||||
return getSessionBusPlatformAddress()
|
||||
}
|
||||
|
||||
// SessionBusPrivate returns a new private connection to the session bus.
|
||||
func SessionBusPrivate(opts ...ConnOption) (*Conn, error) {
|
||||
address, err := getSessionBusAddress(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Dial(address, opts...)
|
||||
}
|
||||
|
||||
// SessionBusPrivate returns a new private connection to the session bus. If
|
||||
// the session bus is not already open, do not attempt to launch it.
|
||||
func SessionBusPrivateNoAutoStartup(opts ...ConnOption) (*Conn, error) {
|
||||
address, err := getSessionBusAddress(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Dial(address, opts...)
|
||||
}
|
||||
|
||||
// SessionBusPrivate returns a new private connection to the session bus.
|
||||
//
|
||||
// Deprecated: use SessionBusPrivate with options instead.
|
||||
func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||
return SessionBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
|
||||
}
|
||||
|
||||
// SystemBus returns a shared connection to the system bus, connecting to it if
|
||||
// not already done.
|
||||
func SystemBus() (conn *Conn, err error) {
|
||||
systemBusLck.Lock()
|
||||
defer systemBusLck.Unlock()
|
||||
if systemBus != nil &&
|
||||
systemBus.Connected() {
|
||||
return systemBus, nil
|
||||
}
|
||||
defer func() {
|
||||
if conn != nil {
|
||||
systemBus = conn
|
||||
}
|
||||
}()
|
||||
conn, err = ConnectSystemBus()
|
||||
return
|
||||
}
|
||||
|
||||
// ConnectSessionBus connects to the session bus.
|
||||
func ConnectSessionBus(opts ...ConnOption) (*Conn, error) {
|
||||
address, err := getSessionBusAddress(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Connect(address, opts...)
|
||||
}
|
||||
|
||||
// ConnectSystemBus connects to the system bus.
|
||||
func ConnectSystemBus(opts ...ConnOption) (*Conn, error) {
|
||||
return Connect(getSystemBusPlatformAddress(), opts...)
|
||||
}
|
||||
|
||||
// Connect connects to the given address.
|
||||
//
|
||||
// Returned connection is ready to use and doesn't require calling
|
||||
// Auth and Hello methods to make it usable.
|
||||
func Connect(address string, opts ...ConnOption) (*Conn, error) {
|
||||
conn, err := Dial(address, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = conn.Auth(conn.auth); err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err = conn.Hello(); err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// SystemBusPrivate returns a new private connection to the system bus.
|
||||
// Note: this connection is not ready to use. One must perform Auth and Hello
|
||||
// on the connection before it is usable.
|
||||
func SystemBusPrivate(opts ...ConnOption) (*Conn, error) {
|
||||
return Dial(getSystemBusPlatformAddress(), opts...)
|
||||
}
|
||||
|
||||
// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers.
|
||||
//
|
||||
// Deprecated: use SystemBusPrivate with options instead.
|
||||
func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||
return SystemBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler))
|
||||
}
|
||||
|
||||
// Dial establishes a new private connection to the message bus specified by address.
|
||||
func Dial(address string, opts ...ConnOption) (*Conn, error) {
|
||||
tr, err := getTransport(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newConn(tr, opts...)
|
||||
}
|
||||
|
||||
// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers.
|
||||
//
|
||||
// Deprecated: use Dial with options instead.
|
||||
func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||
return Dial(address, WithHandler(handler), WithSignalHandler(signalHandler))
|
||||
}
|
||||
|
||||
// ConnOption is a connection option.
|
||||
type ConnOption func(conn *Conn) error
|
||||
|
||||
// WithHandler overrides the default handler.
|
||||
func WithHandler(handler Handler) ConnOption {
|
||||
return func(conn *Conn) error {
|
||||
conn.handler = handler
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSignalHandler overrides the default signal handler.
|
||||
func WithSignalHandler(handler SignalHandler) ConnOption {
|
||||
return func(conn *Conn) error {
|
||||
conn.signalHandler = handler
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSerialGenerator overrides the default signals generator.
|
||||
func WithSerialGenerator(gen SerialGenerator) ConnOption {
|
||||
return func(conn *Conn) error {
|
||||
conn.serialGen = gen
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithAuth sets authentication methods for the auth conversation.
|
||||
func WithAuth(methods ...Auth) ConnOption {
|
||||
return func(conn *Conn) error {
|
||||
conn.auth = methods
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Interceptor intercepts incoming and outgoing messages.
|
||||
type Interceptor func(msg *Message)
|
||||
|
||||
// WithIncomingInterceptor sets the given interceptor for incoming messages.
|
||||
func WithIncomingInterceptor(interceptor Interceptor) ConnOption {
|
||||
return func(conn *Conn) error {
|
||||
conn.inInt = interceptor
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithOutgoingInterceptor sets the given interceptor for outgoing messages.
|
||||
func WithOutgoingInterceptor(interceptor Interceptor) ConnOption {
|
||||
return func(conn *Conn) error {
|
||||
conn.outInt = interceptor
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithContext overrides the default context for the connection.
|
||||
func WithContext(ctx context.Context) ConnOption {
|
||||
return func(conn *Conn) error {
|
||||
conn.ctx = ctx
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewConn creates a new private *Conn from an already established connection.
|
||||
func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) {
|
||||
return newConn(genericTransport{conn}, opts...)
|
||||
}
|
||||
|
||||
// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers.
|
||||
//
|
||||
// Deprecated: use NewConn with options instead.
|
||||
func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||
return NewConn(genericTransport{conn}, WithHandler(handler), WithSignalHandler(signalHandler))
|
||||
}
|
||||
|
||||
// newConn creates a new *Conn from a transport.
|
||||
func newConn(tr transport, opts ...ConnOption) (*Conn, error) {
|
||||
conn := new(Conn)
|
||||
conn.transport = tr
|
||||
for _, opt := range opts {
|
||||
if err := opt(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if conn.ctx == nil {
|
||||
conn.ctx = context.Background()
|
||||
}
|
||||
conn.ctx, conn.cancelCtx = context.WithCancel(conn.ctx)
|
||||
|
||||
conn.calls = newCallTracker()
|
||||
if conn.handler == nil {
|
||||
conn.handler = NewDefaultHandler()
|
||||
}
|
||||
if conn.signalHandler == nil {
|
||||
conn.signalHandler = NewDefaultSignalHandler()
|
||||
}
|
||||
if conn.serialGen == nil {
|
||||
conn.serialGen = newSerialGenerator()
|
||||
}
|
||||
conn.outHandler = &outputHandler{conn: conn}
|
||||
conn.names = newNameTracker()
|
||||
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
||||
|
||||
go func() {
|
||||
<-conn.ctx.Done()
|
||||
conn.Close()
|
||||
}()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// BusObject returns the object owned by the bus daemon which handles
|
||||
// administrative requests.
|
||||
func (conn *Conn) BusObject() BusObject {
|
||||
return conn.busObj
|
||||
}
|
||||
|
||||
// Close closes the connection. Any blocked operations will return with errors
|
||||
// and the channels passed to Eavesdrop and Signal are closed. This method must
|
||||
// not be called on shared connections.
|
||||
func (conn *Conn) Close() error {
|
||||
conn.closeOnce.Do(func() {
|
||||
conn.outHandler.close()
|
||||
if term, ok := conn.signalHandler.(Terminator); ok {
|
||||
term.Terminate()
|
||||
}
|
||||
|
||||
if term, ok := conn.handler.(Terminator); ok {
|
||||
term.Terminate()
|
||||
}
|
||||
|
||||
conn.eavesdroppedLck.Lock()
|
||||
if conn.eavesdropped != nil {
|
||||
close(conn.eavesdropped)
|
||||
}
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
|
||||
conn.cancelCtx()
|
||||
|
||||
conn.closeErr = conn.transport.Close()
|
||||
})
|
||||
return conn.closeErr
|
||||
}
|
||||
|
||||
// Context returns the context associated with the connection. The
|
||||
// context will be cancelled when the connection is closed.
|
||||
func (conn *Conn) Context() context.Context {
|
||||
return conn.ctx
|
||||
}
|
||||
|
||||
// Connected returns whether conn is connected
|
||||
func (conn *Conn) Connected() bool {
|
||||
return conn.ctx.Err() == nil
|
||||
}
|
||||
|
||||
// Eavesdrop causes conn to send all incoming messages to the given channel
|
||||
// without further processing. Method replies, errors and signals will not be
|
||||
// sent to the appropriate channels and method calls will not be handled. If nil
|
||||
// is passed, the normal behaviour is restored.
|
||||
//
|
||||
// The caller has to make sure that ch is sufficiently buffered;
|
||||
// if a message arrives when a write to ch is not possible, the message is
|
||||
// discarded.
|
||||
func (conn *Conn) Eavesdrop(ch chan<- *Message) {
|
||||
conn.eavesdroppedLck.Lock()
|
||||
conn.eavesdropped = ch
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
}
|
||||
|
||||
// getSerial returns an unused serial.
|
||||
func (conn *Conn) getSerial() uint32 {
|
||||
return conn.serialGen.GetSerial()
|
||||
}
|
||||
|
||||
// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
|
||||
// called after authentication, but before sending any other messages to the
|
||||
// bus. Hello must not be called for shared connections.
|
||||
func (conn *Conn) Hello() error {
|
||||
var s string
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.names.acquireUniqueConnectionName(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// inWorker runs in an own goroutine, reading incoming messages from the
|
||||
// transport and dispatching them appropriately.
|
||||
func (conn *Conn) inWorker() {
|
||||
sequenceGen := newSequenceGenerator()
|
||||
for {
|
||||
msg, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
if _, ok := err.(InvalidMessageError); !ok {
|
||||
// Some read error occurred (usually EOF); we can't really do
|
||||
// anything but to shut down all stuff and returns errors to all
|
||||
// pending replies.
|
||||
conn.Close()
|
||||
conn.calls.finalizeAllWithError(sequenceGen, err)
|
||||
return
|
||||
}
|
||||
// invalid messages are ignored
|
||||
continue
|
||||
}
|
||||
conn.eavesdroppedLck.Lock()
|
||||
if conn.eavesdropped != nil {
|
||||
select {
|
||||
case conn.eavesdropped <- msg:
|
||||
default:
|
||||
}
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
continue
|
||||
}
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
dest, _ := msg.Headers[FieldDestination].value.(string)
|
||||
found := dest == "" ||
|
||||
!conn.names.uniqueNameIsKnown() ||
|
||||
conn.names.isKnownName(dest)
|
||||
if !found {
|
||||
// Eavesdropped a message, but no channel for it is registered.
|
||||
// Ignore it.
|
||||
continue
|
||||
}
|
||||
|
||||
if conn.inInt != nil {
|
||||
conn.inInt(msg)
|
||||
}
|
||||
sequence := sequenceGen.next()
|
||||
switch msg.Type {
|
||||
case TypeError:
|
||||
conn.serialGen.RetireSerial(conn.calls.handleDBusError(sequence, msg))
|
||||
case TypeMethodReply:
|
||||
conn.serialGen.RetireSerial(conn.calls.handleReply(sequence, msg))
|
||||
case TypeSignal:
|
||||
conn.handleSignal(sequence, msg)
|
||||
case TypeMethodCall:
|
||||
go conn.handleCall(msg)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) handleSignal(sequence Sequence, msg *Message) {
|
||||
iface := msg.Headers[FieldInterface].value.(string)
|
||||
member := msg.Headers[FieldMember].value.(string)
|
||||
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
||||
// sender is optional for signals.
|
||||
sender, _ := msg.Headers[FieldSender].value.(string)
|
||||
if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" {
|
||||
if member == "NameLost" {
|
||||
// If we lost the name on the bus, remove it from our
|
||||
// tracking list.
|
||||
name, ok := msg.Body[0].(string)
|
||||
if !ok {
|
||||
panic("Unable to read the lost name")
|
||||
}
|
||||
conn.names.loseName(name)
|
||||
} else if member == "NameAcquired" {
|
||||
// If we acquired the name on the bus, add it to our
|
||||
// tracking list.
|
||||
name, ok := msg.Body[0].(string)
|
||||
if !ok {
|
||||
panic("Unable to read the acquired name")
|
||||
}
|
||||
conn.names.acquireName(name)
|
||||
}
|
||||
}
|
||||
signal := &Signal{
|
||||
Sender: sender,
|
||||
Path: msg.Headers[FieldPath].value.(ObjectPath),
|
||||
Name: iface + "." + member,
|
||||
Body: msg.Body,
|
||||
Sequence: sequence,
|
||||
}
|
||||
conn.signalHandler.DeliverSignal(iface, member, signal)
|
||||
}
|
||||
|
||||
// Names returns the list of all names that are currently owned by this
|
||||
// connection. The slice is always at least one element long, the first element
|
||||
// being the unique name of the connection.
|
||||
func (conn *Conn) Names() []string {
|
||||
return conn.names.listKnownNames()
|
||||
}
|
||||
|
||||
// Object returns the object identified by the given destination name and path.
|
||||
func (conn *Conn) Object(dest string, path ObjectPath) BusObject {
|
||||
return &Object{conn, dest, path}
|
||||
}
|
||||
|
||||
func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) {
|
||||
if msg.serial == 0 {
|
||||
msg.serial = conn.getSerial()
|
||||
}
|
||||
if conn.outInt != nil {
|
||||
conn.outInt(msg)
|
||||
}
|
||||
err := conn.outHandler.sendAndIfClosed(msg, ifClosed)
|
||||
if err != nil {
|
||||
conn.handleSendError(msg, err)
|
||||
} else if msg.Type != TypeMethodCall {
|
||||
conn.serialGen.RetireSerial(msg.serial)
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) handleSendError(msg *Message, err error) {
|
||||
if msg.Type == TypeMethodCall {
|
||||
conn.calls.handleSendError(msg, err)
|
||||
} else if msg.Type == TypeMethodReply {
|
||||
if _, ok := err.(FormatError); ok {
|
||||
conn.sendError(err, msg.Headers[FieldDestination].value.(string), msg.Headers[FieldReplySerial].value.(uint32))
|
||||
}
|
||||
}
|
||||
conn.serialGen.RetireSerial(msg.serial)
|
||||
}
|
||||
|
||||
// Send sends the given message to the message bus. You usually don't need to
|
||||
// use this; use the higher-level equivalents (Call / Go, Emit and Export)
|
||||
// instead. If msg is a method call and NoReplyExpected is not set, a non-nil
|
||||
// call is returned and the same value is sent to ch (which must be buffered)
|
||||
// once the call is complete. Otherwise, ch is ignored and a Call structure is
|
||||
// returned of which only the Err member is valid.
|
||||
func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
|
||||
return conn.send(context.Background(), msg, ch)
|
||||
}
|
||||
|
||||
// SendWithContext acts like Send but takes a context
|
||||
func (conn *Conn) SendWithContext(ctx context.Context, msg *Message, ch chan *Call) *Call {
|
||||
return conn.send(ctx, msg, ch)
|
||||
}
|
||||
|
||||
func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
if ch == nil {
|
||||
ch = make(chan *Call, 1)
|
||||
} else if cap(ch) == 0 {
|
||||
panic("dbus: unbuffered channel passed to (*Conn).Send")
|
||||
}
|
||||
|
||||
var call *Call
|
||||
ctx, canceler := context.WithCancel(ctx)
|
||||
msg.serial = conn.getSerial()
|
||||
if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
|
||||
call = new(Call)
|
||||
call.Destination, _ = msg.Headers[FieldDestination].value.(string)
|
||||
call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
|
||||
iface, _ := msg.Headers[FieldInterface].value.(string)
|
||||
member, _ := msg.Headers[FieldMember].value.(string)
|
||||
call.Method = iface + "." + member
|
||||
call.Args = msg.Body
|
||||
call.Done = ch
|
||||
call.ctx = ctx
|
||||
call.ctxCanceler = canceler
|
||||
conn.calls.track(msg.serial, call)
|
||||
if ctx.Err() != nil {
|
||||
// short path: don't even send the message if context already cancelled
|
||||
conn.calls.handleSendError(msg, ctx.Err())
|
||||
return call
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
conn.calls.handleSendError(msg, ctx.Err())
|
||||
}()
|
||||
conn.sendMessageAndIfClosed(msg, func() {
|
||||
conn.calls.handleSendError(msg, ErrClosed)
|
||||
canceler()
|
||||
})
|
||||
} else {
|
||||
canceler()
|
||||
call = &Call{Err: nil, Done: ch}
|
||||
ch <- call
|
||||
conn.sendMessageAndIfClosed(msg, func() {
|
||||
call = &Call{Err: ErrClosed}
|
||||
})
|
||||
}
|
||||
return call
|
||||
}
|
||||
|
||||
// sendError creates an error message corresponding to the parameters and sends
|
||||
// it to conn.out.
|
||||
func (conn *Conn) sendError(err error, dest string, serial uint32) {
|
||||
var e *Error
|
||||
switch em := err.(type) {
|
||||
case Error:
|
||||
e = &em
|
||||
case *Error:
|
||||
e = em
|
||||
case DBusError:
|
||||
name, body := em.DBusError()
|
||||
e = NewError(name, body)
|
||||
default:
|
||||
e = MakeFailedError(err)
|
||||
}
|
||||
msg := new(Message)
|
||||
msg.Type = TypeError
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
if dest != "" {
|
||||
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||
}
|
||||
msg.Headers[FieldErrorName] = MakeVariant(e.Name)
|
||||
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||
msg.Body = e.Body
|
||||
if len(e.Body) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
|
||||
}
|
||||
conn.sendMessageAndIfClosed(msg, nil)
|
||||
}
|
||||
|
||||
// sendReply creates a method reply message corresponding to the parameters and
|
||||
// sends it to conn.out.
|
||||
func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
||||
msg := new(Message)
|
||||
msg.Type = TypeMethodReply
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
if dest != "" {
|
||||
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||
}
|
||||
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||
msg.Body = values
|
||||
if len(values) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||
}
|
||||
conn.sendMessageAndIfClosed(msg, nil)
|
||||
}
|
||||
|
||||
// AddMatchSignal registers the given match rule to receive broadcast
|
||||
// signals based on their contents.
|
||||
func (conn *Conn) AddMatchSignal(options ...MatchOption) error {
|
||||
return conn.AddMatchSignalContext(context.Background(), options...)
|
||||
}
|
||||
|
||||
// AddMatchSignalContext acts like AddMatchSignal but takes a context.
|
||||
func (conn *Conn) AddMatchSignalContext(ctx context.Context, options ...MatchOption) error {
|
||||
options = append([]MatchOption{withMatchType("signal")}, options...)
|
||||
return conn.busObj.CallWithContext(
|
||||
ctx,
|
||||
"org.freedesktop.DBus.AddMatch", 0,
|
||||
formatMatchOptions(options),
|
||||
).Store()
|
||||
}
|
||||
|
||||
// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal.
|
||||
func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error {
|
||||
return conn.RemoveMatchSignalContext(context.Background(), options...)
|
||||
}
|
||||
|
||||
// RemoveMatchSignalContext acts like RemoveMatchSignal but takes a context.
|
||||
func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...MatchOption) error {
|
||||
options = append([]MatchOption{withMatchType("signal")}, options...)
|
||||
return conn.busObj.CallWithContext(
|
||||
ctx,
|
||||
"org.freedesktop.DBus.RemoveMatch", 0,
|
||||
formatMatchOptions(options),
|
||||
).Store()
|
||||
}
|
||||
|
||||
// Signal registers the given channel to be passed all received signal messages.
|
||||
//
|
||||
// Multiple of these channels can be registered at the same time. The channel is
|
||||
// closed if the Conn is closed; it should not be closed by the caller before
|
||||
// RemoveSignal was called on it.
|
||||
//
|
||||
// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
|
||||
// channel for eavesdropped messages, this channel receives all signals, and
|
||||
// none of the channels passed to Signal will receive any signals.
|
||||
//
|
||||
// Panics if the signal handler is not a `SignalRegistrar`.
|
||||
func (conn *Conn) Signal(ch chan<- *Signal) {
|
||||
handler, ok := conn.signalHandler.(SignalRegistrar)
|
||||
if !ok {
|
||||
panic("cannot use this method with a non SignalRegistrar handler")
|
||||
}
|
||||
handler.AddSignal(ch)
|
||||
}
|
||||
|
||||
// RemoveSignal removes the given channel from the list of the registered channels.
|
||||
//
|
||||
// Panics if the signal handler is not a `SignalRegistrar`.
|
||||
func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
|
||||
handler, ok := conn.signalHandler.(SignalRegistrar)
|
||||
if !ok {
|
||||
panic("cannot use this method with a non SignalRegistrar handler")
|
||||
}
|
||||
handler.RemoveSignal(ch)
|
||||
}
|
||||
|
||||
// SupportsUnixFDs returns whether the underlying transport supports passing of
|
||||
// unix file descriptors. If this is false, method calls containing unix file
|
||||
// descriptors will return an error and emitted signals containing them will
|
||||
// not be sent.
|
||||
func (conn *Conn) SupportsUnixFDs() bool {
|
||||
return conn.unixFD
|
||||
}
|
||||
|
||||
// Error represents a D-Bus message of type Error.
|
||||
type Error struct {
|
||||
Name string
|
||||
Body []interface{}
|
||||
}
|
||||
|
||||
func NewError(name string, body []interface{}) *Error {
|
||||
return &Error{name, body}
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
if len(e.Body) >= 1 {
|
||||
s, ok := e.Body[0].(string)
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
// Signal represents a D-Bus message of type Signal. The name member is given in
|
||||
// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
|
||||
type Signal struct {
|
||||
Sender string
|
||||
Path ObjectPath
|
||||
Name string
|
||||
Body []interface{}
|
||||
Sequence Sequence
|
||||
}
|
||||
|
||||
// transport is a D-Bus transport.
|
||||
type transport interface {
|
||||
// Read and Write raw data (for example, for the authentication protocol).
|
||||
io.ReadWriteCloser
|
||||
|
||||
// Send the initial null byte used for the EXTERNAL mechanism.
|
||||
SendNullByte() error
|
||||
|
||||
// Returns whether this transport supports passing Unix FDs.
|
||||
SupportsUnixFDs() bool
|
||||
|
||||
// Signal the transport that Unix FD passing is enabled for this connection.
|
||||
EnableUnixFDs()
|
||||
|
||||
// Read / send a message, handling things like Unix FDs.
|
||||
ReadMessage() (*Message, error)
|
||||
SendMessage(*Message) error
|
||||
}
|
||||
|
||||
var (
|
||||
transports = make(map[string]func(string) (transport, error))
|
||||
)
|
||||
|
||||
func getTransport(address string) (transport, error) {
|
||||
var err error
|
||||
var t transport
|
||||
|
||||
addresses := strings.Split(address, ";")
|
||||
for _, v := range addresses {
|
||||
i := strings.IndexRune(v, ':')
|
||||
if i == -1 {
|
||||
err = errors.New("dbus: invalid bus address (no transport)")
|
||||
continue
|
||||
}
|
||||
f := transports[v[:i]]
|
||||
if f == nil {
|
||||
err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
|
||||
continue
|
||||
}
|
||||
t, err = f(v[i+1:])
|
||||
if err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// getKey gets a key from a the list of keys. Returns "" on error / not found...
|
||||
func getKey(s, key string) string {
|
||||
for _, keyEqualsValue := range strings.Split(s, ",") {
|
||||
keyValue := strings.SplitN(keyEqualsValue, "=", 2)
|
||||
if len(keyValue) == 2 && keyValue[0] == key {
|
||||
val, err := UnescapeBusAddressValue(keyValue[1])
|
||||
if err != nil {
|
||||
// No way to return an error.
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type outputHandler struct {
|
||||
conn *Conn
|
||||
sendLck sync.Mutex
|
||||
closed struct {
|
||||
isClosed bool
|
||||
lck sync.RWMutex
|
||||
}
|
||||
}
|
||||
|
||||
func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error {
|
||||
h.closed.lck.RLock()
|
||||
defer h.closed.lck.RUnlock()
|
||||
if h.closed.isClosed {
|
||||
if ifClosed != nil {
|
||||
ifClosed()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
h.sendLck.Lock()
|
||||
defer h.sendLck.Unlock()
|
||||
return h.conn.SendMessage(msg)
|
||||
}
|
||||
|
||||
func (h *outputHandler) close() {
|
||||
h.closed.lck.Lock()
|
||||
defer h.closed.lck.Unlock()
|
||||
h.closed.isClosed = true
|
||||
}
|
||||
|
||||
type serialGenerator struct {
|
||||
lck sync.Mutex
|
||||
nextSerial uint32
|
||||
serialUsed map[uint32]bool
|
||||
}
|
||||
|
||||
func newSerialGenerator() *serialGenerator {
|
||||
return &serialGenerator{
|
||||
serialUsed: map[uint32]bool{0: true},
|
||||
nextSerial: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (gen *serialGenerator) GetSerial() uint32 {
|
||||
gen.lck.Lock()
|
||||
defer gen.lck.Unlock()
|
||||
n := gen.nextSerial
|
||||
for gen.serialUsed[n] {
|
||||
n++
|
||||
}
|
||||
gen.serialUsed[n] = true
|
||||
gen.nextSerial = n + 1
|
||||
return n
|
||||
}
|
||||
|
||||
func (gen *serialGenerator) RetireSerial(serial uint32) {
|
||||
gen.lck.Lock()
|
||||
defer gen.lck.Unlock()
|
||||
delete(gen.serialUsed, serial)
|
||||
}
|
||||
|
||||
type nameTracker struct {
|
||||
lck sync.RWMutex
|
||||
unique string
|
||||
names map[string]struct{}
|
||||
}
|
||||
|
||||
func newNameTracker() *nameTracker {
|
||||
return &nameTracker{names: map[string]struct{}{}}
|
||||
}
|
||||
func (tracker *nameTracker) acquireUniqueConnectionName(name string) {
|
||||
tracker.lck.Lock()
|
||||
defer tracker.lck.Unlock()
|
||||
tracker.unique = name
|
||||
}
|
||||
func (tracker *nameTracker) acquireName(name string) {
|
||||
tracker.lck.Lock()
|
||||
defer tracker.lck.Unlock()
|
||||
tracker.names[name] = struct{}{}
|
||||
}
|
||||
func (tracker *nameTracker) loseName(name string) {
|
||||
tracker.lck.Lock()
|
||||
defer tracker.lck.Unlock()
|
||||
delete(tracker.names, name)
|
||||
}
|
||||
|
||||
func (tracker *nameTracker) uniqueNameIsKnown() bool {
|
||||
tracker.lck.RLock()
|
||||
defer tracker.lck.RUnlock()
|
||||
return tracker.unique != ""
|
||||
}
|
||||
func (tracker *nameTracker) isKnownName(name string) bool {
|
||||
tracker.lck.RLock()
|
||||
defer tracker.lck.RUnlock()
|
||||
_, ok := tracker.names[name]
|
||||
return ok || name == tracker.unique
|
||||
}
|
||||
func (tracker *nameTracker) listKnownNames() []string {
|
||||
tracker.lck.RLock()
|
||||
defer tracker.lck.RUnlock()
|
||||
out := make([]string, 0, len(tracker.names)+1)
|
||||
out = append(out, tracker.unique)
|
||||
for k := range tracker.names {
|
||||
out = append(out, k)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type callTracker struct {
|
||||
calls map[uint32]*Call
|
||||
lck sync.RWMutex
|
||||
}
|
||||
|
||||
func newCallTracker() *callTracker {
|
||||
return &callTracker{calls: map[uint32]*Call{}}
|
||||
}
|
||||
|
||||
func (tracker *callTracker) track(sn uint32, call *Call) {
|
||||
tracker.lck.Lock()
|
||||
tracker.calls[sn] = call
|
||||
tracker.lck.Unlock()
|
||||
}
|
||||
|
||||
func (tracker *callTracker) handleReply(sequence Sequence, msg *Message) uint32 {
|
||||
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
||||
tracker.lck.RLock()
|
||||
_, ok := tracker.calls[serial]
|
||||
tracker.lck.RUnlock()
|
||||
if ok {
|
||||
tracker.finalizeWithBody(serial, sequence, msg.Body)
|
||||
}
|
||||
return serial
|
||||
}
|
||||
|
||||
func (tracker *callTracker) handleDBusError(sequence Sequence, msg *Message) uint32 {
|
||||
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
||||
tracker.lck.RLock()
|
||||
_, ok := tracker.calls[serial]
|
||||
tracker.lck.RUnlock()
|
||||
if ok {
|
||||
name, _ := msg.Headers[FieldErrorName].value.(string)
|
||||
tracker.finalizeWithError(serial, sequence, Error{name, msg.Body})
|
||||
}
|
||||
return serial
|
||||
}
|
||||
|
||||
func (tracker *callTracker) handleSendError(msg *Message, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
tracker.lck.RLock()
|
||||
_, ok := tracker.calls[msg.serial]
|
||||
tracker.lck.RUnlock()
|
||||
if ok {
|
||||
tracker.finalizeWithError(msg.serial, NoSequence, err)
|
||||
}
|
||||
}
|
||||
|
||||
// finalize was the only func that did not strobe Done
|
||||
func (tracker *callTracker) finalize(sn uint32) {
|
||||
tracker.lck.Lock()
|
||||
defer tracker.lck.Unlock()
|
||||
c, ok := tracker.calls[sn]
|
||||
if ok {
|
||||
delete(tracker.calls, sn)
|
||||
c.ContextCancel()
|
||||
}
|
||||
}
|
||||
|
||||
func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, body []interface{}) {
|
||||
tracker.lck.Lock()
|
||||
c, ok := tracker.calls[sn]
|
||||
if ok {
|
||||
delete(tracker.calls, sn)
|
||||
}
|
||||
tracker.lck.Unlock()
|
||||
if ok {
|
||||
c.Body = body
|
||||
c.ResponseSequence = sequence
|
||||
c.done()
|
||||
}
|
||||
}
|
||||
|
||||
func (tracker *callTracker) finalizeWithError(sn uint32, sequence Sequence, err error) {
|
||||
tracker.lck.Lock()
|
||||
c, ok := tracker.calls[sn]
|
||||
if ok {
|
||||
delete(tracker.calls, sn)
|
||||
}
|
||||
tracker.lck.Unlock()
|
||||
if ok {
|
||||
c.Err = err
|
||||
c.ResponseSequence = sequence
|
||||
c.done()
|
||||
}
|
||||
}
|
||||
|
||||
func (tracker *callTracker) finalizeAllWithError(sequenceGen *sequenceGenerator, err error) {
|
||||
tracker.lck.Lock()
|
||||
closedCalls := make([]*Call, 0, len(tracker.calls))
|
||||
for sn := range tracker.calls {
|
||||
closedCalls = append(closedCalls, tracker.calls[sn])
|
||||
}
|
||||
tracker.calls = map[uint32]*Call{}
|
||||
tracker.lck.Unlock()
|
||||
for _, call := range closedCalls {
|
||||
call.Err = err
|
||||
call.ResponseSequence = sequenceGen.next()
|
||||
call.done()
|
||||
}
|
||||
}
|
||||
37
vendor/github.com/godbus/dbus/v5/conn_darwin.go
generated
vendored
Normal file
37
vendor/github.com/godbus/dbus/v5/conn_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket"
|
||||
|
||||
func getSessionBusPlatformAddress() (string, error) {
|
||||
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||
b, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||
}
|
||||
|
||||
return "unix:path=" + string(b[:len(b)-1]), nil
|
||||
}
|
||||
|
||||
func getSystemBusPlatformAddress() string {
|
||||
address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||
if address != "" {
|
||||
return fmt.Sprintf("unix:path=%s", address)
|
||||
}
|
||||
return defaultSystemBusAddress
|
||||
}
|
||||
|
||||
func tryDiscoverDbusSessionBusAddress() string {
|
||||
return ""
|
||||
}
|
||||
90
vendor/github.com/godbus/dbus/v5/conn_other.go
generated
vendored
Normal file
90
vendor/github.com/godbus/dbus/v5/conn_other.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// +build !darwin
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var execCommand = exec.Command
|
||||
|
||||
func getSessionBusPlatformAddress() (string, error) {
|
||||
cmd := execCommand("dbus-launch")
|
||||
b, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
i := bytes.IndexByte(b, '=')
|
||||
j := bytes.IndexByte(b, '\n')
|
||||
|
||||
if i == -1 || j == -1 || i > j {
|
||||
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||
}
|
||||
|
||||
env, addr := string(b[0:i]), string(b[i+1:j])
|
||||
os.Setenv(env, addr)
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// tryDiscoverDbusSessionBusAddress tries to discover an existing dbus session
|
||||
// and return the value of its DBUS_SESSION_BUS_ADDRESS.
|
||||
// It tries different techniques employed by different operating systems,
|
||||
// returning the first valid address it finds, or an empty string.
|
||||
//
|
||||
// * /run/user/<uid>/bus if this exists, it *is* the bus socket. present on
|
||||
// Ubuntu 18.04
|
||||
// * /run/user/<uid>/dbus-session: if this exists, it can be parsed for the bus
|
||||
// address. present on Ubuntu 16.04
|
||||
//
|
||||
// See https://dbus.freedesktop.org/doc/dbus-launch.1.html
|
||||
func tryDiscoverDbusSessionBusAddress() string {
|
||||
if runtimeDirectory, err := getRuntimeDirectory(); err == nil {
|
||||
|
||||
if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) {
|
||||
// if /run/user/<uid>/bus exists, that file itself
|
||||
// *is* the unix socket, so return its path
|
||||
return fmt.Sprintf("unix:path=%s", EscapeBusAddressValue(runUserBusFile))
|
||||
}
|
||||
if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) {
|
||||
// if /run/user/<uid>/dbus-session exists, it's a
|
||||
// text file // containing the address of the socket, e.g.:
|
||||
// DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-E1c73yNqrG
|
||||
|
||||
if f, err := ioutil.ReadFile(runUserSessionDbusFile); err == nil {
|
||||
fileContent := string(f)
|
||||
|
||||
prefix := "DBUS_SESSION_BUS_ADDRESS="
|
||||
|
||||
if strings.HasPrefix(fileContent, prefix) {
|
||||
address := strings.TrimRight(strings.TrimPrefix(fileContent, prefix), "\n\r")
|
||||
return address
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getRuntimeDirectory() (string, error) {
|
||||
if currentUser, err := user.Current(); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return fmt.Sprintf("/run/user/%s", currentUser.Uid), nil
|
||||
}
|
||||
}
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
17
vendor/github.com/godbus/dbus/v5/conn_unix.go
generated
vendored
Normal file
17
vendor/github.com/godbus/dbus/v5/conn_unix.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
//+build !windows,!solaris,!darwin
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
|
||||
|
||||
func getSystemBusPlatformAddress() string {
|
||||
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||
if address != "" {
|
||||
return address
|
||||
}
|
||||
return defaultSystemBusAddress
|
||||
}
|
||||
15
vendor/github.com/godbus/dbus/v5/conn_windows.go
generated
vendored
Normal file
15
vendor/github.com/godbus/dbus/v5/conn_windows.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
//+build windows
|
||||
|
||||
package dbus
|
||||
|
||||
import "os"
|
||||
|
||||
const defaultSystemBusAddress = "tcp:host=127.0.0.1,port=12434"
|
||||
|
||||
func getSystemBusPlatformAddress() string {
|
||||
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||
if address != "" {
|
||||
return address
|
||||
}
|
||||
return defaultSystemBusAddress
|
||||
}
|
||||
430
vendor/github.com/godbus/dbus/v5/dbus.go
generated
vendored
Normal file
430
vendor/github.com/godbus/dbus/v5/dbus.go
generated
vendored
Normal file
@@ -0,0 +1,430 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
byteType = reflect.TypeOf(byte(0))
|
||||
boolType = reflect.TypeOf(false)
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
int16Type = reflect.TypeOf(int16(0))
|
||||
uint16Type = reflect.TypeOf(uint16(0))
|
||||
intType = reflect.TypeOf(int(0))
|
||||
uintType = reflect.TypeOf(uint(0))
|
||||
int32Type = reflect.TypeOf(int32(0))
|
||||
uint32Type = reflect.TypeOf(uint32(0))
|
||||
int64Type = reflect.TypeOf(int64(0))
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
float64Type = reflect.TypeOf(float64(0))
|
||||
stringType = reflect.TypeOf("")
|
||||
signatureType = reflect.TypeOf(Signature{""})
|
||||
objectPathType = reflect.TypeOf(ObjectPath(""))
|
||||
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
||||
interfacesType = reflect.TypeOf([]interface{}{})
|
||||
interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||
unixFDType = reflect.TypeOf(UnixFD(0))
|
||||
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
||||
errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
)
|
||||
|
||||
// An InvalidTypeError signals that a value which cannot be represented in the
|
||||
// D-Bus wire format was passed to a function.
|
||||
type InvalidTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e InvalidTypeError) Error() string {
|
||||
return "dbus: invalid type " + e.Type.String()
|
||||
}
|
||||
|
||||
// Store copies the values contained in src to dest, which must be a slice of
|
||||
// pointers. It converts slices of interfaces from src to corresponding structs
|
||||
// in dest. An error is returned if the lengths of src and dest or the types of
|
||||
// their elements don't match.
|
||||
func Store(src []interface{}, dest ...interface{}) error {
|
||||
if len(src) != len(dest) {
|
||||
return errors.New("dbus.Store: length mismatch")
|
||||
}
|
||||
|
||||
for i := range src {
|
||||
if err := storeInterfaces(src[i], dest[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func storeInterfaces(src, dest interface{}) error {
|
||||
return store(reflect.ValueOf(dest), reflect.ValueOf(src))
|
||||
}
|
||||
|
||||
func store(dest, src reflect.Value) error {
|
||||
if dest.Kind() == reflect.Ptr {
|
||||
if dest.IsNil() {
|
||||
dest.Set(reflect.New(dest.Type().Elem()))
|
||||
}
|
||||
return store(dest.Elem(), src)
|
||||
}
|
||||
switch src.Kind() {
|
||||
case reflect.Slice:
|
||||
return storeSlice(dest, src)
|
||||
case reflect.Map:
|
||||
return storeMap(dest, src)
|
||||
default:
|
||||
return storeBase(dest, src)
|
||||
}
|
||||
}
|
||||
|
||||
func storeBase(dest, src reflect.Value) error {
|
||||
return setDest(dest, src)
|
||||
}
|
||||
|
||||
func setDest(dest, src reflect.Value) error {
|
||||
if !isVariant(src.Type()) && isVariant(dest.Type()) {
|
||||
//special conversion for dbus.Variant
|
||||
dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
|
||||
return nil
|
||||
}
|
||||
if isVariant(src.Type()) && !isVariant(dest.Type()) {
|
||||
src = getVariantValue(src)
|
||||
return store(dest, src)
|
||||
}
|
||||
if !src.Type().ConvertibleTo(dest.Type()) {
|
||||
return fmt.Errorf(
|
||||
"dbus.Store: type mismatch: cannot convert %s to %s",
|
||||
src.Type(), dest.Type())
|
||||
}
|
||||
dest.Set(src.Convert(dest.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func kindsAreCompatible(dest, src reflect.Type) bool {
|
||||
switch {
|
||||
case isVariant(dest):
|
||||
return true
|
||||
case dest.Kind() == reflect.Interface:
|
||||
return true
|
||||
default:
|
||||
return dest.Kind() == src.Kind()
|
||||
}
|
||||
}
|
||||
|
||||
func isConvertibleTo(dest, src reflect.Type) bool {
|
||||
switch {
|
||||
case isVariant(dest):
|
||||
return true
|
||||
case dest.Kind() == reflect.Interface:
|
||||
return true
|
||||
case dest.Kind() == reflect.Slice:
|
||||
return src.Kind() == reflect.Slice &&
|
||||
isConvertibleTo(dest.Elem(), src.Elem())
|
||||
case dest.Kind() == reflect.Ptr:
|
||||
dest = dest.Elem()
|
||||
return isConvertibleTo(dest, src)
|
||||
case dest.Kind() == reflect.Struct:
|
||||
return src == interfacesType || dest.Kind() == src.Kind()
|
||||
default:
|
||||
return src.ConvertibleTo(dest)
|
||||
}
|
||||
}
|
||||
|
||||
func storeMap(dest, src reflect.Value) error {
|
||||
switch {
|
||||
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||
return fmt.Errorf(
|
||||
"dbus.Store: type mismatch: "+
|
||||
"map: cannot store a value of %s into %s",
|
||||
src.Type(), dest.Type())
|
||||
case isVariant(dest.Type()):
|
||||
return storeMapIntoVariant(dest, src)
|
||||
case dest.Kind() == reflect.Interface:
|
||||
return storeMapIntoInterface(dest, src)
|
||||
case isConvertibleTo(dest.Type().Key(), src.Type().Key()) &&
|
||||
isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||
return storeMapIntoMap(dest, src)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"dbus.Store: type mismatch: "+
|
||||
"map: cannot convert a value of %s into %s",
|
||||
src.Type(), dest.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func storeMapIntoVariant(dest, src reflect.Value) error {
|
||||
dv := reflect.MakeMap(src.Type())
|
||||
err := store(dv, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storeBase(dest, dv)
|
||||
}
|
||||
|
||||
func storeMapIntoInterface(dest, src reflect.Value) error {
|
||||
var dv reflect.Value
|
||||
if isVariant(src.Type().Elem()) {
|
||||
//Convert variants to interface{} recursively when converting
|
||||
//to interface{}
|
||||
dv = reflect.MakeMap(
|
||||
reflect.MapOf(src.Type().Key(), interfaceType))
|
||||
} else {
|
||||
dv = reflect.MakeMap(src.Type())
|
||||
}
|
||||
err := store(dv, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storeBase(dest, dv)
|
||||
}
|
||||
|
||||
func storeMapIntoMap(dest, src reflect.Value) error {
|
||||
if dest.IsNil() {
|
||||
dest.Set(reflect.MakeMap(dest.Type()))
|
||||
}
|
||||
keys := src.MapKeys()
|
||||
for _, key := range keys {
|
||||
dkey := key.Convert(dest.Type().Key())
|
||||
dval := reflect.New(dest.Type().Elem()).Elem()
|
||||
err := store(dval, getVariantValue(src.MapIndex(key)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest.SetMapIndex(dkey, dval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func storeSlice(dest, src reflect.Value) error {
|
||||
switch {
|
||||
case src.Type() == interfacesType && dest.Kind() == reflect.Struct:
|
||||
//The decoder always decodes structs as slices of interface{}
|
||||
return storeStruct(dest, src)
|
||||
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||
return fmt.Errorf(
|
||||
"dbus.Store: type mismatch: "+
|
||||
"slice: cannot store a value of %s into %s",
|
||||
src.Type(), dest.Type())
|
||||
case isVariant(dest.Type()):
|
||||
return storeSliceIntoVariant(dest, src)
|
||||
case dest.Kind() == reflect.Interface:
|
||||
return storeSliceIntoInterface(dest, src)
|
||||
case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||
return storeSliceIntoSlice(dest, src)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"dbus.Store: type mismatch: "+
|
||||
"slice: cannot convert a value of %s into %s",
|
||||
src.Type(), dest.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func storeStruct(dest, src reflect.Value) error {
|
||||
if isVariant(dest.Type()) {
|
||||
return storeBase(dest, src)
|
||||
}
|
||||
dval := make([]interface{}, 0, dest.NumField())
|
||||
dtype := dest.Type()
|
||||
for i := 0; i < dest.NumField(); i++ {
|
||||
field := dest.Field(i)
|
||||
ftype := dtype.Field(i)
|
||||
if ftype.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
if ftype.Tag.Get("dbus") == "-" {
|
||||
continue
|
||||
}
|
||||
dval = append(dval, field.Addr().Interface())
|
||||
}
|
||||
if src.Len() != len(dval) {
|
||||
return fmt.Errorf(
|
||||
"dbus.Store: type mismatch: "+
|
||||
"destination struct does not have "+
|
||||
"enough fields need: %d have: %d",
|
||||
src.Len(), len(dval))
|
||||
}
|
||||
return Store(src.Interface().([]interface{}), dval...)
|
||||
}
|
||||
|
||||
func storeSliceIntoVariant(dest, src reflect.Value) error {
|
||||
dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||
err := store(dv, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storeBase(dest, dv)
|
||||
}
|
||||
|
||||
func storeSliceIntoInterface(dest, src reflect.Value) error {
|
||||
var dv reflect.Value
|
||||
if isVariant(src.Type().Elem()) {
|
||||
//Convert variants to interface{} recursively when converting
|
||||
//to interface{}
|
||||
dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
|
||||
src.Len(), src.Cap())
|
||||
} else {
|
||||
dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||
}
|
||||
err := store(dv, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storeBase(dest, dv)
|
||||
}
|
||||
|
||||
func storeSliceIntoSlice(dest, src reflect.Value) error {
|
||||
if dest.IsNil() || dest.Len() < src.Len() {
|
||||
dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
|
||||
} else if dest.Len() > src.Len() {
|
||||
dest.Set(dest.Slice(0, src.Len()))
|
||||
}
|
||||
for i := 0; i < src.Len(); i++ {
|
||||
err := store(dest.Index(i), getVariantValue(src.Index(i)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getVariantValue(in reflect.Value) reflect.Value {
|
||||
if isVariant(in.Type()) {
|
||||
return reflect.ValueOf(in.Interface().(Variant).Value())
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
func isVariant(t reflect.Type) bool {
|
||||
return t == variantType
|
||||
}
|
||||
|
||||
// An ObjectPath is an object path as defined by the D-Bus spec.
|
||||
type ObjectPath string
|
||||
|
||||
// IsValid returns whether the object path is valid.
|
||||
func (o ObjectPath) IsValid() bool {
|
||||
s := string(o)
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
if s[0] != '/' {
|
||||
return false
|
||||
}
|
||||
if s[len(s)-1] == '/' && len(s) != 1 {
|
||||
return false
|
||||
}
|
||||
// probably not used, but technically possible
|
||||
if s == "/" {
|
||||
return true
|
||||
}
|
||||
split := strings.Split(s[1:], "/")
|
||||
for _, v := range split {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, c := range v {
|
||||
if !isMemberChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
|
||||
// documentation for more information about Unix file descriptor passsing.
|
||||
type UnixFD int32
|
||||
|
||||
// A UnixFDIndex is the representation of a Unix file descriptor in a message.
|
||||
type UnixFDIndex uint32
|
||||
|
||||
// alignment returns the alignment of values of type t.
|
||||
func alignment(t reflect.Type) int {
|
||||
switch t {
|
||||
case variantType:
|
||||
return 1
|
||||
case objectPathType:
|
||||
return 4
|
||||
case signatureType:
|
||||
return 1
|
||||
case interfacesType:
|
||||
return 4
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Uint8:
|
||||
return 1
|
||||
case reflect.Uint16, reflect.Int16:
|
||||
return 2
|
||||
case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
||||
return 4
|
||||
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
||||
return 8
|
||||
case reflect.Ptr:
|
||||
return alignment(t.Elem())
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// isKeyType returns whether t is a valid type for a D-Bus dict.
|
||||
func isKeyType(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
||||
reflect.String, reflect.Uint, reflect.Int:
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isValidInterface returns whether s is a valid name for an interface.
|
||||
func isValidInterface(s string) bool {
|
||||
if len(s) == 0 || len(s) > 255 || s[0] == '.' {
|
||||
return false
|
||||
}
|
||||
elem := strings.Split(s, ".")
|
||||
if len(elem) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, v := range elem {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
}
|
||||
if v[0] >= '0' && v[0] <= '9' {
|
||||
return false
|
||||
}
|
||||
for _, c := range v {
|
||||
if !isMemberChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isValidMember returns whether s is a valid name for a member.
|
||||
func isValidMember(s string) bool {
|
||||
if len(s) == 0 || len(s) > 255 {
|
||||
return false
|
||||
}
|
||||
i := strings.Index(s, ".")
|
||||
if i != -1 {
|
||||
return false
|
||||
}
|
||||
if s[0] >= '0' && s[0] <= '9' {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
if !isMemberChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isMemberChar(c rune) bool {
|
||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') || c == '_'
|
||||
}
|
||||
292
vendor/github.com/godbus/dbus/v5/decoder.go
generated
vendored
Normal file
292
vendor/github.com/godbus/dbus/v5/decoder.go
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
in io.Reader
|
||||
order binary.ByteOrder
|
||||
pos int
|
||||
fds []int
|
||||
}
|
||||
|
||||
// newDecoder returns a new decoder that reads values from in. The input is
|
||||
// expected to be in the given byte order.
|
||||
func newDecoder(in io.Reader, order binary.ByteOrder, fds []int) *decoder {
|
||||
dec := new(decoder)
|
||||
dec.in = in
|
||||
dec.order = order
|
||||
dec.fds = fds
|
||||
return dec
|
||||
}
|
||||
|
||||
// align aligns the input to the given boundary and panics on error.
|
||||
func (dec *decoder) align(n int) {
|
||||
if dec.pos%n != 0 {
|
||||
newpos := (dec.pos + n - 1) & ^(n - 1)
|
||||
empty := make([]byte, newpos-dec.pos)
|
||||
if _, err := io.ReadFull(dec.in, empty); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos = newpos
|
||||
}
|
||||
}
|
||||
|
||||
// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
|
||||
func (dec *decoder) binread(v interface{}) {
|
||||
if err := binary.Read(dec.in, dec.order, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
|
||||
defer func() {
|
||||
var ok bool
|
||||
v := recover()
|
||||
if err, ok = v.(error); ok {
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
err = FormatError("unexpected EOF")
|
||||
}
|
||||
}
|
||||
}()
|
||||
vs = make([]interface{}, 0)
|
||||
s := sig.str
|
||||
for s != "" {
|
||||
err, rem := validSingle(s, &depthCounter{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := dec.decode(s[:len(s)-len(rem)], 0)
|
||||
vs = append(vs, v)
|
||||
s = rem
|
||||
}
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
func (dec *decoder) decode(s string, depth int) interface{} {
|
||||
dec.align(alignment(typeFor(s)))
|
||||
switch s[0] {
|
||||
case 'y':
|
||||
var b [1]byte
|
||||
if _, err := dec.in.Read(b[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos++
|
||||
return b[0]
|
||||
case 'b':
|
||||
i := dec.decode("u", depth).(uint32)
|
||||
switch {
|
||||
case i == 0:
|
||||
return false
|
||||
case i == 1:
|
||||
return true
|
||||
default:
|
||||
panic(FormatError("invalid value for boolean"))
|
||||
}
|
||||
case 'n':
|
||||
var i int16
|
||||
dec.binread(&i)
|
||||
dec.pos += 2
|
||||
return i
|
||||
case 'i':
|
||||
var i int32
|
||||
dec.binread(&i)
|
||||
dec.pos += 4
|
||||
return i
|
||||
case 'x':
|
||||
var i int64
|
||||
dec.binread(&i)
|
||||
dec.pos += 8
|
||||
return i
|
||||
case 'q':
|
||||
var i uint16
|
||||
dec.binread(&i)
|
||||
dec.pos += 2
|
||||
return i
|
||||
case 'u':
|
||||
var i uint32
|
||||
dec.binread(&i)
|
||||
dec.pos += 4
|
||||
return i
|
||||
case 't':
|
||||
var i uint64
|
||||
dec.binread(&i)
|
||||
dec.pos += 8
|
||||
return i
|
||||
case 'd':
|
||||
var f float64
|
||||
dec.binread(&f)
|
||||
dec.pos += 8
|
||||
return f
|
||||
case 's':
|
||||
length := dec.decode("u", depth).(uint32)
|
||||
b := make([]byte, int(length)+1)
|
||||
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos += int(length) + 1
|
||||
return string(b[:len(b)-1])
|
||||
case 'o':
|
||||
return ObjectPath(dec.decode("s", depth).(string))
|
||||
case 'g':
|
||||
length := dec.decode("y", depth).(byte)
|
||||
b := make([]byte, int(length)+1)
|
||||
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos += int(length) + 1
|
||||
sig, err := ParseSignature(string(b[:len(b)-1]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sig
|
||||
case 'v':
|
||||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
var variant Variant
|
||||
sig := dec.decode("g", depth).(Signature)
|
||||
if len(sig.str) == 0 {
|
||||
panic(FormatError("variant signature is empty"))
|
||||
}
|
||||
err, rem := validSingle(sig.str, &depthCounter{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if rem != "" {
|
||||
panic(FormatError("variant signature has multiple types"))
|
||||
}
|
||||
variant.sig = sig
|
||||
variant.value = dec.decode(sig.str, depth+1)
|
||||
return variant
|
||||
case 'h':
|
||||
idx := dec.decode("u", depth).(uint32)
|
||||
if int(idx) < len(dec.fds) {
|
||||
return UnixFD(dec.fds[idx])
|
||||
}
|
||||
return UnixFDIndex(idx)
|
||||
case 'a':
|
||||
if len(s) > 1 && s[1] == '{' {
|
||||
ksig := s[2:3]
|
||||
vsig := s[3 : len(s)-1]
|
||||
v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
|
||||
if depth >= 63 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
length := dec.decode("u", depth).(uint32)
|
||||
// Even for empty maps, the correct padding must be included
|
||||
dec.align(8)
|
||||
spos := dec.pos
|
||||
for dec.pos < spos+int(length) {
|
||||
dec.align(8)
|
||||
if !isKeyType(v.Type().Key()) {
|
||||
panic(InvalidTypeError{v.Type()})
|
||||
}
|
||||
kv := dec.decode(ksig, depth+2)
|
||||
vv := dec.decode(vsig, depth+2)
|
||||
v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
||||
}
|
||||
return v.Interface()
|
||||
}
|
||||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
sig := s[1:]
|
||||
length := dec.decode("u", depth).(uint32)
|
||||
// capacity can be determined only for fixed-size element types
|
||||
var capacity int
|
||||
if s := sigByteSize(sig); s != 0 {
|
||||
capacity = int(length) / s
|
||||
}
|
||||
v := reflect.MakeSlice(reflect.SliceOf(typeFor(sig)), 0, capacity)
|
||||
// Even for empty arrays, the correct padding must be included
|
||||
align := alignment(typeFor(s[1:]))
|
||||
if len(s) > 1 && s[1] == '(' {
|
||||
//Special case for arrays of structs
|
||||
//structs decode as a slice of interface{} values
|
||||
//but the dbus alignment does not match this
|
||||
align = 8
|
||||
}
|
||||
dec.align(align)
|
||||
spos := dec.pos
|
||||
for dec.pos < spos+int(length) {
|
||||
ev := dec.decode(s[1:], depth+1)
|
||||
v = reflect.Append(v, reflect.ValueOf(ev))
|
||||
}
|
||||
return v.Interface()
|
||||
case '(':
|
||||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
dec.align(8)
|
||||
v := make([]interface{}, 0)
|
||||
s = s[1 : len(s)-1]
|
||||
for s != "" {
|
||||
err, rem := validSingle(s, &depthCounter{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ev := dec.decode(s[:len(s)-len(rem)], depth+1)
|
||||
v = append(v, ev)
|
||||
s = rem
|
||||
}
|
||||
return v
|
||||
default:
|
||||
panic(SignatureError{Sig: s})
|
||||
}
|
||||
}
|
||||
|
||||
// sigByteSize tries to calculates size of the given signature in bytes.
|
||||
//
|
||||
// It returns zero when it can't, for example when it contains non-fixed size
|
||||
// types such as strings, maps and arrays that require reading of the transmitted
|
||||
// data, for that we would need to implement the unread method for Decoder first.
|
||||
func sigByteSize(sig string) int {
|
||||
var total int
|
||||
for offset := 0; offset < len(sig); {
|
||||
switch sig[offset] {
|
||||
case 'y':
|
||||
total += 1
|
||||
offset += 1
|
||||
case 'n', 'q':
|
||||
total += 2
|
||||
offset += 1
|
||||
case 'b', 'i', 'u', 'h':
|
||||
total += 4
|
||||
offset += 1
|
||||
case 'x', 't', 'd':
|
||||
total += 8
|
||||
offset += 1
|
||||
case '(':
|
||||
i := 1
|
||||
depth := 1
|
||||
for i < len(sig[offset:]) && depth != 0 {
|
||||
if sig[offset+i] == '(' {
|
||||
depth++
|
||||
} else if sig[offset+i] == ')' {
|
||||
depth--
|
||||
}
|
||||
i++
|
||||
}
|
||||
s := sigByteSize(sig[offset+1 : offset+i-1])
|
||||
if s == 0 {
|
||||
return 0
|
||||
}
|
||||
total += s
|
||||
offset += i
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// A FormatError is an error in the wire format.
|
||||
type FormatError string
|
||||
|
||||
func (e FormatError) Error() string {
|
||||
return "dbus: wire format error: " + string(e)
|
||||
}
|
||||
342
vendor/github.com/godbus/dbus/v5/default_handler.go
generated
vendored
Normal file
342
vendor/github.com/godbus/dbus/v5/default_handler.go
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func newIntrospectIntf(h *defaultHandler) *exportedIntf {
|
||||
methods := make(map[string]Method)
|
||||
methods["Introspect"] = exportedMethod{
|
||||
reflect.ValueOf(func(msg Message) (string, *Error) {
|
||||
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||
return h.introspectPath(path), nil
|
||||
}),
|
||||
}
|
||||
return newExportedIntf(methods, true)
|
||||
}
|
||||
|
||||
//NewDefaultHandler returns an instance of the default
|
||||
//call handler. This is useful if you want to implement only
|
||||
//one of the two handlers but not both.
|
||||
//
|
||||
// Deprecated: this is the default value, don't use it, it will be unexported.
|
||||
func NewDefaultHandler() *defaultHandler {
|
||||
h := &defaultHandler{
|
||||
objects: make(map[ObjectPath]*exportedObj),
|
||||
defaultIntf: make(map[string]*exportedIntf),
|
||||
}
|
||||
h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
|
||||
return h
|
||||
}
|
||||
|
||||
type defaultHandler struct {
|
||||
sync.RWMutex
|
||||
objects map[ObjectPath]*exportedObj
|
||||
defaultIntf map[string]*exportedIntf
|
||||
}
|
||||
|
||||
func (h *defaultHandler) PathExists(path ObjectPath) bool {
|
||||
_, ok := h.objects[path]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (h *defaultHandler) introspectPath(path ObjectPath) string {
|
||||
subpath := make(map[string]struct{})
|
||||
var xml bytes.Buffer
|
||||
xml.WriteString("<node>")
|
||||
for obj := range h.objects {
|
||||
p := string(path)
|
||||
if p != "/" {
|
||||
p += "/"
|
||||
}
|
||||
if strings.HasPrefix(string(obj), p) {
|
||||
node_name := strings.Split(string(obj[len(p):]), "/")[0]
|
||||
subpath[node_name] = struct{}{}
|
||||
}
|
||||
}
|
||||
for s := range subpath {
|
||||
xml.WriteString("\n\t<node name=\"" + s + "\"/>")
|
||||
}
|
||||
xml.WriteString("\n</node>")
|
||||
return xml.String()
|
||||
}
|
||||
|
||||
func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
|
||||
h.RLock()
|
||||
defer h.RUnlock()
|
||||
object, ok := h.objects[path]
|
||||
if ok {
|
||||
return object, ok
|
||||
}
|
||||
|
||||
// If an object wasn't found for this exact path,
|
||||
// look for a matching subtree registration
|
||||
subtreeObject := newExportedObject()
|
||||
path = path[:strings.LastIndex(string(path), "/")]
|
||||
for len(path) > 0 {
|
||||
object, ok = h.objects[path]
|
||||
if ok {
|
||||
for name, iface := range object.interfaces {
|
||||
// Only include this handler if it registered for the subtree
|
||||
if iface.isFallbackInterface() {
|
||||
subtreeObject.interfaces[name] = iface
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
path = path[:strings.LastIndex(string(path), "/")]
|
||||
}
|
||||
|
||||
for name, intf := range h.defaultIntf {
|
||||
if _, exists := subtreeObject.interfaces[name]; exists {
|
||||
continue
|
||||
}
|
||||
subtreeObject.interfaces[name] = intf
|
||||
}
|
||||
|
||||
return subtreeObject, true
|
||||
}
|
||||
|
||||
func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
|
||||
h.Lock()
|
||||
h.objects[path] = object
|
||||
h.Unlock()
|
||||
}
|
||||
|
||||
func (h *defaultHandler) DeleteObject(path ObjectPath) {
|
||||
h.Lock()
|
||||
delete(h.objects, path)
|
||||
h.Unlock()
|
||||
}
|
||||
|
||||
type exportedMethod struct {
|
||||
reflect.Value
|
||||
}
|
||||
|
||||
func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
|
||||
t := m.Type()
|
||||
|
||||
params := make([]reflect.Value, len(args))
|
||||
for i := 0; i < len(args); i++ {
|
||||
params[i] = reflect.ValueOf(args[i]).Elem()
|
||||
}
|
||||
|
||||
ret := m.Value.Call(params)
|
||||
var err error
|
||||
nilErr := false // The reflection will find almost-nils, let's only pass back clean ones!
|
||||
if t.NumOut() > 0 {
|
||||
if e, ok := ret[t.NumOut()-1].Interface().(*Error); ok { // godbus *Error
|
||||
nilErr = ret[t.NumOut()-1].IsNil()
|
||||
ret = ret[:t.NumOut()-1]
|
||||
err = e
|
||||
} else if ret[t.NumOut()-1].Type().Implements(errType) { // Go error
|
||||
i := ret[t.NumOut()-1].Interface()
|
||||
if i == nil {
|
||||
nilErr = ret[t.NumOut()-1].IsNil()
|
||||
} else {
|
||||
err = i.(error)
|
||||
}
|
||||
ret = ret[:t.NumOut()-1]
|
||||
}
|
||||
}
|
||||
out := make([]interface{}, len(ret))
|
||||
for i, val := range ret {
|
||||
out[i] = val.Interface()
|
||||
}
|
||||
if nilErr || err == nil {
|
||||
//concrete type to interface nil is a special case
|
||||
return out, nil
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
func (m exportedMethod) NumArguments() int {
|
||||
return m.Value.Type().NumIn()
|
||||
}
|
||||
|
||||
func (m exportedMethod) ArgumentValue(i int) interface{} {
|
||||
return reflect.Zero(m.Type().In(i)).Interface()
|
||||
}
|
||||
|
||||
func (m exportedMethod) NumReturns() int {
|
||||
return m.Value.Type().NumOut()
|
||||
}
|
||||
|
||||
func (m exportedMethod) ReturnValue(i int) interface{} {
|
||||
return reflect.Zero(m.Type().Out(i)).Interface()
|
||||
}
|
||||
|
||||
func newExportedObject() *exportedObj {
|
||||
return &exportedObj{
|
||||
interfaces: make(map[string]*exportedIntf),
|
||||
}
|
||||
}
|
||||
|
||||
type exportedObj struct {
|
||||
mu sync.RWMutex
|
||||
interfaces map[string]*exportedIntf
|
||||
}
|
||||
|
||||
func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
|
||||
if name == "" {
|
||||
return obj, true
|
||||
}
|
||||
obj.mu.RLock()
|
||||
defer obj.mu.RUnlock()
|
||||
intf, exists := obj.interfaces[name]
|
||||
return intf, exists
|
||||
}
|
||||
|
||||
func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
|
||||
obj.mu.Lock()
|
||||
defer obj.mu.Unlock()
|
||||
obj.interfaces[name] = iface
|
||||
}
|
||||
|
||||
func (obj *exportedObj) DeleteInterface(name string) {
|
||||
obj.mu.Lock()
|
||||
defer obj.mu.Unlock()
|
||||
delete(obj.interfaces, name)
|
||||
}
|
||||
|
||||
func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
|
||||
obj.mu.RLock()
|
||||
defer obj.mu.RUnlock()
|
||||
for _, intf := range obj.interfaces {
|
||||
method, exists := intf.LookupMethod(name)
|
||||
if exists {
|
||||
return method, exists
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (obj *exportedObj) isFallbackInterface() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
|
||||
return &exportedIntf{
|
||||
methods: methods,
|
||||
includeSubtree: includeSubtree,
|
||||
}
|
||||
}
|
||||
|
||||
type exportedIntf struct {
|
||||
methods map[string]Method
|
||||
|
||||
// Whether or not this export is for the entire subtree
|
||||
includeSubtree bool
|
||||
}
|
||||
|
||||
func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
|
||||
out, exists := obj.methods[name]
|
||||
return out, exists
|
||||
}
|
||||
|
||||
func (obj *exportedIntf) isFallbackInterface() bool {
|
||||
return obj.includeSubtree
|
||||
}
|
||||
|
||||
//NewDefaultSignalHandler returns an instance of the default
|
||||
//signal handler. This is useful if you want to implement only
|
||||
//one of the two handlers but not both.
|
||||
//
|
||||
// Deprecated: this is the default value, don't use it, it will be unexported.
|
||||
func NewDefaultSignalHandler() *defaultSignalHandler {
|
||||
return &defaultSignalHandler{}
|
||||
}
|
||||
|
||||
type defaultSignalHandler struct {
|
||||
mu sync.RWMutex
|
||||
closed bool
|
||||
signals []*signalChannelData
|
||||
}
|
||||
|
||||
func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
|
||||
sh.mu.RLock()
|
||||
defer sh.mu.RUnlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
for _, scd := range sh.signals {
|
||||
scd.deliver(signal)
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *defaultSignalHandler) Terminate() {
|
||||
sh.mu.Lock()
|
||||
defer sh.mu.Unlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
|
||||
for _, scd := range sh.signals {
|
||||
scd.close()
|
||||
close(scd.ch)
|
||||
}
|
||||
sh.closed = true
|
||||
sh.signals = nil
|
||||
}
|
||||
|
||||
func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) {
|
||||
sh.mu.Lock()
|
||||
defer sh.mu.Unlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
sh.signals = append(sh.signals, &signalChannelData{
|
||||
ch: ch,
|
||||
done: make(chan struct{}),
|
||||
})
|
||||
}
|
||||
|
||||
func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) {
|
||||
sh.mu.Lock()
|
||||
defer sh.mu.Unlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
for i := len(sh.signals) - 1; i >= 0; i-- {
|
||||
if ch == sh.signals[i].ch {
|
||||
sh.signals[i].close()
|
||||
copy(sh.signals[i:], sh.signals[i+1:])
|
||||
sh.signals[len(sh.signals)-1] = nil
|
||||
sh.signals = sh.signals[:len(sh.signals)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type signalChannelData struct {
|
||||
wg sync.WaitGroup
|
||||
ch chan<- *Signal
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (scd *signalChannelData) deliver(signal *Signal) {
|
||||
select {
|
||||
case scd.ch <- signal:
|
||||
case <-scd.done:
|
||||
return
|
||||
default:
|
||||
scd.wg.Add(1)
|
||||
go scd.deferredDeliver(signal)
|
||||
}
|
||||
}
|
||||
|
||||
func (scd *signalChannelData) deferredDeliver(signal *Signal) {
|
||||
select {
|
||||
case scd.ch <- signal:
|
||||
case <-scd.done:
|
||||
}
|
||||
scd.wg.Done()
|
||||
}
|
||||
|
||||
func (scd *signalChannelData) close() {
|
||||
close(scd.done)
|
||||
scd.wg.Wait() // wait until all spawned goroutines return
|
||||
}
|
||||
71
vendor/github.com/godbus/dbus/v5/doc.go
generated
vendored
Normal file
71
vendor/github.com/godbus/dbus/v5/doc.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Package dbus implements bindings to the D-Bus message bus system.
|
||||
|
||||
To use the message bus API, you first need to connect to a bus (usually the
|
||||
session or system bus). The acquired connection then can be used to call methods
|
||||
on remote objects and emit or receive signals. Using the Export method, you can
|
||||
arrange D-Bus methods calls to be directly translated to method calls on a Go
|
||||
value.
|
||||
|
||||
Conversion Rules
|
||||
|
||||
For outgoing messages, Go types are automatically converted to the
|
||||
corresponding D-Bus types. See the official specification at
|
||||
https://dbus.freedesktop.org/doc/dbus-specification.html#type-system for more
|
||||
information on the D-Bus type system. The following types are directly encoded
|
||||
as their respective D-Bus equivalents:
|
||||
|
||||
Go type | D-Bus type
|
||||
------------+-----------
|
||||
byte | BYTE
|
||||
bool | BOOLEAN
|
||||
int16 | INT16
|
||||
uint16 | UINT16
|
||||
int | INT32
|
||||
uint | UINT32
|
||||
int32 | INT32
|
||||
uint32 | UINT32
|
||||
int64 | INT64
|
||||
uint64 | UINT64
|
||||
float64 | DOUBLE
|
||||
string | STRING
|
||||
ObjectPath | OBJECT_PATH
|
||||
Signature | SIGNATURE
|
||||
Variant | VARIANT
|
||||
interface{} | VARIANT
|
||||
UnixFDIndex | UNIX_FD
|
||||
|
||||
Slices and arrays encode as ARRAYs of their element type.
|
||||
|
||||
Maps encode as DICTs, provided that their key type can be used as a key for
|
||||
a DICT.
|
||||
|
||||
Structs other than Variant and Signature encode as a STRUCT containing their
|
||||
exported fields in order. Fields whose tags contain `dbus:"-"` and unexported
|
||||
fields will be skipped.
|
||||
|
||||
Pointers encode as the value they're pointed to.
|
||||
|
||||
Types convertible to one of the base types above will be mapped as the
|
||||
base type.
|
||||
|
||||
Trying to encode any other type or a slice, map or struct containing an
|
||||
unsupported type will result in an InvalidTypeError.
|
||||
|
||||
For incoming messages, the inverse of these rules are used, with the exception
|
||||
of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces
|
||||
containing the struct fields in the correct order. The Store function can be
|
||||
used to convert such values to Go structs.
|
||||
|
||||
Unix FD passing
|
||||
|
||||
Handling Unix file descriptors deserves special mention. To use them, you should
|
||||
first check that they are supported on a connection by calling SupportsUnixFDs.
|
||||
If it returns true, all method of Connection will translate messages containing
|
||||
UnixFD's to messages that are accompanied by the given file descriptors with the
|
||||
UnixFD values being substituted by the correct indices. Similarly, the indices
|
||||
of incoming messages are automatically resolved. It shouldn't be necessary to use
|
||||
UnixFDIndex.
|
||||
|
||||
*/
|
||||
package dbus
|
||||
235
vendor/github.com/godbus/dbus/v5/encoder.go
generated
vendored
Normal file
235
vendor/github.com/godbus/dbus/v5/encoder.go
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// An encoder encodes values to the D-Bus wire format.
|
||||
type encoder struct {
|
||||
out io.Writer
|
||||
fds []int
|
||||
order binary.ByteOrder
|
||||
pos int
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to out in the given byte order.
|
||||
func newEncoder(out io.Writer, order binary.ByteOrder, fds []int) *encoder {
|
||||
enc := newEncoderAtOffset(out, 0, order, fds)
|
||||
return enc
|
||||
}
|
||||
|
||||
// newEncoderAtOffset returns a new encoder that writes to out in the given
|
||||
// byte order. Specify the offset to initialize pos for proper alignment
|
||||
// computation.
|
||||
func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder, fds []int) *encoder {
|
||||
enc := new(encoder)
|
||||
enc.out = out
|
||||
enc.order = order
|
||||
enc.pos = offset
|
||||
enc.fds = fds
|
||||
return enc
|
||||
}
|
||||
|
||||
// Aligns the next output to be on a multiple of n. Panics on write errors.
|
||||
func (enc *encoder) align(n int) {
|
||||
pad := enc.padding(0, n)
|
||||
if pad > 0 {
|
||||
empty := make([]byte, pad)
|
||||
if _, err := enc.out.Write(empty); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += pad
|
||||
}
|
||||
}
|
||||
|
||||
// pad returns the number of bytes of padding, based on current position and additional offset.
|
||||
// and alignment.
|
||||
func (enc *encoder) padding(offset, algn int) int {
|
||||
abs := enc.pos + offset
|
||||
if abs%algn != 0 {
|
||||
newabs := (abs + algn - 1) & ^(algn - 1)
|
||||
return newabs - abs
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
|
||||
func (enc *encoder) binwrite(v interface{}) {
|
||||
if err := binary.Write(enc.out, enc.order, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Encode encodes the given values to the underlying reader. All written values
|
||||
// are aligned properly as required by the D-Bus spec.
|
||||
func (enc *encoder) Encode(vs ...interface{}) (err error) {
|
||||
defer func() {
|
||||
err, _ = recover().(error)
|
||||
}()
|
||||
for _, v := range vs {
|
||||
enc.encode(reflect.ValueOf(v), 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encode encodes the given value to the writer and panics on error. depth holds
|
||||
// the depth of the container nesting.
|
||||
func (enc *encoder) encode(v reflect.Value, depth int) {
|
||||
if depth > 64 {
|
||||
panic(FormatError("input exceeds depth limitation"))
|
||||
}
|
||||
enc.align(alignment(v.Type()))
|
||||
switch v.Kind() {
|
||||
case reflect.Uint8:
|
||||
var b [1]byte
|
||||
b[0] = byte(v.Uint())
|
||||
if _, err := enc.out.Write(b[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos++
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
enc.encode(reflect.ValueOf(uint32(1)), depth)
|
||||
} else {
|
||||
enc.encode(reflect.ValueOf(uint32(0)), depth)
|
||||
}
|
||||
case reflect.Int16:
|
||||
enc.binwrite(int16(v.Int()))
|
||||
enc.pos += 2
|
||||
case reflect.Uint16:
|
||||
enc.binwrite(uint16(v.Uint()))
|
||||
enc.pos += 2
|
||||
case reflect.Int, reflect.Int32:
|
||||
if v.Type() == unixFDType {
|
||||
fd := v.Int()
|
||||
idx := len(enc.fds)
|
||||
enc.fds = append(enc.fds, int(fd))
|
||||
enc.binwrite(uint32(idx))
|
||||
} else {
|
||||
enc.binwrite(int32(v.Int()))
|
||||
}
|
||||
enc.pos += 4
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
enc.binwrite(uint32(v.Uint()))
|
||||
enc.pos += 4
|
||||
case reflect.Int64:
|
||||
enc.binwrite(v.Int())
|
||||
enc.pos += 8
|
||||
case reflect.Uint64:
|
||||
enc.binwrite(v.Uint())
|
||||
enc.pos += 8
|
||||
case reflect.Float64:
|
||||
enc.binwrite(v.Float())
|
||||
enc.pos += 8
|
||||
case reflect.String:
|
||||
str := v.String()
|
||||
if !utf8.ValidString(str) {
|
||||
panic(FormatError("input has a not-utf8 char in string"))
|
||||
}
|
||||
if strings.IndexByte(str, byte(0)) != -1 {
|
||||
panic(FormatError("input has a null char('\\000') in string"))
|
||||
}
|
||||
if v.Type() == objectPathType {
|
||||
if !ObjectPath(str).IsValid() {
|
||||
panic(FormatError("invalid object path"))
|
||||
}
|
||||
}
|
||||
enc.encode(reflect.ValueOf(uint32(len(str))), depth)
|
||||
b := make([]byte, v.Len()+1)
|
||||
copy(b, str)
|
||||
b[len(b)-1] = 0
|
||||
n, err := enc.out.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += n
|
||||
case reflect.Ptr:
|
||||
enc.encode(v.Elem(), depth)
|
||||
case reflect.Slice, reflect.Array:
|
||||
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||
// plus alignment for elements.
|
||||
n := enc.padding(0, 4) + 4
|
||||
offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem()))
|
||||
|
||||
var buf bytes.Buffer
|
||||
bufenc := newEncoderAtOffset(&buf, offset, enc.order, enc.fds)
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
bufenc.encode(v.Index(i), depth+1)
|
||||
}
|
||||
|
||||
if buf.Len() > 1<<26 {
|
||||
panic(FormatError("input exceeds array size limitation"))
|
||||
}
|
||||
|
||||
enc.fds = bufenc.fds
|
||||
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||
length := buf.Len()
|
||||
enc.align(alignment(v.Type().Elem()))
|
||||
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += length
|
||||
case reflect.Struct:
|
||||
switch t := v.Type(); t {
|
||||
case signatureType:
|
||||
str := v.Field(0)
|
||||
enc.encode(reflect.ValueOf(byte(str.Len())), depth)
|
||||
b := make([]byte, str.Len()+1)
|
||||
copy(b, str.String())
|
||||
b[len(b)-1] = 0
|
||||
n, err := enc.out.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += n
|
||||
case variantType:
|
||||
variant := v.Interface().(Variant)
|
||||
enc.encode(reflect.ValueOf(variant.sig), depth+1)
|
||||
enc.encode(reflect.ValueOf(variant.value), depth+1)
|
||||
default:
|
||||
for i := 0; i < v.Type().NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||
enc.encode(v.Field(i), depth+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
// Maps are arrays of structures, so they actually increase the depth by
|
||||
// 2.
|
||||
if !isKeyType(v.Type().Key()) {
|
||||
panic(InvalidTypeError{v.Type()})
|
||||
}
|
||||
keys := v.MapKeys()
|
||||
// Lookahead offset: 4 bytes for uint32 length (with alignment),
|
||||
// plus 8-byte alignment
|
||||
n := enc.padding(0, 4) + 4
|
||||
offset := enc.pos + n + enc.padding(n, 8)
|
||||
|
||||
var buf bytes.Buffer
|
||||
bufenc := newEncoderAtOffset(&buf, offset, enc.order, enc.fds)
|
||||
for _, k := range keys {
|
||||
bufenc.align(8)
|
||||
bufenc.encode(k, depth+2)
|
||||
bufenc.encode(v.MapIndex(k), depth+2)
|
||||
}
|
||||
enc.fds = bufenc.fds
|
||||
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||
length := buf.Len()
|
||||
enc.align(8)
|
||||
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += length
|
||||
case reflect.Interface:
|
||||
enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth)
|
||||
default:
|
||||
panic(InvalidTypeError{v.Type()})
|
||||
}
|
||||
}
|
||||
84
vendor/github.com/godbus/dbus/v5/escape.go
generated
vendored
Normal file
84
vendor/github.com/godbus/dbus/v5/escape.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package dbus
|
||||
|
||||
import "net/url"
|
||||
|
||||
// EscapeBusAddressValue implements a requirement to escape the values
|
||||
// in D-Bus server addresses, as defined by the D-Bus specification at
|
||||
// https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
|
||||
func EscapeBusAddressValue(val string) string {
|
||||
toEsc := strNeedsEscape(val)
|
||||
if toEsc == 0 {
|
||||
// Avoid unneeded allocation/copying.
|
||||
return val
|
||||
}
|
||||
|
||||
// Avoid allocation for short paths.
|
||||
var buf [64]byte
|
||||
var out []byte
|
||||
// Every to-be-escaped byte needs 2 extra bytes.
|
||||
required := len(val) + 2*toEsc
|
||||
if required <= len(buf) {
|
||||
out = buf[:required]
|
||||
} else {
|
||||
out = make([]byte, required)
|
||||
}
|
||||
|
||||
j := 0
|
||||
for i := 0; i < len(val); i++ {
|
||||
if ch := val[i]; needsEscape(ch) {
|
||||
// Convert ch to %xx, where xx is hex value.
|
||||
out[j] = '%'
|
||||
out[j+1] = hexchar(ch >> 4)
|
||||
out[j+2] = hexchar(ch & 0x0F)
|
||||
j += 3
|
||||
} else {
|
||||
out[j] = ch
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// UnescapeBusAddressValue unescapes values in D-Bus server addresses,
|
||||
// as defined by the D-Bus specification at
|
||||
// https://dbus.freedesktop.org/doc/dbus-specification.html#addresses.
|
||||
func UnescapeBusAddressValue(val string) (string, error) {
|
||||
// Looks like url.PathUnescape does exactly what is required.
|
||||
return url.PathUnescape(val)
|
||||
}
|
||||
|
||||
// hexchar returns an octal representation of a n, where n < 16.
|
||||
// For invalid values of n, the function panics.
|
||||
func hexchar(n byte) byte {
|
||||
const hex = "0123456789abcdef"
|
||||
|
||||
// For n >= len(hex), runtime will panic.
|
||||
return hex[n]
|
||||
}
|
||||
|
||||
// needsEscape tells if a byte is NOT one of optionally-escaped bytes.
|
||||
func needsEscape(c byte) bool {
|
||||
if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
|
||||
return false
|
||||
}
|
||||
switch c {
|
||||
case '-', '_', '/', '\\', '.', '*':
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// strNeedsEscape tells how many bytes in the string need escaping.
|
||||
func strNeedsEscape(val string) int {
|
||||
count := 0
|
||||
|
||||
for i := 0; i < len(val); i++ {
|
||||
if needsEscape(val[i]) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
463
vendor/github.com/godbus/dbus/v5/export.go
generated
vendored
Normal file
463
vendor/github.com/godbus/dbus/v5/export.go
generated
vendored
Normal file
@@ -0,0 +1,463 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMsgInvalidArg = Error{
|
||||
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||
[]interface{}{"Invalid type / number of args"},
|
||||
}
|
||||
ErrMsgNoObject = Error{
|
||||
"org.freedesktop.DBus.Error.NoSuchObject",
|
||||
[]interface{}{"No such object"},
|
||||
}
|
||||
ErrMsgUnknownMethod = Error{
|
||||
"org.freedesktop.DBus.Error.UnknownMethod",
|
||||
[]interface{}{"Unknown / invalid method"},
|
||||
}
|
||||
ErrMsgUnknownInterface = Error{
|
||||
"org.freedesktop.DBus.Error.UnknownInterface",
|
||||
[]interface{}{"Object does not implement the interface"},
|
||||
}
|
||||
)
|
||||
|
||||
func MakeNoObjectError(path ObjectPath) Error {
|
||||
return Error{
|
||||
"org.freedesktop.DBus.Error.NoSuchObject",
|
||||
[]interface{}{fmt.Sprintf("No such object '%s'", string(path))},
|
||||
}
|
||||
}
|
||||
|
||||
func MakeUnknownMethodError(methodName string) Error {
|
||||
return Error{
|
||||
"org.freedesktop.DBus.Error.UnknownMethod",
|
||||
[]interface{}{fmt.Sprintf("Unknown / invalid method '%s'", methodName)},
|
||||
}
|
||||
}
|
||||
|
||||
func MakeUnknownInterfaceError(ifaceName string) Error {
|
||||
return Error{
|
||||
"org.freedesktop.DBus.Error.UnknownInterface",
|
||||
[]interface{}{fmt.Sprintf("Object does not implement the interface '%s'", ifaceName)},
|
||||
}
|
||||
}
|
||||
|
||||
func MakeFailedError(err error) *Error {
|
||||
return &Error{
|
||||
"org.freedesktop.DBus.Error.Failed",
|
||||
[]interface{}{err.Error()},
|
||||
}
|
||||
}
|
||||
|
||||
// Sender is a type which can be used in exported methods to receive the message
|
||||
// sender.
|
||||
type Sender string
|
||||
|
||||
func computeMethodName(name string, mapping map[string]string) string {
|
||||
newname, ok := mapping[name]
|
||||
if ok {
|
||||
name = newname
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
methods := make(map[string]reflect.Value)
|
||||
val := reflect.ValueOf(in)
|
||||
typ := val.Type()
|
||||
for i := 0; i < typ.NumMethod(); i++ {
|
||||
methtype := typ.Method(i)
|
||||
method := val.Method(i)
|
||||
t := method.Type()
|
||||
// only track valid methods must return *Error as last arg
|
||||
// and must be exported
|
||||
if t.NumOut() == 0 ||
|
||||
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) ||
|
||||
methtype.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
// map names while building table
|
||||
methods[computeMethodName(methtype.Name, mapping)] = method
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
func getAllMethods(in interface{}, mapping map[string]string) map[string]reflect.Value {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
methods := make(map[string]reflect.Value)
|
||||
val := reflect.ValueOf(in)
|
||||
typ := val.Type()
|
||||
for i := 0; i < typ.NumMethod(); i++ {
|
||||
methtype := typ.Method(i)
|
||||
method := val.Method(i)
|
||||
// map names while building table
|
||||
methods[computeMethodName(methtype.Name, mapping)] = method
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
|
||||
pointers := make([]interface{}, m.NumArguments())
|
||||
decode := make([]interface{}, 0, len(body))
|
||||
|
||||
for i := 0; i < m.NumArguments(); i++ {
|
||||
tp := reflect.TypeOf(m.ArgumentValue(i))
|
||||
val := reflect.New(tp)
|
||||
pointers[i] = val.Interface()
|
||||
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
||||
val.Elem().SetString(sender)
|
||||
} else if tp == reflect.TypeOf((*Message)(nil)).Elem() {
|
||||
val.Elem().Set(reflect.ValueOf(*msg))
|
||||
} else {
|
||||
decode = append(decode, pointers[i])
|
||||
}
|
||||
}
|
||||
|
||||
if len(decode) != len(body) {
|
||||
return nil, ErrMsgInvalidArg
|
||||
}
|
||||
|
||||
if err := Store(body, decode...); err != nil {
|
||||
return nil, ErrMsgInvalidArg
|
||||
}
|
||||
|
||||
return pointers, nil
|
||||
}
|
||||
|
||||
func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
|
||||
if decoder, ok := m.(ArgumentDecoder); ok {
|
||||
return decoder.DecodeArguments(conn, sender, msg, msg.Body)
|
||||
}
|
||||
return standardMethodArgumentDecode(m, sender, msg, msg.Body)
|
||||
}
|
||||
|
||||
// handleCall handles the given method call (i.e. looks if it's one of the
|
||||
// pre-implemented ones and searches for a corresponding handler if not).
|
||||
func (conn *Conn) handleCall(msg *Message) {
|
||||
name := msg.Headers[FieldMember].value.(string)
|
||||
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||
ifaceName, _ := msg.Headers[FieldInterface].value.(string)
|
||||
sender, hasSender := msg.Headers[FieldSender].value.(string)
|
||||
serial := msg.serial
|
||||
|
||||
if len(name) == 0 {
|
||||
conn.sendError(ErrMsgUnknownMethod, sender, serial)
|
||||
}
|
||||
|
||||
if ifaceName == "org.freedesktop.DBus.Peer" {
|
||||
switch name {
|
||||
case "Ping":
|
||||
conn.sendReply(sender, serial)
|
||||
case "GetMachineId":
|
||||
conn.sendReply(sender, serial, conn.uuid)
|
||||
default:
|
||||
conn.sendError(MakeUnknownMethodError(name), sender, serial)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
object, ok := conn.handler.LookupObject(path)
|
||||
if !ok {
|
||||
conn.sendError(MakeNoObjectError(path), sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
iface, exists := object.LookupInterface(ifaceName)
|
||||
if !exists {
|
||||
conn.sendError(MakeUnknownInterfaceError(ifaceName), sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
m, exists := iface.LookupMethod(name)
|
||||
if !exists {
|
||||
conn.sendError(MakeUnknownMethodError(name), sender, serial)
|
||||
return
|
||||
}
|
||||
args, err := conn.decodeArguments(m, sender, msg)
|
||||
if err != nil {
|
||||
conn.sendError(err, sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
ret, err := m.Call(args...)
|
||||
if err != nil {
|
||||
conn.sendError(err, sender, serial)
|
||||
return
|
||||
}
|
||||
|
||||
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||
reply := new(Message)
|
||||
reply.Type = TypeMethodReply
|
||||
reply.Headers = make(map[HeaderField]Variant)
|
||||
if hasSender {
|
||||
reply.Headers[FieldDestination] = msg.Headers[FieldSender]
|
||||
}
|
||||
reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
|
||||
reply.Body = make([]interface{}, len(ret))
|
||||
for i := 0; i < len(ret); i++ {
|
||||
reply.Body[i] = ret[i]
|
||||
}
|
||||
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
|
||||
|
||||
if err := reply.IsValid(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "dbus: dropping invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err)
|
||||
} else {
|
||||
conn.sendMessageAndIfClosed(reply, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit emits the given signal on the message bus. The name parameter must be
|
||||
// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
|
||||
func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
|
||||
i := strings.LastIndex(name, ".")
|
||||
if i == -1 {
|
||||
return errors.New("dbus: invalid method name")
|
||||
}
|
||||
iface := name[:i]
|
||||
member := name[i+1:]
|
||||
msg := new(Message)
|
||||
msg.Type = TypeSignal
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||
msg.Headers[FieldMember] = MakeVariant(member)
|
||||
msg.Headers[FieldPath] = MakeVariant(path)
|
||||
msg.Body = values
|
||||
if len(values) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||
}
|
||||
if err := msg.IsValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var closed bool
|
||||
conn.sendMessageAndIfClosed(msg, func() {
|
||||
closed = true
|
||||
})
|
||||
if closed {
|
||||
return ErrClosed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Export registers the given value to be exported as an object on the
|
||||
// message bus.
|
||||
//
|
||||
// If a method call on the given path and interface is received, an exported
|
||||
// method with the same name is called with v as the receiver if the
|
||||
// parameters match and the last return value is of type *Error. If this
|
||||
// *Error is not nil, it is sent back to the caller as an error.
|
||||
// Otherwise, a method reply is sent with the other return values as its body.
|
||||
//
|
||||
// Any parameters with the special type Sender are set to the sender of the
|
||||
// dbus message when the method is called. Parameters of this type do not
|
||||
// contribute to the dbus signature of the method (i.e. the method is exposed
|
||||
// as if the parameters of type Sender were not there).
|
||||
//
|
||||
// Similarly, any parameters with the type Message are set to the raw message
|
||||
// received on the bus. Again, parameters of this type do not contribute to the
|
||||
// dbus signature of the method.
|
||||
//
|
||||
// Every method call is executed in a new goroutine, so the method may be called
|
||||
// in multiple goroutines at once.
|
||||
//
|
||||
// Method calls on the interface org.freedesktop.DBus.Peer will be automatically
|
||||
// handled for every object.
|
||||
//
|
||||
// Passing nil as the first parameter will cause conn to cease handling calls on
|
||||
// the given combination of path and interface.
|
||||
//
|
||||
// Export returns an error if path is not a valid path name.
|
||||
func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
|
||||
return conn.ExportWithMap(v, nil, path, iface)
|
||||
}
|
||||
|
||||
// ExportAll registers all exported methods defined by the given object on
|
||||
// the message bus.
|
||||
//
|
||||
// Unlike Export there is no requirement to have the last parameter as type
|
||||
// *Error. If you want to be able to return error then you can append an error
|
||||
// type parameter to your method signature. If the error returned is not nil,
|
||||
// it is sent back to the caller as an error. Otherwise, a method reply is
|
||||
// sent with the other return values as its body.
|
||||
func (conn *Conn) ExportAll(v interface{}, path ObjectPath, iface string) error {
|
||||
return conn.export(getAllMethods(v, nil), path, iface, false)
|
||||
}
|
||||
|
||||
// ExportWithMap works exactly like Export but provides the ability to remap
|
||||
// method names (e.g. export a lower-case method).
|
||||
//
|
||||
// The keys in the map are the real method names (exported on the struct), and
|
||||
// the values are the method names to be exported on DBus.
|
||||
func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||
return conn.export(getMethods(v, mapping), path, iface, false)
|
||||
}
|
||||
|
||||
// ExportSubtree works exactly like Export but registers the given value for
|
||||
// an entire subtree rather under the root path provided.
|
||||
//
|
||||
// In order to make this useful, one parameter in each of the value's exported
|
||||
// methods should be a Message, in which case it will contain the raw message
|
||||
// (allowing one to get access to the path that caused the method to be called).
|
||||
//
|
||||
// Note that more specific export paths take precedence over less specific. For
|
||||
// example, a method call using the ObjectPath /foo/bar/baz will call a method
|
||||
// exported on /foo/bar before a method exported on /foo.
|
||||
func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error {
|
||||
return conn.ExportSubtreeWithMap(v, nil, path, iface)
|
||||
}
|
||||
|
||||
// ExportSubtreeWithMap works exactly like ExportSubtree but provides the
|
||||
// ability to remap method names (e.g. export a lower-case method).
|
||||
//
|
||||
// The keys in the map are the real method names (exported on the struct), and
|
||||
// the values are the method names to be exported on DBus.
|
||||
func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error {
|
||||
return conn.export(getMethods(v, mapping), path, iface, true)
|
||||
}
|
||||
|
||||
// ExportMethodTable like Export registers the given methods as an object
|
||||
// on the message bus. Unlike Export the it uses a method table to define
|
||||
// the object instead of a native go object.
|
||||
//
|
||||
// The method table is a map from method name to function closure
|
||||
// representing the method. This allows an object exported on the bus to not
|
||||
// necessarily be a native go object. It can be useful for generating exposed
|
||||
// methods on the fly.
|
||||
//
|
||||
// Any non-function objects in the method table are ignored.
|
||||
func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
||||
return conn.exportMethodTable(methods, path, iface, false)
|
||||
}
|
||||
|
||||
// Like ExportSubtree, but with the same caveats as ExportMethodTable.
|
||||
func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error {
|
||||
return conn.exportMethodTable(methods, path, iface, true)
|
||||
}
|
||||
|
||||
func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error {
|
||||
var out map[string]reflect.Value
|
||||
if methods != nil {
|
||||
out = make(map[string]reflect.Value)
|
||||
for name, method := range methods {
|
||||
rval := reflect.ValueOf(method)
|
||||
if rval.Kind() != reflect.Func {
|
||||
continue
|
||||
}
|
||||
t := rval.Type()
|
||||
// only track valid methods must return *Error as last arg
|
||||
if t.NumOut() == 0 ||
|
||||
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) {
|
||||
continue
|
||||
}
|
||||
out[name] = rval
|
||||
}
|
||||
}
|
||||
return conn.export(out, path, iface, includeSubtree)
|
||||
}
|
||||
|
||||
func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error {
|
||||
if h.PathExists(path) {
|
||||
obj := h.objects[path]
|
||||
obj.DeleteInterface(iface)
|
||||
if len(obj.interfaces) == 0 {
|
||||
h.DeleteObject(path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// export is the worker function for all exports/registrations.
|
||||
func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
|
||||
h, ok := conn.handler.(*defaultHandler)
|
||||
if !ok {
|
||||
return fmt.Errorf(
|
||||
`dbus: export only allowed on the default handler. Received: %T"`,
|
||||
conn.handler)
|
||||
}
|
||||
|
||||
if !path.IsValid() {
|
||||
return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
|
||||
}
|
||||
|
||||
// Remove a previous export if the interface is nil
|
||||
if methods == nil {
|
||||
return conn.unexport(h, path, iface)
|
||||
}
|
||||
|
||||
// If this is the first handler for this path, make a new map to hold all
|
||||
// handlers for this path.
|
||||
if !h.PathExists(path) {
|
||||
h.AddObject(path, newExportedObject())
|
||||
}
|
||||
|
||||
exportedMethods := make(map[string]Method)
|
||||
for name, method := range methods {
|
||||
exportedMethods[name] = exportedMethod{method}
|
||||
}
|
||||
|
||||
// Finally, save this handler
|
||||
obj := h.objects[path]
|
||||
obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response.
|
||||
func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
|
||||
var r uint32
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ReleaseNameReply(r), nil
|
||||
}
|
||||
|
||||
// RequestName calls org.freedesktop.DBus.RequestName and awaits a response.
|
||||
func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
|
||||
var r uint32
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return RequestNameReply(r), nil
|
||||
}
|
||||
|
||||
// ReleaseNameReply is the reply to a ReleaseName call.
|
||||
type ReleaseNameReply uint32
|
||||
|
||||
const (
|
||||
ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
|
||||
ReleaseNameReplyNonExistent
|
||||
ReleaseNameReplyNotOwner
|
||||
)
|
||||
|
||||
// RequestNameFlags represents the possible flags for a RequestName call.
|
||||
type RequestNameFlags uint32
|
||||
|
||||
const (
|
||||
NameFlagAllowReplacement RequestNameFlags = 1 << iota
|
||||
NameFlagReplaceExisting
|
||||
NameFlagDoNotQueue
|
||||
)
|
||||
|
||||
// RequestNameReply is the reply to a RequestName call.
|
||||
type RequestNameReply uint32
|
||||
|
||||
const (
|
||||
RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
|
||||
RequestNameReplyInQueue
|
||||
RequestNameReplyExists
|
||||
RequestNameReplyAlreadyOwner
|
||||
)
|
||||
25
vendor/github.com/godbus/dbus/v5/homedir.go
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/v5/homedir.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
)
|
||||
|
||||
// Get returns the home directory of the current user, which is usually the
|
||||
// value of HOME environment variable. In case it is not set or empty, os/user
|
||||
// package is used.
|
||||
//
|
||||
// If linking statically with cgo enabled against glibc, make sure the
|
||||
// osusergo build tag is used.
|
||||
//
|
||||
// If needing to do nss lookups, do not disable cgo or set osusergo.
|
||||
func getHomeDir() string {
|
||||
homeDir := os.Getenv("HOME")
|
||||
if homeDir != "" {
|
||||
return homeDir
|
||||
}
|
||||
if u, err := user.Current(); err == nil {
|
||||
return u.HomeDir
|
||||
}
|
||||
return "/"
|
||||
}
|
||||
28
vendor/github.com/godbus/dbus/v5/introspect/call.go
generated
vendored
Normal file
28
vendor/github.com/godbus/dbus/v5/introspect/call.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package introspect
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"strings"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
// Call calls org.freedesktop.Introspectable.Introspect on a remote object
|
||||
// and returns the introspection data.
|
||||
func Call(o dbus.BusObject) (*Node, error) {
|
||||
var xmldata string
|
||||
var node Node
|
||||
|
||||
err := o.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xmldata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = xml.NewDecoder(strings.NewReader(xmldata)).Decode(&node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node.Name == "" {
|
||||
node.Name = string(o.Path())
|
||||
}
|
||||
return &node, nil
|
||||
}
|
||||
86
vendor/github.com/godbus/dbus/v5/introspect/introspect.go
generated
vendored
Normal file
86
vendor/github.com/godbus/dbus/v5/introspect/introspect.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Package introspect provides some utilities for dealing with the DBus
|
||||
// introspection format.
|
||||
package introspect
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Introspectable interface.
|
||||
var IntrospectData = Interface{
|
||||
Name: "org.freedesktop.DBus.Introspectable",
|
||||
Methods: []Method{
|
||||
{
|
||||
Name: "Introspect",
|
||||
Args: []Arg{
|
||||
{"out", "s", "out"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// XML document type declaration of the introspection format version 1.0
|
||||
const IntrospectDeclarationString = `
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
`
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Introspectable interface,
|
||||
// as a string.
|
||||
const IntrospectDataString = `
|
||||
<interface name="org.freedesktop.DBus.Introspectable">
|
||||
<method name="Introspect">
|
||||
<arg name="out" direction="out" type="s"/>
|
||||
</method>
|
||||
</interface>
|
||||
`
|
||||
|
||||
// Node is the root element of an introspection.
|
||||
type Node struct {
|
||||
XMLName xml.Name `xml:"node"`
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Interfaces []Interface `xml:"interface"`
|
||||
Children []Node `xml:"node,omitempty"`
|
||||
}
|
||||
|
||||
// Interface describes a DBus interface that is available on the message bus.
|
||||
type Interface struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Methods []Method `xml:"method"`
|
||||
Signals []Signal `xml:"signal"`
|
||||
Properties []Property `xml:"property"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Method describes a Method on an Interface as returned by an introspection.
|
||||
type Method struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Args []Arg `xml:"arg"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Signal describes a Signal emitted on an Interface.
|
||||
type Signal struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Args []Arg `xml:"arg"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Property describes a property of an Interface.
|
||||
type Property struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Access string `xml:"access,attr"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Arg represents an argument of a method or a signal.
|
||||
type Arg struct {
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Type string `xml:"type,attr"`
|
||||
Direction string `xml:"direction,attr,omitempty"`
|
||||
}
|
||||
|
||||
// Annotation is an annotation in the introspection format.
|
||||
type Annotation struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
77
vendor/github.com/godbus/dbus/v5/introspect/introspectable.go
generated
vendored
Normal file
77
vendor/github.com/godbus/dbus/v5/introspect/introspectable.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
package introspect
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
// Introspectable implements org.freedesktop.Introspectable.
|
||||
//
|
||||
// You can create it by converting the XML-formatted introspection data from a
|
||||
// string to an Introspectable or call NewIntrospectable with a Node. Then,
|
||||
// export it as org.freedesktop.Introspectable on you object.
|
||||
type Introspectable string
|
||||
|
||||
// NewIntrospectable returns an Introspectable that returns the introspection
|
||||
// data that corresponds to the given Node. If n.Interfaces doesn't contain the
|
||||
// data for org.freedesktop.DBus.Introspectable, it is added automatically.
|
||||
func NewIntrospectable(n *Node) Introspectable {
|
||||
found := false
|
||||
for _, v := range n.Interfaces {
|
||||
if v.Name == "org.freedesktop.DBus.Introspectable" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
n.Interfaces = append(n.Interfaces, IntrospectData)
|
||||
}
|
||||
b, err := xml.Marshal(n)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Introspectable(strings.TrimSpace(IntrospectDeclarationString) + string(b))
|
||||
}
|
||||
|
||||
// Introspect implements org.freedesktop.Introspectable.Introspect.
|
||||
func (i Introspectable) Introspect() (string, *dbus.Error) {
|
||||
return string(i), nil
|
||||
}
|
||||
|
||||
// Methods returns the description of the methods of v. This can be used to
|
||||
// create a Node which can be passed to NewIntrospectable.
|
||||
func Methods(v interface{}) []Method {
|
||||
t := reflect.TypeOf(v)
|
||||
ms := make([]Method, 0, t.NumMethod())
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
if t.Method(i).PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
mt := t.Method(i).Type
|
||||
if mt.NumOut() == 0 ||
|
||||
mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{}) {
|
||||
|
||||
continue
|
||||
}
|
||||
var m Method
|
||||
m.Name = t.Method(i).Name
|
||||
m.Args = make([]Arg, 0, mt.NumIn()+mt.NumOut()-2)
|
||||
for j := 1; j < mt.NumIn(); j++ {
|
||||
if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() &&
|
||||
mt.In(j) != reflect.TypeOf((*dbus.Message)(nil)).Elem() {
|
||||
arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"}
|
||||
m.Args = append(m.Args, arg)
|
||||
}
|
||||
}
|
||||
for j := 0; j < mt.NumOut()-1; j++ {
|
||||
arg := Arg{"", dbus.SignatureOfType(mt.Out(j)).String(), "out"}
|
||||
m.Args = append(m.Args, arg)
|
||||
}
|
||||
m.Annotations = make([]Annotation, 0)
|
||||
ms = append(ms, m)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
89
vendor/github.com/godbus/dbus/v5/match.go
generated
vendored
Normal file
89
vendor/github.com/godbus/dbus/v5/match.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers.
|
||||
// For full list of available options consult
|
||||
// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
|
||||
type MatchOption struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
func formatMatchOptions(options []MatchOption) string {
|
||||
items := make([]string, 0, len(options))
|
||||
for _, option := range options {
|
||||
items = append(items, option.key+"='"+option.value+"'")
|
||||
}
|
||||
return strings.Join(items, ",")
|
||||
}
|
||||
|
||||
// WithMatchOption creates match option with given key and value
|
||||
func WithMatchOption(key, value string) MatchOption {
|
||||
return MatchOption{key, value}
|
||||
}
|
||||
|
||||
// doesn't make sense to export this option because clients can only
|
||||
// subscribe to messages with signal type.
|
||||
func withMatchType(typ string) MatchOption {
|
||||
return WithMatchOption("type", typ)
|
||||
}
|
||||
|
||||
// WithMatchSender sets sender match option.
|
||||
func WithMatchSender(sender string) MatchOption {
|
||||
return WithMatchOption("sender", sender)
|
||||
}
|
||||
|
||||
// WithMatchSender sets interface match option.
|
||||
func WithMatchInterface(iface string) MatchOption {
|
||||
return WithMatchOption("interface", iface)
|
||||
}
|
||||
|
||||
// WithMatchMember sets member match option.
|
||||
func WithMatchMember(member string) MatchOption {
|
||||
return WithMatchOption("member", member)
|
||||
}
|
||||
|
||||
// WithMatchObjectPath creates match option that filters events based on given path
|
||||
func WithMatchObjectPath(path ObjectPath) MatchOption {
|
||||
return WithMatchOption("path", string(path))
|
||||
}
|
||||
|
||||
// WithMatchPathNamespace sets path_namespace match option.
|
||||
func WithMatchPathNamespace(namespace ObjectPath) MatchOption {
|
||||
return WithMatchOption("path_namespace", string(namespace))
|
||||
}
|
||||
|
||||
// WithMatchDestination sets destination match option.
|
||||
func WithMatchDestination(destination string) MatchOption {
|
||||
return WithMatchOption("destination", destination)
|
||||
}
|
||||
|
||||
// WithMatchArg sets argN match option, range of N is 0 to 63.
|
||||
func WithMatchArg(argIdx int, value string) MatchOption {
|
||||
if argIdx < 0 || argIdx > 63 {
|
||||
panic("range of argument index is 0 to 63")
|
||||
}
|
||||
return WithMatchOption("arg"+strconv.Itoa(argIdx), value)
|
||||
}
|
||||
|
||||
// WithMatchArgPath sets argN path match option, range of N is 0 to 63.
|
||||
func WithMatchArgPath(argIdx int, path string) MatchOption {
|
||||
if argIdx < 0 || argIdx > 63 {
|
||||
panic("range of argument index is 0 to 63")
|
||||
}
|
||||
return WithMatchOption("arg"+strconv.Itoa(argIdx)+"path", path)
|
||||
}
|
||||
|
||||
// WithMatchArg0Namespace sets arg0namespace match option.
|
||||
func WithMatchArg0Namespace(arg0Namespace string) MatchOption {
|
||||
return WithMatchOption("arg0namespace", arg0Namespace)
|
||||
}
|
||||
|
||||
// WithMatchEavesdrop sets eavesdrop match option.
|
||||
func WithMatchEavesdrop(eavesdrop bool) MatchOption {
|
||||
return WithMatchOption("eavesdrop", strconv.FormatBool(eavesdrop))
|
||||
}
|
||||
390
vendor/github.com/godbus/dbus/v5/message.go
generated
vendored
Normal file
390
vendor/github.com/godbus/dbus/v5/message.go
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const protoVersion byte = 1
|
||||
|
||||
// Flags represents the possible flags of a D-Bus message.
|
||||
type Flags byte
|
||||
|
||||
const (
|
||||
// FlagNoReplyExpected signals that the message is not expected to generate
|
||||
// a reply. If this flag is set on outgoing messages, any possible reply
|
||||
// will be discarded.
|
||||
FlagNoReplyExpected Flags = 1 << iota
|
||||
// FlagNoAutoStart signals that the message bus should not automatically
|
||||
// start an application when handling this message.
|
||||
FlagNoAutoStart
|
||||
// FlagAllowInteractiveAuthorization may be set on a method call
|
||||
// message to inform the receiving side that the caller is prepared
|
||||
// to wait for interactive authorization, which might take a
|
||||
// considerable time to complete. For instance, if this flag is set,
|
||||
// it would be appropriate to query the user for passwords or
|
||||
// confirmation via Polkit or a similar framework.
|
||||
FlagAllowInteractiveAuthorization
|
||||
)
|
||||
|
||||
// Type represents the possible types of a D-Bus message.
|
||||
type Type byte
|
||||
|
||||
const (
|
||||
TypeMethodCall Type = 1 + iota
|
||||
TypeMethodReply
|
||||
TypeError
|
||||
TypeSignal
|
||||
typeMax
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeMethodCall:
|
||||
return "method call"
|
||||
case TypeMethodReply:
|
||||
return "reply"
|
||||
case TypeError:
|
||||
return "error"
|
||||
case TypeSignal:
|
||||
return "signal"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
|
||||
// HeaderField represents the possible byte codes for the headers
|
||||
// of a D-Bus message.
|
||||
type HeaderField byte
|
||||
|
||||
const (
|
||||
FieldPath HeaderField = 1 + iota
|
||||
FieldInterface
|
||||
FieldMember
|
||||
FieldErrorName
|
||||
FieldReplySerial
|
||||
FieldDestination
|
||||
FieldSender
|
||||
FieldSignature
|
||||
FieldUnixFDs
|
||||
fieldMax
|
||||
)
|
||||
|
||||
// An InvalidMessageError describes the reason why a D-Bus message is regarded as
|
||||
// invalid.
|
||||
type InvalidMessageError string
|
||||
|
||||
func (e InvalidMessageError) Error() string {
|
||||
return "dbus: invalid message: " + string(e)
|
||||
}
|
||||
|
||||
// fieldType are the types of the various header fields.
|
||||
var fieldTypes = [fieldMax]reflect.Type{
|
||||
FieldPath: objectPathType,
|
||||
FieldInterface: stringType,
|
||||
FieldMember: stringType,
|
||||
FieldErrorName: stringType,
|
||||
FieldReplySerial: uint32Type,
|
||||
FieldDestination: stringType,
|
||||
FieldSender: stringType,
|
||||
FieldSignature: signatureType,
|
||||
FieldUnixFDs: uint32Type,
|
||||
}
|
||||
|
||||
// requiredFields lists the header fields that are required by the different
|
||||
// message types.
|
||||
var requiredFields = [typeMax][]HeaderField{
|
||||
TypeMethodCall: {FieldPath, FieldMember},
|
||||
TypeMethodReply: {FieldReplySerial},
|
||||
TypeError: {FieldErrorName, FieldReplySerial},
|
||||
TypeSignal: {FieldPath, FieldInterface, FieldMember},
|
||||
}
|
||||
|
||||
// Message represents a single D-Bus message.
|
||||
type Message struct {
|
||||
Type
|
||||
Flags
|
||||
Headers map[HeaderField]Variant
|
||||
Body []interface{}
|
||||
|
||||
serial uint32
|
||||
}
|
||||
|
||||
type header struct {
|
||||
Field byte
|
||||
Variant
|
||||
}
|
||||
|
||||
func DecodeMessageWithFDs(rd io.Reader, fds []int) (msg *Message, err error) {
|
||||
var order binary.ByteOrder
|
||||
var hlength, length uint32
|
||||
var typ, flags, proto byte
|
||||
var headers []header
|
||||
|
||||
b := make([]byte, 1)
|
||||
_, err = rd.Read(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch b[0] {
|
||||
case 'l':
|
||||
order = binary.LittleEndian
|
||||
case 'B':
|
||||
order = binary.BigEndian
|
||||
default:
|
||||
return nil, InvalidMessageError("invalid byte order")
|
||||
}
|
||||
|
||||
dec := newDecoder(rd, order, fds)
|
||||
dec.pos = 1
|
||||
|
||||
msg = new(Message)
|
||||
vs, err := dec.Decode(Signature{"yyyuu"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = Store(vs, &typ, &flags, &proto, &length, &msg.serial); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg.Type = Type(typ)
|
||||
msg.Flags = Flags(flags)
|
||||
|
||||
// get the header length separately because we need it later
|
||||
b = make([]byte, 4)
|
||||
_, err = io.ReadFull(rd, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.Read(bytes.NewBuffer(b), order, &hlength)
|
||||
if hlength+length+16 > 1<<27 {
|
||||
return nil, InvalidMessageError("message is too long")
|
||||
}
|
||||
dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order, fds)
|
||||
dec.pos = 12
|
||||
vs, err = dec.Decode(Signature{"a(yv)"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = Store(vs, &headers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
for _, v := range headers {
|
||||
msg.Headers[HeaderField(v.Field)] = v.Variant
|
||||
}
|
||||
|
||||
dec.align(8)
|
||||
body := make([]byte, int(length))
|
||||
if length != 0 {
|
||||
_, err := io.ReadFull(rd, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = msg.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig, _ := msg.Headers[FieldSignature].value.(Signature)
|
||||
if sig.str != "" {
|
||||
buf := bytes.NewBuffer(body)
|
||||
dec = newDecoder(buf, order, fds)
|
||||
vs, err := dec.Decode(sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg.Body = vs
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMessage tries to decode a single message in the D-Bus wire format
|
||||
// from the given reader. The byte order is figured out from the first byte.
|
||||
// The possibly returned error can be an error of the underlying reader, an
|
||||
// InvalidMessageError or a FormatError.
|
||||
func DecodeMessage(rd io.Reader) (msg *Message, err error) {
|
||||
return DecodeMessageWithFDs(rd, make([]int, 0))
|
||||
}
|
||||
|
||||
type nullwriter struct{}
|
||||
|
||||
func (nullwriter) Write(p []byte) (cnt int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (msg *Message) CountFds() (int, error) {
|
||||
if len(msg.Body) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
enc := newEncoder(nullwriter{}, nativeEndian, make([]int, 0))
|
||||
err := enc.Encode(msg.Body...)
|
||||
return len(enc.fds), err
|
||||
}
|
||||
|
||||
func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds []int, err error) {
|
||||
if err := msg.validateHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var vs [7]interface{}
|
||||
switch order {
|
||||
case binary.LittleEndian:
|
||||
vs[0] = byte('l')
|
||||
case binary.BigEndian:
|
||||
vs[0] = byte('B')
|
||||
default:
|
||||
return nil, errors.New("dbus: invalid byte order")
|
||||
}
|
||||
body := new(bytes.Buffer)
|
||||
fds = make([]int, 0)
|
||||
enc := newEncoder(body, order, fds)
|
||||
if len(msg.Body) != 0 {
|
||||
err = enc.Encode(msg.Body...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
vs[1] = msg.Type
|
||||
vs[2] = msg.Flags
|
||||
vs[3] = protoVersion
|
||||
vs[4] = uint32(len(body.Bytes()))
|
||||
vs[5] = msg.serial
|
||||
headers := make([]header, 0, len(msg.Headers))
|
||||
for k, v := range msg.Headers {
|
||||
headers = append(headers, header{byte(k), v})
|
||||
}
|
||||
vs[6] = headers
|
||||
var buf bytes.Buffer
|
||||
enc = newEncoder(&buf, order, enc.fds)
|
||||
err = enc.Encode(vs[:]...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
enc.align(8)
|
||||
body.WriteTo(&buf)
|
||||
if buf.Len() > 1<<27 {
|
||||
return make([]int, 0), InvalidMessageError("message is too long")
|
||||
}
|
||||
if _, err := buf.WriteTo(out); err != nil {
|
||||
return make([]int, 0), err
|
||||
}
|
||||
return enc.fds, nil
|
||||
}
|
||||
|
||||
// EncodeTo encodes and sends a message to the given writer. The byte order must
|
||||
// be either binary.LittleEndian or binary.BigEndian. If the message is not
|
||||
// valid or an error occurs when writing, an error is returned.
|
||||
func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) (err error) {
|
||||
_, err = msg.EncodeToWithFDs(out, order)
|
||||
return err
|
||||
}
|
||||
|
||||
// IsValid checks whether msg is a valid message and returns an
|
||||
// InvalidMessageError or FormatError if it is not.
|
||||
func (msg *Message) IsValid() error {
|
||||
var b bytes.Buffer
|
||||
return msg.EncodeTo(&b, nativeEndian)
|
||||
}
|
||||
|
||||
func (msg *Message) validateHeader() error {
|
||||
if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected|FlagAllowInteractiveAuthorization) != 0 {
|
||||
return InvalidMessageError("invalid flags")
|
||||
}
|
||||
if msg.Type == 0 || msg.Type >= typeMax {
|
||||
return InvalidMessageError("invalid message type")
|
||||
}
|
||||
for k, v := range msg.Headers {
|
||||
if k == 0 || k >= fieldMax {
|
||||
return InvalidMessageError("invalid header")
|
||||
}
|
||||
if reflect.TypeOf(v.value) != fieldTypes[k] {
|
||||
return InvalidMessageError("invalid type of header field")
|
||||
}
|
||||
}
|
||||
for _, v := range requiredFields[msg.Type] {
|
||||
if _, ok := msg.Headers[v]; !ok {
|
||||
return InvalidMessageError("missing required header")
|
||||
}
|
||||
}
|
||||
if path, ok := msg.Headers[FieldPath]; ok {
|
||||
if !path.value.(ObjectPath).IsValid() {
|
||||
return InvalidMessageError("invalid path name")
|
||||
}
|
||||
}
|
||||
if iface, ok := msg.Headers[FieldInterface]; ok {
|
||||
if !isValidInterface(iface.value.(string)) {
|
||||
return InvalidMessageError("invalid interface name")
|
||||
}
|
||||
}
|
||||
if member, ok := msg.Headers[FieldMember]; ok {
|
||||
if !isValidMember(member.value.(string)) {
|
||||
return InvalidMessageError("invalid member name")
|
||||
}
|
||||
}
|
||||
if errname, ok := msg.Headers[FieldErrorName]; ok {
|
||||
if !isValidInterface(errname.value.(string)) {
|
||||
return InvalidMessageError("invalid error name")
|
||||
}
|
||||
}
|
||||
if len(msg.Body) != 0 {
|
||||
if _, ok := msg.Headers[FieldSignature]; !ok {
|
||||
return InvalidMessageError("missing signature")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serial returns the message's serial number. The returned value is only valid
|
||||
// for messages received by eavesdropping.
|
||||
func (msg *Message) Serial() uint32 {
|
||||
return msg.serial
|
||||
}
|
||||
|
||||
// String returns a string representation of a message similar to the format of
|
||||
// dbus-monitor.
|
||||
func (msg *Message) String() string {
|
||||
if err := msg.IsValid(); err != nil {
|
||||
return "<invalid>"
|
||||
}
|
||||
s := msg.Type.String()
|
||||
if v, ok := msg.Headers[FieldSender]; ok {
|
||||
s += " from " + v.value.(string)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldDestination]; ok {
|
||||
s += " to " + v.value.(string)
|
||||
}
|
||||
s += " serial " + strconv.FormatUint(uint64(msg.serial), 10)
|
||||
if v, ok := msg.Headers[FieldReplySerial]; ok {
|
||||
s += " reply_serial " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldUnixFDs]; ok {
|
||||
s += " unixfds " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldPath]; ok {
|
||||
s += " path " + string(v.value.(ObjectPath))
|
||||
}
|
||||
if v, ok := msg.Headers[FieldInterface]; ok {
|
||||
s += " interface " + v.value.(string)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldErrorName]; ok {
|
||||
s += " error " + v.value.(string)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldMember]; ok {
|
||||
s += " member " + v.value.(string)
|
||||
}
|
||||
if len(msg.Body) != 0 {
|
||||
s += "\n"
|
||||
}
|
||||
for i, v := range msg.Body {
|
||||
s += " " + MakeVariant(v).String()
|
||||
if i != len(msg.Body)-1 {
|
||||
s += "\n"
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
174
vendor/github.com/godbus/dbus/v5/object.go
generated
vendored
Normal file
174
vendor/github.com/godbus/dbus/v5/object.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BusObject is the interface of a remote object on which methods can be
|
||||
// invoked.
|
||||
type BusObject interface {
|
||||
Call(method string, flags Flags, args ...interface{}) *Call
|
||||
CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call
|
||||
Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call
|
||||
GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call
|
||||
AddMatchSignal(iface, member string, options ...MatchOption) *Call
|
||||
RemoveMatchSignal(iface, member string, options ...MatchOption) *Call
|
||||
GetProperty(p string) (Variant, error)
|
||||
StoreProperty(p string, value interface{}) error
|
||||
SetProperty(p string, v interface{}) error
|
||||
Destination() string
|
||||
Path() ObjectPath
|
||||
}
|
||||
|
||||
// Object represents a remote object on which methods can be invoked.
|
||||
type Object struct {
|
||||
conn *Conn
|
||||
dest string
|
||||
path ObjectPath
|
||||
}
|
||||
|
||||
// Call calls a method with (*Object).Go and waits for its reply.
|
||||
func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
|
||||
return <-o.createCall(context.Background(), method, flags, make(chan *Call, 1), args...).Done
|
||||
}
|
||||
|
||||
// CallWithContext acts like Call but takes a context
|
||||
func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call {
|
||||
return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done
|
||||
}
|
||||
|
||||
// AddMatchSignal subscribes BusObject to signals from specified interface,
|
||||
// method (member). Additional filter rules can be added via WithMatch* option constructors.
|
||||
// Note: To filter events by object path you have to specify this path via an option.
|
||||
//
|
||||
// Deprecated: use (*Conn) AddMatchSignal instead.
|
||||
func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call {
|
||||
base := []MatchOption{
|
||||
withMatchType("signal"),
|
||||
WithMatchInterface(iface),
|
||||
WithMatchMember(member),
|
||||
}
|
||||
|
||||
options = append(base, options...)
|
||||
return o.conn.BusObject().Call(
|
||||
"org.freedesktop.DBus.AddMatch",
|
||||
0,
|
||||
formatMatchOptions(options),
|
||||
)
|
||||
}
|
||||
|
||||
// RemoveMatchSignal unsubscribes BusObject from signals from specified interface,
|
||||
// method (member). Additional filter rules can be added via WithMatch* option constructors
|
||||
//
|
||||
// Deprecated: use (*Conn) RemoveMatchSignal instead.
|
||||
func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call {
|
||||
base := []MatchOption{
|
||||
withMatchType("signal"),
|
||||
WithMatchInterface(iface),
|
||||
WithMatchMember(member),
|
||||
}
|
||||
|
||||
options = append(base, options...)
|
||||
return o.conn.BusObject().Call(
|
||||
"org.freedesktop.DBus.RemoveMatch",
|
||||
0,
|
||||
formatMatchOptions(options),
|
||||
)
|
||||
}
|
||||
|
||||
// Go calls a method with the given arguments asynchronously. It returns a
|
||||
// Call structure representing this method call. The passed channel will
|
||||
// return the same value once the call is done. If ch is nil, a new channel
|
||||
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
||||
//
|
||||
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
||||
// is returned with any error in Err and a closed channel in Done containing
|
||||
// the returned Call as it's one entry.
|
||||
//
|
||||
// If the method parameter contains a dot ('.'), the part before the last dot
|
||||
// specifies the interface on which the method is called.
|
||||
func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||
return o.createCall(context.Background(), method, flags, ch, args...)
|
||||
}
|
||||
|
||||
// GoWithContext acts like Go but takes a context
|
||||
func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||
return o.createCall(ctx, method, flags, ch, args...)
|
||||
}
|
||||
|
||||
func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
iface := ""
|
||||
i := strings.LastIndex(method, ".")
|
||||
if i != -1 {
|
||||
iface = method[:i]
|
||||
}
|
||||
method = method[i+1:]
|
||||
msg := new(Message)
|
||||
msg.Type = TypeMethodCall
|
||||
msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldPath] = MakeVariant(o.path)
|
||||
msg.Headers[FieldDestination] = MakeVariant(o.dest)
|
||||
msg.Headers[FieldMember] = MakeVariant(method)
|
||||
if iface != "" {
|
||||
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||
}
|
||||
msg.Body = args
|
||||
if len(args) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
|
||||
}
|
||||
return o.conn.SendWithContext(ctx, msg, ch)
|
||||
}
|
||||
|
||||
// GetProperty calls org.freedesktop.DBus.Properties.Get on the given
|
||||
// object. The property name must be given in interface.member notation.
|
||||
func (o *Object) GetProperty(p string) (Variant, error) {
|
||||
var result Variant
|
||||
err := o.StoreProperty(p, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
// StoreProperty calls org.freedesktop.DBus.Properties.Get on the given
|
||||
// object. The property name must be given in interface.member notation.
|
||||
// It stores the returned property into the provided value.
|
||||
func (o *Object) StoreProperty(p string, value interface{}) error {
|
||||
idx := strings.LastIndex(p, ".")
|
||||
if idx == -1 || idx+1 == len(p) {
|
||||
return errors.New("dbus: invalid property " + p)
|
||||
}
|
||||
|
||||
iface := p[:idx]
|
||||
prop := p[idx+1:]
|
||||
|
||||
return o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).
|
||||
Store(value)
|
||||
}
|
||||
|
||||
// SetProperty calls org.freedesktop.DBus.Properties.Set on the given
|
||||
// object. The property name must be given in interface.member notation.
|
||||
func (o *Object) SetProperty(p string, v interface{}) error {
|
||||
idx := strings.LastIndex(p, ".")
|
||||
if idx == -1 || idx+1 == len(p) {
|
||||
return errors.New("dbus: invalid property " + p)
|
||||
}
|
||||
|
||||
iface := p[:idx]
|
||||
prop := p[idx+1:]
|
||||
|
||||
return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, v).Err
|
||||
}
|
||||
|
||||
// Destination returns the destination that calls on (o *Object) are sent to.
|
||||
func (o *Object) Destination() string {
|
||||
return o.dest
|
||||
}
|
||||
|
||||
// Path returns the path that calls on (o *Object") are sent to.
|
||||
func (o *Object) Path() ObjectPath {
|
||||
return o.path
|
||||
}
|
||||
348
vendor/github.com/godbus/dbus/v5/prop/prop.go
generated
vendored
Normal file
348
vendor/github.com/godbus/dbus/v5/prop/prop.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
// Package prop provides the Properties struct which can be used to implement
|
||||
// org.freedesktop.DBus.Properties.
|
||||
package prop
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"github.com/godbus/dbus/v5/introspect"
|
||||
)
|
||||
|
||||
// EmitType controls how org.freedesktop.DBus.Properties.PropertiesChanged is
|
||||
// emitted for a property. If it is EmitTrue, the signal is emitted. If it is
|
||||
// EmitInvalidates, the signal is also emitted, but the new value of the property
|
||||
// is not disclosed. If it is EmitConst, the property never changes value during
|
||||
// the lifetime of the object it belongs to, and hence the signal is never emitted
|
||||
// for it.
|
||||
type EmitType byte
|
||||
|
||||
const (
|
||||
EmitFalse EmitType = iota
|
||||
EmitTrue
|
||||
EmitInvalidates
|
||||
EmitConst
|
||||
)
|
||||
|
||||
func (e EmitType) String() (str string) {
|
||||
switch e {
|
||||
case EmitFalse:
|
||||
str = "false"
|
||||
case EmitTrue:
|
||||
str = "true"
|
||||
case EmitInvalidates:
|
||||
str = "invalidates"
|
||||
case EmitConst:
|
||||
str = "const"
|
||||
default:
|
||||
panic("invalid value for EmitType")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ErrIfaceNotFound is the error returned to peers who try to access properties
|
||||
// on interfaces that aren't found.
|
||||
var ErrIfaceNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil)
|
||||
|
||||
// ErrPropNotFound is the error returned to peers trying to access properties
|
||||
// that aren't found.
|
||||
var ErrPropNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil)
|
||||
|
||||
// ErrReadOnly is the error returned to peers trying to set a read-only
|
||||
// property.
|
||||
var ErrReadOnly = dbus.NewError("org.freedesktop.DBus.Properties.Error.ReadOnly", nil)
|
||||
|
||||
// ErrInvalidArg is returned to peers if the type of the property that is being
|
||||
// changed and the argument don't match.
|
||||
var ErrInvalidArg = dbus.NewError("org.freedesktop.DBus.Properties.Error.InvalidArg", nil)
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Properties interface.
|
||||
var IntrospectData = introspect.Interface{
|
||||
Name: "org.freedesktop.DBus.Properties",
|
||||
Methods: []introspect.Method{
|
||||
{
|
||||
Name: "Get",
|
||||
Args: []introspect.Arg{
|
||||
{Name: "interface", Type: "s", Direction: "in"},
|
||||
{Name: "property", Type: "s", Direction: "in"},
|
||||
{Name: "value", Type: "v", Direction: "out"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "GetAll",
|
||||
Args: []introspect.Arg{
|
||||
{Name: "interface", Type: "s", Direction: "in"},
|
||||
{Name: "props", Type: "a{sv}", Direction: "out"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Set",
|
||||
Args: []introspect.Arg{
|
||||
{Name: "interface", Type: "s", Direction: "in"},
|
||||
{Name: "property", Type: "s", Direction: "in"},
|
||||
{Name: "value", Type: "v", Direction: "in"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Signals: []introspect.Signal{
|
||||
{
|
||||
Name: "PropertiesChanged",
|
||||
Args: []introspect.Arg{
|
||||
{Name: "interface", Type: "s", Direction: "out"},
|
||||
{Name: "changed_properties", Type: "a{sv}", Direction: "out"},
|
||||
{Name: "invalidates_properties", Type: "as", Direction: "out"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Properties interface, as
|
||||
// a string.
|
||||
const IntrospectDataString = `
|
||||
<interface name="org.freedesktop.DBus.Properties">
|
||||
<method name="Get">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="out" type="v"/>
|
||||
</method>
|
||||
<method name="GetAll">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="props" direction="out" type="a{sv}"/>
|
||||
</method>
|
||||
<method name="Set">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="in" type="v"/>
|
||||
</method>
|
||||
<signal name="PropertiesChanged">
|
||||
<arg name="interface" type="s"/>
|
||||
<arg name="changed_properties" type="a{sv}"/>
|
||||
<arg name="invalidates_properties" type="as"/>
|
||||
</signal>
|
||||
</interface>
|
||||
`
|
||||
|
||||
// Prop represents a single property. It is used for creating a Properties
|
||||
// value.
|
||||
type Prop struct {
|
||||
// Initial value. Must be a DBus-representable type. This is not modified
|
||||
// after Properties has been initialized; use Get or GetMust to access the
|
||||
// value.
|
||||
Value interface{}
|
||||
|
||||
// If true, the value can be modified by calls to Set.
|
||||
Writable bool
|
||||
|
||||
// Controls how org.freedesktop.DBus.Properties.PropertiesChanged is
|
||||
// emitted if this property changes.
|
||||
Emit EmitType
|
||||
|
||||
// If not nil, anytime this property is changed by Set, this function is
|
||||
// called with an appropriate Change as its argument. If the returned error
|
||||
// is not nil, it is sent back to the caller of Set and the property is not
|
||||
// changed.
|
||||
Callback func(*Change) *dbus.Error
|
||||
}
|
||||
|
||||
// Introspection returns the introspection data for p.
|
||||
// The "name" argument is used as the property's name in the resulting data.
|
||||
func (p *Prop) Introspection(name string) introspect.Property {
|
||||
var result = introspect.Property{Name: name, Type: dbus.SignatureOf(p.Value).String()}
|
||||
if p.Writable {
|
||||
result.Access = "readwrite"
|
||||
} else {
|
||||
result.Access = "read"
|
||||
}
|
||||
result.Annotations = []introspect.Annotation{
|
||||
{
|
||||
Name: "org.freedesktop.DBus.Property.EmitsChangedSignal",
|
||||
Value: p.Emit.String(),
|
||||
},
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Change represents a change of a property by a call to Set.
|
||||
type Change struct {
|
||||
Props *Properties
|
||||
Iface string
|
||||
Name string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Properties is a set of values that can be made available to the message bus
|
||||
// using the org.freedesktop.DBus.Properties interface. It is safe for
|
||||
// concurrent use by multiple goroutines.
|
||||
type Properties struct {
|
||||
m Map
|
||||
mut sync.RWMutex
|
||||
conn *dbus.Conn
|
||||
path dbus.ObjectPath
|
||||
}
|
||||
|
||||
// New falls back to Export, but it returns nil if properties export fails,
|
||||
// swallowing the error, shouldn't be used.
|
||||
//
|
||||
// Deprecated: use Export instead.
|
||||
func New(conn *dbus.Conn, path dbus.ObjectPath, props Map) *Properties {
|
||||
p, err := Export(conn, path, props)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Export returns a new Properties structure that manages the given properties.
|
||||
// The key for the first-level map of props is the name of the interface; the
|
||||
// second-level key is the name of the property. The returned structure will be
|
||||
// exported as org.freedesktop.DBus.Properties on path.
|
||||
func Export(
|
||||
conn *dbus.Conn, path dbus.ObjectPath, props Map,
|
||||
) (*Properties, error) {
|
||||
p := &Properties{m: copyProps(props), conn: conn, path: path}
|
||||
if err := conn.Export(p, path, "org.freedesktop.DBus.Properties"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Map is a helper type for supplying the configuration of properties to be handled.
|
||||
type Map = map[string]map[string]*Prop
|
||||
|
||||
func copyProps(in Map) Map {
|
||||
out := make(Map, len(in))
|
||||
for intf, props := range in {
|
||||
out[intf] = make(map[string]*Prop)
|
||||
for name, prop := range props {
|
||||
out[intf][name] = new(Prop)
|
||||
*out[intf][name] = *prop
|
||||
val := reflect.New(reflect.TypeOf(prop.Value))
|
||||
val.Elem().Set(reflect.ValueOf(prop.Value))
|
||||
out[intf][name].Value = val.Interface()
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Get implements org.freedesktop.DBus.Properties.Get.
|
||||
func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
m, ok := p.m[iface]
|
||||
if !ok {
|
||||
return dbus.Variant{}, ErrIfaceNotFound
|
||||
}
|
||||
prop, ok := m[property]
|
||||
if !ok {
|
||||
return dbus.Variant{}, ErrPropNotFound
|
||||
}
|
||||
return dbus.MakeVariant(reflect.ValueOf(prop.Value).Elem().Interface()), nil
|
||||
}
|
||||
|
||||
// GetAll implements org.freedesktop.DBus.Properties.GetAll.
|
||||
func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
m, ok := p.m[iface]
|
||||
if !ok {
|
||||
return nil, ErrIfaceNotFound
|
||||
}
|
||||
rm := make(map[string]dbus.Variant, len(m))
|
||||
for k, v := range m {
|
||||
rm[k] = dbus.MakeVariant(reflect.ValueOf(v.Value).Elem().Interface())
|
||||
}
|
||||
return rm, nil
|
||||
}
|
||||
|
||||
// GetMust returns the value of the given property and panics if either the
|
||||
// interface or the property name are invalid.
|
||||
func (p *Properties) GetMust(iface, property string) interface{} {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
return reflect.ValueOf(p.m[iface][property].Value).Elem().Interface()
|
||||
}
|
||||
|
||||
// Introspection returns the introspection data that represents the properties
|
||||
// of iface.
|
||||
func (p *Properties) Introspection(iface string) []introspect.Property {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
m := p.m[iface]
|
||||
s := make([]introspect.Property, 0, len(m))
|
||||
for name, prop := range m {
|
||||
s = append(s, prop.Introspection(name))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// set sets the given property and emits PropertyChanged if appropriate. p.mut
|
||||
// must already be locked.
|
||||
func (p *Properties) set(iface, property string, v interface{}) error {
|
||||
prop := p.m[iface][property]
|
||||
err := dbus.Store([]interface{}{v}, prop.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.emitChange(iface, property)
|
||||
}
|
||||
|
||||
func (p *Properties) emitChange(iface, property string) error {
|
||||
prop := p.m[iface][property]
|
||||
switch prop.Emit {
|
||||
case EmitFalse:
|
||||
return nil // do nothing
|
||||
case EmitInvalidates:
|
||||
return p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
|
||||
iface, map[string]dbus.Variant{}, []string{property})
|
||||
case EmitTrue:
|
||||
return p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
|
||||
iface, map[string]dbus.Variant{property: dbus.MakeVariant(prop.Value)},
|
||||
[]string{})
|
||||
case EmitConst:
|
||||
return nil
|
||||
default:
|
||||
panic("invalid value for EmitType")
|
||||
}
|
||||
}
|
||||
|
||||
// Set implements org.freedesktop.Properties.Set.
|
||||
func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error {
|
||||
p.mut.Lock()
|
||||
defer p.mut.Unlock()
|
||||
m, ok := p.m[iface]
|
||||
if !ok {
|
||||
return ErrIfaceNotFound
|
||||
}
|
||||
prop, ok := m[property]
|
||||
if !ok {
|
||||
return ErrPropNotFound
|
||||
}
|
||||
if !prop.Writable {
|
||||
return ErrReadOnly
|
||||
}
|
||||
if newv.Signature() != dbus.SignatureOf(prop.Value) {
|
||||
return ErrInvalidArg
|
||||
}
|
||||
if prop.Callback != nil {
|
||||
err := prop.Callback(&Change{p, iface, property, newv.Value()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := p.set(iface, property, newv.Value()); err != nil {
|
||||
return dbus.MakeFailedError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMust sets the value of the given property and panics if the interface or
|
||||
// the property name are invalid.
|
||||
func (p *Properties) SetMust(iface, property string, v interface{}) {
|
||||
p.mut.Lock()
|
||||
defer p.mut.Unlock() // unlock in case of panic
|
||||
err := p.set(iface, property, v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
24
vendor/github.com/godbus/dbus/v5/sequence.go
generated
vendored
Normal file
24
vendor/github.com/godbus/dbus/v5/sequence.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package dbus
|
||||
|
||||
// Sequence represents the value of a monotonically increasing counter.
|
||||
type Sequence uint64
|
||||
|
||||
const (
|
||||
// NoSequence indicates the absence of a sequence value.
|
||||
NoSequence Sequence = 0
|
||||
)
|
||||
|
||||
// sequenceGenerator represents a monotonically increasing counter.
|
||||
type sequenceGenerator struct {
|
||||
nextSequence Sequence
|
||||
}
|
||||
|
||||
func (generator *sequenceGenerator) next() Sequence {
|
||||
result := generator.nextSequence
|
||||
generator.nextSequence++
|
||||
return result
|
||||
}
|
||||
|
||||
func newSequenceGenerator() *sequenceGenerator {
|
||||
return &sequenceGenerator{nextSequence: 1}
|
||||
}
|
||||
125
vendor/github.com/godbus/dbus/v5/sequential_handler.go
generated
vendored
Normal file
125
vendor/github.com/godbus/dbus/v5/sequential_handler.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewSequentialSignalHandler returns an instance of a new
|
||||
// signal handler that guarantees sequential processing of signals. It is a
|
||||
// guarantee of this signal handler that signals will be written to
|
||||
// channels in the order they are received on the DBus connection.
|
||||
func NewSequentialSignalHandler() SignalHandler {
|
||||
return &sequentialSignalHandler{}
|
||||
}
|
||||
|
||||
type sequentialSignalHandler struct {
|
||||
mu sync.RWMutex
|
||||
closed bool
|
||||
signals []*sequentialSignalChannelData
|
||||
}
|
||||
|
||||
func (sh *sequentialSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
|
||||
sh.mu.RLock()
|
||||
defer sh.mu.RUnlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
for _, scd := range sh.signals {
|
||||
scd.deliver(signal)
|
||||
}
|
||||
}
|
||||
|
||||
func (sh *sequentialSignalHandler) Terminate() {
|
||||
sh.mu.Lock()
|
||||
defer sh.mu.Unlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
|
||||
for _, scd := range sh.signals {
|
||||
scd.close()
|
||||
close(scd.ch)
|
||||
}
|
||||
sh.closed = true
|
||||
sh.signals = nil
|
||||
}
|
||||
|
||||
func (sh *sequentialSignalHandler) AddSignal(ch chan<- *Signal) {
|
||||
sh.mu.Lock()
|
||||
defer sh.mu.Unlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
sh.signals = append(sh.signals, newSequentialSignalChannelData(ch))
|
||||
}
|
||||
|
||||
func (sh *sequentialSignalHandler) RemoveSignal(ch chan<- *Signal) {
|
||||
sh.mu.Lock()
|
||||
defer sh.mu.Unlock()
|
||||
if sh.closed {
|
||||
return
|
||||
}
|
||||
for i := len(sh.signals) - 1; i >= 0; i-- {
|
||||
if ch == sh.signals[i].ch {
|
||||
sh.signals[i].close()
|
||||
copy(sh.signals[i:], sh.signals[i+1:])
|
||||
sh.signals[len(sh.signals)-1] = nil
|
||||
sh.signals = sh.signals[:len(sh.signals)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type sequentialSignalChannelData struct {
|
||||
ch chan<- *Signal
|
||||
in chan *Signal
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func newSequentialSignalChannelData(ch chan<- *Signal) *sequentialSignalChannelData {
|
||||
scd := &sequentialSignalChannelData{
|
||||
ch: ch,
|
||||
in: make(chan *Signal),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
go scd.bufferSignals()
|
||||
return scd
|
||||
}
|
||||
|
||||
func (scd *sequentialSignalChannelData) bufferSignals() {
|
||||
defer close(scd.done)
|
||||
|
||||
// Ensure that signals are delivered to scd.ch in the same
|
||||
// order they are received from scd.in.
|
||||
var queue []*Signal
|
||||
for {
|
||||
if len(queue) == 0 {
|
||||
signal, ok := <- scd.in
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
queue = append(queue, signal)
|
||||
}
|
||||
select {
|
||||
case scd.ch <- queue[0]:
|
||||
copy(queue, queue[1:])
|
||||
queue[len(queue)-1] = nil
|
||||
queue = queue[:len(queue)-1]
|
||||
case signal, ok := <-scd.in:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
queue = append(queue, signal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (scd *sequentialSignalChannelData) deliver(signal *Signal) {
|
||||
scd.in <- signal
|
||||
}
|
||||
|
||||
func (scd *sequentialSignalChannelData) close() {
|
||||
close(scd.in)
|
||||
// Ensure that bufferSignals() has exited and won't attempt
|
||||
// any future sends on scd.ch
|
||||
<-scd.done
|
||||
}
|
||||
107
vendor/github.com/godbus/dbus/v5/server_interfaces.go
generated
vendored
Normal file
107
vendor/github.com/godbus/dbus/v5/server_interfaces.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
package dbus
|
||||
|
||||
// Terminator allows a handler to implement a shutdown mechanism that
|
||||
// is called when the connection terminates.
|
||||
type Terminator interface {
|
||||
Terminate()
|
||||
}
|
||||
|
||||
// Handler is the representation of a D-Bus Application.
|
||||
//
|
||||
// The Handler must have a way to lookup objects given
|
||||
// an ObjectPath. The returned object must implement the
|
||||
// ServerObject interface.
|
||||
type Handler interface {
|
||||
LookupObject(path ObjectPath) (ServerObject, bool)
|
||||
}
|
||||
|
||||
// ServerObject is the representation of an D-Bus Object.
|
||||
//
|
||||
// Objects are registered at a path for a given Handler.
|
||||
// The Objects implement D-Bus interfaces. The semantics
|
||||
// of Interface lookup is up to the implementation of
|
||||
// the ServerObject. The ServerObject implementation may
|
||||
// choose to implement empty string as a valid interface
|
||||
// represeting all methods or not per the D-Bus specification.
|
||||
type ServerObject interface {
|
||||
LookupInterface(name string) (Interface, bool)
|
||||
}
|
||||
|
||||
// An Interface is the representation of a D-Bus Interface.
|
||||
//
|
||||
// Interfaces are a grouping of methods implemented by the Objects.
|
||||
// Interfaces are responsible for routing method calls.
|
||||
type Interface interface {
|
||||
LookupMethod(name string) (Method, bool)
|
||||
}
|
||||
|
||||
// A Method represents the exposed methods on D-Bus.
|
||||
type Method interface {
|
||||
// Call requires that all arguments are decoded before being passed to it.
|
||||
Call(args ...interface{}) ([]interface{}, error)
|
||||
NumArguments() int
|
||||
NumReturns() int
|
||||
// ArgumentValue returns a representative value for the argument at position
|
||||
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||
// to use for this Value.
|
||||
ArgumentValue(position int) interface{}
|
||||
// ReturnValue returns a representative value for the return at position
|
||||
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||
// to use for this Value.
|
||||
ReturnValue(position int) interface{}
|
||||
}
|
||||
|
||||
// An Argument Decoder can decode arguments using the non-standard mechanism
|
||||
//
|
||||
// If a method implements this interface then the non-standard
|
||||
// decoder will be used.
|
||||
//
|
||||
// Method arguments must be decoded from the message.
|
||||
// The mechanism for doing this will vary based on the
|
||||
// implementation of the method. A normal approach is provided
|
||||
// as part of this library, but may be replaced with
|
||||
// any other decoding scheme.
|
||||
type ArgumentDecoder interface {
|
||||
// To decode the arguments of a method the sender and message are
|
||||
// provided in case the semantics of the implementer provides access
|
||||
// to these as part of the method invocation.
|
||||
DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error)
|
||||
}
|
||||
|
||||
// A SignalHandler is responsible for delivering a signal.
|
||||
//
|
||||
// Signal delivery may be changed from the default channel
|
||||
// based approach by Handlers implementing the SignalHandler
|
||||
// interface.
|
||||
type SignalHandler interface {
|
||||
DeliverSignal(iface, name string, signal *Signal)
|
||||
}
|
||||
|
||||
// SignalRegistrar manages signal delivery channels.
|
||||
//
|
||||
// This is an optional set of methods for `SignalHandler`.
|
||||
type SignalRegistrar interface {
|
||||
AddSignal(ch chan<- *Signal)
|
||||
RemoveSignal(ch chan<- *Signal)
|
||||
}
|
||||
|
||||
// A DBusError is used to convert a generic object to a D-Bus error.
|
||||
//
|
||||
// Any custom error mechanism may implement this interface to provide
|
||||
// a custom encoding of the error on D-Bus. By default if a normal
|
||||
// error is returned, it will be encoded as the generic
|
||||
// "org.freedesktop.DBus.Error.Failed" error. By implementing this
|
||||
// interface as well a custom encoding may be provided.
|
||||
type DBusError interface {
|
||||
DBusError() (string, []interface{})
|
||||
}
|
||||
|
||||
// SerialGenerator is responsible for serials generation.
|
||||
//
|
||||
// Different approaches for the serial generation can be used,
|
||||
// maintaining a map guarded with a mutex (the standard way) or
|
||||
// simply increment an atomic counter.
|
||||
type SerialGenerator interface {
|
||||
GetSerial() uint32
|
||||
RetireSerial(serial uint32)
|
||||
}
|
||||
293
vendor/github.com/godbus/dbus/v5/sig.go
generated
vendored
Normal file
293
vendor/github.com/godbus/dbus/v5/sig.go
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var sigToType = map[byte]reflect.Type{
|
||||
'y': byteType,
|
||||
'b': boolType,
|
||||
'n': int16Type,
|
||||
'q': uint16Type,
|
||||
'i': int32Type,
|
||||
'u': uint32Type,
|
||||
'x': int64Type,
|
||||
't': uint64Type,
|
||||
'd': float64Type,
|
||||
's': stringType,
|
||||
'g': signatureType,
|
||||
'o': objectPathType,
|
||||
'v': variantType,
|
||||
'h': unixFDIndexType,
|
||||
}
|
||||
|
||||
// Signature represents a correct type signature as specified by the D-Bus
|
||||
// specification. The zero value represents the empty signature, "".
|
||||
type Signature struct {
|
||||
str string
|
||||
}
|
||||
|
||||
// SignatureOf returns the concatenation of all the signatures of the given
|
||||
// values. It panics if one of them is not representable in D-Bus.
|
||||
func SignatureOf(vs ...interface{}) Signature {
|
||||
var s string
|
||||
for _, v := range vs {
|
||||
s += getSignature(reflect.TypeOf(v), &depthCounter{})
|
||||
}
|
||||
return Signature{s}
|
||||
}
|
||||
|
||||
// SignatureOfType returns the signature of the given type. It panics if the
|
||||
// type is not representable in D-Bus.
|
||||
func SignatureOfType(t reflect.Type) Signature {
|
||||
return Signature{getSignature(t, &depthCounter{})}
|
||||
}
|
||||
|
||||
// getSignature returns the signature of the given type and panics on unknown types.
|
||||
func getSignature(t reflect.Type, depth *depthCounter) (sig string) {
|
||||
if !depth.Valid() {
|
||||
panic("container nesting too deep")
|
||||
}
|
||||
defer func() {
|
||||
if len(sig) > 255 {
|
||||
panic("signature exceeds the length limitation")
|
||||
}
|
||||
}()
|
||||
// handle simple types first
|
||||
switch t.Kind() {
|
||||
case reflect.Uint8:
|
||||
return "y"
|
||||
case reflect.Bool:
|
||||
return "b"
|
||||
case reflect.Int16:
|
||||
return "n"
|
||||
case reflect.Uint16:
|
||||
return "q"
|
||||
case reflect.Int, reflect.Int32:
|
||||
if t == unixFDType {
|
||||
return "h"
|
||||
}
|
||||
return "i"
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
if t == unixFDIndexType {
|
||||
return "h"
|
||||
}
|
||||
return "u"
|
||||
case reflect.Int64:
|
||||
return "x"
|
||||
case reflect.Uint64:
|
||||
return "t"
|
||||
case reflect.Float64:
|
||||
return "d"
|
||||
case reflect.Ptr:
|
||||
return getSignature(t.Elem(), depth)
|
||||
case reflect.String:
|
||||
if t == objectPathType {
|
||||
return "o"
|
||||
}
|
||||
return "s"
|
||||
case reflect.Struct:
|
||||
if t == variantType {
|
||||
return "v"
|
||||
} else if t == signatureType {
|
||||
return "g"
|
||||
}
|
||||
var s string
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||
s += getSignature(t.Field(i).Type, depth.EnterStruct())
|
||||
}
|
||||
}
|
||||
if len(s) == 0 {
|
||||
panic(InvalidTypeError{t})
|
||||
}
|
||||
return "(" + s + ")"
|
||||
case reflect.Array, reflect.Slice:
|
||||
return "a" + getSignature(t.Elem(), depth.EnterArray())
|
||||
case reflect.Map:
|
||||
if !isKeyType(t.Key()) {
|
||||
panic(InvalidTypeError{t})
|
||||
}
|
||||
return "a{" + getSignature(t.Key(), depth.EnterArray().EnterDictEntry()) + getSignature(t.Elem(), depth.EnterArray().EnterDictEntry()) + "}"
|
||||
case reflect.Interface:
|
||||
return "v"
|
||||
}
|
||||
panic(InvalidTypeError{t})
|
||||
}
|
||||
|
||||
// ParseSignature returns the signature represented by this string, or a
|
||||
// SignatureError if the string is not a valid signature.
|
||||
func ParseSignature(s string) (sig Signature, err error) {
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
if len(s) > 255 {
|
||||
return Signature{""}, SignatureError{s, "too long"}
|
||||
}
|
||||
sig.str = s
|
||||
for err == nil && len(s) != 0 {
|
||||
err, s = validSingle(s, &depthCounter{})
|
||||
}
|
||||
if err != nil {
|
||||
sig = Signature{""}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ParseSignatureMust behaves like ParseSignature, except that it panics if s
|
||||
// is not valid.
|
||||
func ParseSignatureMust(s string) Signature {
|
||||
sig, err := ParseSignature(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sig
|
||||
}
|
||||
|
||||
// Empty returns whether the signature is the empty signature.
|
||||
func (s Signature) Empty() bool {
|
||||
return s.str == ""
|
||||
}
|
||||
|
||||
// Single returns whether the signature represents a single, complete type.
|
||||
func (s Signature) Single() bool {
|
||||
err, r := validSingle(s.str, &depthCounter{})
|
||||
return err != nil && r == ""
|
||||
}
|
||||
|
||||
// String returns the signature's string representation.
|
||||
func (s Signature) String() string {
|
||||
return s.str
|
||||
}
|
||||
|
||||
// A SignatureError indicates that a signature passed to a function or received
|
||||
// on a connection is not a valid signature.
|
||||
type SignatureError struct {
|
||||
Sig string
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e SignatureError) Error() string {
|
||||
return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
|
||||
}
|
||||
|
||||
type depthCounter struct {
|
||||
arrayDepth, structDepth, dictEntryDepth int
|
||||
}
|
||||
|
||||
func (cnt *depthCounter) Valid() bool {
|
||||
return cnt.arrayDepth <= 32 && cnt.structDepth <= 32 && cnt.dictEntryDepth <= 32
|
||||
}
|
||||
|
||||
func (cnt depthCounter) EnterArray() *depthCounter {
|
||||
cnt.arrayDepth++
|
||||
return &cnt
|
||||
}
|
||||
|
||||
func (cnt depthCounter) EnterStruct() *depthCounter {
|
||||
cnt.structDepth++
|
||||
return &cnt
|
||||
}
|
||||
|
||||
func (cnt depthCounter) EnterDictEntry() *depthCounter {
|
||||
cnt.dictEntryDepth++
|
||||
return &cnt
|
||||
}
|
||||
|
||||
// Try to read a single type from this string. If it was successful, err is nil
|
||||
// and rem is the remaining unparsed part. Otherwise, err is a non-nil
|
||||
// SignatureError and rem is "". depth is the current recursion depth which may
|
||||
// not be greater than 64 and should be given as 0 on the first call.
|
||||
func validSingle(s string, depth *depthCounter) (err error, rem string) {
|
||||
if s == "" {
|
||||
return SignatureError{Sig: s, Reason: "empty signature"}, ""
|
||||
}
|
||||
if !depth.Valid() {
|
||||
return SignatureError{Sig: s, Reason: "container nesting too deep"}, ""
|
||||
}
|
||||
switch s[0] {
|
||||
case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h':
|
||||
return nil, s[1:]
|
||||
case 'a':
|
||||
if len(s) > 1 && s[1] == '{' {
|
||||
i := findMatching(s[1:], '{', '}')
|
||||
if i == -1 {
|
||||
return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""
|
||||
}
|
||||
i++
|
||||
rem = s[i+1:]
|
||||
s = s[2:i]
|
||||
if err, _ = validSingle(s[:1], depth.EnterArray().EnterDictEntry()); err != nil {
|
||||
return err, ""
|
||||
}
|
||||
err, nr := validSingle(s[1:], depth.EnterArray().EnterDictEntry())
|
||||
if err != nil {
|
||||
return err, ""
|
||||
}
|
||||
if nr != "" {
|
||||
return SignatureError{Sig: s, Reason: "too many types in dict"}, ""
|
||||
}
|
||||
return nil, rem
|
||||
}
|
||||
return validSingle(s[1:], depth.EnterArray())
|
||||
case '(':
|
||||
i := findMatching(s, '(', ')')
|
||||
if i == -1 {
|
||||
return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""
|
||||
}
|
||||
rem = s[i+1:]
|
||||
s = s[1:i]
|
||||
for err == nil && s != "" {
|
||||
err, s = validSingle(s, depth.EnterStruct())
|
||||
}
|
||||
if err != nil {
|
||||
rem = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
return SignatureError{Sig: s, Reason: "invalid type character"}, ""
|
||||
}
|
||||
|
||||
func findMatching(s string, left, right rune) int {
|
||||
n := 0
|
||||
for i, v := range s {
|
||||
if v == left {
|
||||
n++
|
||||
} else if v == right {
|
||||
n--
|
||||
}
|
||||
if n == 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// typeFor returns the type of the given signature. It ignores any left over
|
||||
// characters and panics if s doesn't start with a valid type signature.
|
||||
func typeFor(s string) (t reflect.Type) {
|
||||
err, _ := validSingle(s, &depthCounter{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if t, ok := sigToType[s[0]]; ok {
|
||||
return t
|
||||
}
|
||||
switch s[0] {
|
||||
case 'a':
|
||||
if s[1] == '{' {
|
||||
i := strings.LastIndex(s, "}")
|
||||
t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))
|
||||
} else {
|
||||
t = reflect.SliceOf(typeFor(s[1:]))
|
||||
}
|
||||
case '(':
|
||||
t = interfacesType
|
||||
}
|
||||
return
|
||||
}
|
||||
6
vendor/github.com/godbus/dbus/v5/transport_darwin.go
generated
vendored
Normal file
6
vendor/github.com/godbus/dbus/v5/transport_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package dbus
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
_, err := t.Write([]byte{0})
|
||||
return err
|
||||
}
|
||||
52
vendor/github.com/godbus/dbus/v5/transport_generic.go
generated
vendored
Normal file
52
vendor/github.com/godbus/dbus/v5/transport_generic.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var nativeEndian binary.ByteOrder
|
||||
|
||||
func detectEndianness() binary.ByteOrder {
|
||||
var x uint32 = 0x01020304
|
||||
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||
return binary.BigEndian
|
||||
}
|
||||
return binary.LittleEndian
|
||||
}
|
||||
|
||||
func init() {
|
||||
nativeEndian = detectEndianness()
|
||||
}
|
||||
|
||||
type genericTransport struct {
|
||||
io.ReadWriteCloser
|
||||
}
|
||||
|
||||
func (t genericTransport) SendNullByte() error {
|
||||
_, err := t.Write([]byte{0})
|
||||
return err
|
||||
}
|
||||
|
||||
func (t genericTransport) SupportsUnixFDs() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t genericTransport) EnableUnixFDs() {}
|
||||
|
||||
func (t genericTransport) ReadMessage() (*Message, error) {
|
||||
return DecodeMessage(t)
|
||||
}
|
||||
|
||||
func (t genericTransport) SendMessage(msg *Message) error {
|
||||
fds, err := msg.CountFds()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fds != 0 {
|
||||
return errors.New("dbus: unix fd passing not enabled")
|
||||
}
|
||||
return msg.EncodeTo(t, nativeEndian)
|
||||
}
|
||||
39
vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go
generated
vendored
Normal file
39
vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
//+build !windows
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
)
|
||||
|
||||
func init() {
|
||||
transports["nonce-tcp"] = newNonceTcpTransport
|
||||
}
|
||||
|
||||
func newNonceTcpTransport(keys string) (transport, error) {
|
||||
host := getKey(keys, "host")
|
||||
port := getKey(keys, "port")
|
||||
noncefile := getKey(keys, "noncefile")
|
||||
if host == "" || port == "" || noncefile == "" {
|
||||
return nil, errors.New("dbus: unsupported address (must set host, port and noncefile)")
|
||||
}
|
||||
protocol, err := tcpFamily(keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
socket, err := net.Dial(protocol, net.JoinHostPort(host, port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := ioutil.ReadFile(noncefile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = socket.Write(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewConn(socket)
|
||||
}
|
||||
41
vendor/github.com/godbus/dbus/v5/transport_tcp.go
generated
vendored
Normal file
41
vendor/github.com/godbus/dbus/v5/transport_tcp.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
func init() {
|
||||
transports["tcp"] = newTcpTransport
|
||||
}
|
||||
|
||||
func tcpFamily(keys string) (string, error) {
|
||||
switch getKey(keys, "family") {
|
||||
case "":
|
||||
return "tcp", nil
|
||||
case "ipv4":
|
||||
return "tcp4", nil
|
||||
case "ipv6":
|
||||
return "tcp6", nil
|
||||
default:
|
||||
return "", errors.New("dbus: invalid tcp family (must be ipv4 or ipv6)")
|
||||
}
|
||||
}
|
||||
|
||||
func newTcpTransport(keys string) (transport, error) {
|
||||
host := getKey(keys, "host")
|
||||
port := getKey(keys, "port")
|
||||
if host == "" || port == "" {
|
||||
return nil, errors.New("dbus: unsupported address (must set host and port)")
|
||||
}
|
||||
|
||||
protocol, err := tcpFamily(keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
socket, err := net.Dial(protocol, net.JoinHostPort(host, port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewConn(socket)
|
||||
}
|
||||
212
vendor/github.com/godbus/dbus/v5/transport_unix.go
generated
vendored
Normal file
212
vendor/github.com/godbus/dbus/v5/transport_unix.go
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
//+build !windows,!solaris
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type oobReader struct {
|
||||
conn *net.UnixConn
|
||||
oob []byte
|
||||
buf [4096]byte
|
||||
}
|
||||
|
||||
func (o *oobReader) Read(b []byte) (n int, err error) {
|
||||
n, oobn, flags, _, err := o.conn.ReadMsgUnix(b, o.buf[:])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if flags&syscall.MSG_CTRUNC != 0 {
|
||||
return n, errors.New("dbus: control data truncated (too many fds received)")
|
||||
}
|
||||
o.oob = append(o.oob, o.buf[:oobn]...)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type unixTransport struct {
|
||||
*net.UnixConn
|
||||
rdr *oobReader
|
||||
hasUnixFDs bool
|
||||
}
|
||||
|
||||
func newUnixTransport(keys string) (transport, error) {
|
||||
var err error
|
||||
|
||||
t := new(unixTransport)
|
||||
abstract := getKey(keys, "abstract")
|
||||
path := getKey(keys, "path")
|
||||
switch {
|
||||
case abstract == "" && path == "":
|
||||
return nil, errors.New("dbus: invalid address (neither path nor abstract set)")
|
||||
case abstract != "" && path == "":
|
||||
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: "@" + abstract, Net: "unix"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
case abstract == "" && path != "":
|
||||
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: path, Net: "unix"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
default:
|
||||
return nil, errors.New("dbus: invalid address (both path and abstract set)")
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
transports["unix"] = newUnixTransport
|
||||
}
|
||||
|
||||
func (t *unixTransport) EnableUnixFDs() {
|
||||
t.hasUnixFDs = true
|
||||
}
|
||||
|
||||
func (t *unixTransport) ReadMessage() (*Message, error) {
|
||||
var (
|
||||
blen, hlen uint32
|
||||
csheader [16]byte
|
||||
headers []header
|
||||
order binary.ByteOrder
|
||||
unixfds uint32
|
||||
)
|
||||
// To be sure that all bytes of out-of-band data are read, we use a special
|
||||
// reader that uses ReadUnix on the underlying connection instead of Read
|
||||
// and gathers the out-of-band data in a buffer.
|
||||
if t.rdr == nil {
|
||||
t.rdr = &oobReader{conn: t.UnixConn}
|
||||
} else {
|
||||
t.rdr.oob = nil
|
||||
}
|
||||
|
||||
// read the first 16 bytes (the part of the header that has a constant size),
|
||||
// from which we can figure out the length of the rest of the message
|
||||
if _, err := io.ReadFull(t.rdr, csheader[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch csheader[0] {
|
||||
case 'l':
|
||||
order = binary.LittleEndian
|
||||
case 'B':
|
||||
order = binary.BigEndian
|
||||
default:
|
||||
return nil, InvalidMessageError("invalid byte order")
|
||||
}
|
||||
// csheader[4:8] -> length of message body, csheader[12:16] -> length of
|
||||
// header fields (without alignment)
|
||||
binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen)
|
||||
binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen)
|
||||
if hlen%8 != 0 {
|
||||
hlen += 8 - (hlen % 8)
|
||||
}
|
||||
|
||||
// decode headers and look for unix fds
|
||||
headerdata := make([]byte, hlen+4)
|
||||
copy(headerdata, csheader[12:])
|
||||
if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dec := newDecoder(bytes.NewBuffer(headerdata), order, make([]int, 0))
|
||||
dec.pos = 12
|
||||
vs, err := dec.Decode(Signature{"a(yv)"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
Store(vs, &headers)
|
||||
for _, v := range headers {
|
||||
if v.Field == byte(FieldUnixFDs) {
|
||||
unixfds, _ = v.Variant.value.(uint32)
|
||||
}
|
||||
}
|
||||
all := make([]byte, 16+hlen+blen)
|
||||
copy(all, csheader[:])
|
||||
copy(all[16:], headerdata[4:])
|
||||
if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unixfds != 0 {
|
||||
if !t.hasUnixFDs {
|
||||
return nil, errors.New("dbus: got unix fds on unsupported transport")
|
||||
}
|
||||
// read the fds from the OOB data
|
||||
scms, err := syscall.ParseSocketControlMessage(t.rdr.oob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(scms) != 1 {
|
||||
return nil, errors.New("dbus: received more than one socket control message")
|
||||
}
|
||||
fds, err := syscall.ParseUnixRights(&scms[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg, err := DecodeMessageWithFDs(bytes.NewBuffer(all), fds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// substitute the values in the message body (which are indices for the
|
||||
// array receiver via OOB) with the actual values
|
||||
for i, v := range msg.Body {
|
||||
switch index := v.(type) {
|
||||
case UnixFDIndex:
|
||||
if uint32(index) >= unixfds {
|
||||
return nil, InvalidMessageError("invalid index for unix fd")
|
||||
}
|
||||
msg.Body[i] = UnixFD(fds[index])
|
||||
case []UnixFDIndex:
|
||||
fdArray := make([]UnixFD, len(index))
|
||||
for k, j := range index {
|
||||
if uint32(j) >= unixfds {
|
||||
return nil, InvalidMessageError("invalid index for unix fd")
|
||||
}
|
||||
fdArray[k] = UnixFD(fds[j])
|
||||
}
|
||||
msg.Body[i] = fdArray
|
||||
}
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
return DecodeMessage(bytes.NewBuffer(all))
|
||||
}
|
||||
|
||||
func (t *unixTransport) SendMessage(msg *Message) error {
|
||||
fdcnt, err := msg.CountFds()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fdcnt != 0 {
|
||||
if !t.hasUnixFDs {
|
||||
return errors.New("dbus: unix fd passing not enabled")
|
||||
}
|
||||
msg.Headers[FieldUnixFDs] = MakeVariant(uint32(fdcnt))
|
||||
buf := new(bytes.Buffer)
|
||||
fds, err := msg.EncodeToWithFDs(buf, nativeEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oob := syscall.UnixRights(fds...)
|
||||
n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != buf.Len() || oobn != len(oob) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
} else {
|
||||
if err := msg.EncodeTo(t, nativeEndian); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *unixTransport) SupportsUnixFDs() bool {
|
||||
return true
|
||||
}
|
||||
95
vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go
generated
vendored
Normal file
95
vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// The UnixCredentials system call is currently only implemented on Linux
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// https://golang.org/s/go1.4-syscall
|
||||
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||
|
||||
// Local implementation of the UnixCredentials system call for DragonFly BSD
|
||||
|
||||
package dbus
|
||||
|
||||
/*
|
||||
#include <sys/ucred.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
|
||||
// http://golang.org/src/pkg/syscall/ztypes_dragonfly_amd64.go
|
||||
type Ucred struct {
|
||||
Pid int32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/types_linux.go
|
||||
// http://golang.org/src/pkg/syscall/types_dragonfly.go
|
||||
// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/ucred.h
|
||||
const (
|
||||
SizeofUcred = C.sizeof_struct_ucred
|
||||
)
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
func cmsgAlignOf(salen int) int {
|
||||
// From http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
//salign := sizeofPtr
|
||||
// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
|
||||
// still require 32-bit aligned access to network subsystem.
|
||||
//if darwin64Bit || dragonfly64Bit {
|
||||
// salign = 4
|
||||
//}
|
||||
salign := 4
|
||||
return (salen + salign - 1) & ^(salign - 1)
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// UnixCredentials encodes credentials into a socket control message
|
||||
// for sending to another process. This can be used for
|
||||
// authentication.
|
||||
func UnixCredentials(ucred *Ucred) []byte {
|
||||
b := make([]byte, syscall.CmsgSpace(SizeofUcred))
|
||||
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
h.Level = syscall.SOL_SOCKET
|
||||
h.Type = syscall.SCM_CREDS
|
||||
h.SetLen(syscall.CmsgLen(SizeofUcred))
|
||||
*((*Ucred)(cmsgData(h))) = *ucred
|
||||
return b
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// ParseUnixCredentials decodes a socket control message that contains
|
||||
// credentials in a Ucred structure. To receive such a message, the
|
||||
// SO_PASSCRED option must be enabled on the socket.
|
||||
func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
|
||||
if m.Header.Level != syscall.SOL_SOCKET {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if m.Header.Type != syscall.SCM_CREDS {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||
return &ucred, nil
|
||||
}
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||
b := UnixCredentials(ucred)
|
||||
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oobn != len(b) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
92
vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go
generated
vendored
Normal file
92
vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// The UnixCredentials system call is currently only implemented on Linux
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// https://golang.org/s/go1.4-syscall
|
||||
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||
|
||||
// Local implementation of the UnixCredentials system call for FreeBSD
|
||||
|
||||
package dbus
|
||||
|
||||
/*
|
||||
const int sizeofPtr = sizeof(void*);
|
||||
#define _WANT_UCRED
|
||||
#include <sys/types.h>
|
||||
#include <sys/ucred.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
|
||||
// https://golang.org/src/syscall/ztypes_freebsd_amd64.go
|
||||
type Ucred struct {
|
||||
Pid int32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/types_linux.go
|
||||
// https://golang.org/src/syscall/types_freebsd.go
|
||||
// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h
|
||||
const (
|
||||
SizeofUcred = C.sizeof_struct_ucred
|
||||
)
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
func cmsgAlignOf(salen int) int {
|
||||
salign := C.sizeofPtr
|
||||
|
||||
return (salen + salign - 1) & ^(salign - 1)
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// UnixCredentials encodes credentials into a socket control message
|
||||
// for sending to another process. This can be used for
|
||||
// authentication.
|
||||
func UnixCredentials(ucred *Ucred) []byte {
|
||||
b := make([]byte, syscall.CmsgSpace(SizeofUcred))
|
||||
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
h.Level = syscall.SOL_SOCKET
|
||||
h.Type = syscall.SCM_CREDS
|
||||
h.SetLen(syscall.CmsgLen(SizeofUcred))
|
||||
*((*Ucred)(cmsgData(h))) = *ucred
|
||||
return b
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// ParseUnixCredentials decodes a socket control message that contains
|
||||
// credentials in a Ucred structure. To receive such a message, the
|
||||
// SO_PASSCRED option must be enabled on the socket.
|
||||
func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
|
||||
if m.Header.Level != syscall.SOL_SOCKET {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if m.Header.Type != syscall.SCM_CREDS {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||
return &ucred, nil
|
||||
}
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||
b := UnixCredentials(ucred)
|
||||
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oobn != len(b) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
25
vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go
generated
vendored
Normal file
25
vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// The UnixCredentials system call is currently only implemented on Linux
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// https://golang.org/s/go1.4-syscall
|
||||
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
ucred := &syscall.Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||
b := syscall.UnixCredentials(ucred)
|
||||
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oobn != len(b) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
generated
vendored
Normal file
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package dbus
|
||||
|
||||
import "io"
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go
generated
vendored
Normal file
14
vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package dbus
|
||||
|
||||
import "io"
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
6
vendor/github.com/godbus/dbus/v5/transport_zos.go
generated
vendored
Normal file
6
vendor/github.com/godbus/dbus/v5/transport_zos.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package dbus
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
_, err := t.Write([]byte{0})
|
||||
return err
|
||||
}
|
||||
150
vendor/github.com/godbus/dbus/v5/variant.go
generated
vendored
Normal file
150
vendor/github.com/godbus/dbus/v5/variant.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Variant represents the D-Bus variant type.
|
||||
type Variant struct {
|
||||
sig Signature
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// MakeVariant converts the given value to a Variant. It panics if v cannot be
|
||||
// represented as a D-Bus type.
|
||||
func MakeVariant(v interface{}) Variant {
|
||||
return MakeVariantWithSignature(v, SignatureOf(v))
|
||||
}
|
||||
|
||||
// MakeVariantWithSignature converts the given value to a Variant.
|
||||
func MakeVariantWithSignature(v interface{}, s Signature) Variant {
|
||||
return Variant{s, v}
|
||||
}
|
||||
|
||||
// ParseVariant parses the given string as a variant as described at
|
||||
// https://developer.gnome.org/glib/stable/gvariant-text.html. If sig is not
|
||||
// empty, it is taken to be the expected signature for the variant.
|
||||
func ParseVariant(s string, sig Signature) (Variant, error) {
|
||||
tokens := varLex(s)
|
||||
p := &varParser{tokens: tokens}
|
||||
n, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
if sig.str == "" {
|
||||
sig, err = varInfer(n)
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
}
|
||||
v, err := n.Value(sig)
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
return MakeVariant(v), nil
|
||||
}
|
||||
|
||||
// format returns a formatted version of v and whether this string can be parsed
|
||||
// unambiguously.
|
||||
func (v Variant) format() (string, bool) {
|
||||
switch v.sig.str[0] {
|
||||
case 'b', 'i':
|
||||
return fmt.Sprint(v.value), true
|
||||
case 'n', 'q', 'u', 'x', 't', 'd', 'h':
|
||||
return fmt.Sprint(v.value), false
|
||||
case 's':
|
||||
return strconv.Quote(v.value.(string)), true
|
||||
case 'o':
|
||||
return strconv.Quote(string(v.value.(ObjectPath))), false
|
||||
case 'g':
|
||||
return strconv.Quote(v.value.(Signature).str), false
|
||||
case 'v':
|
||||
s, unamb := v.value.(Variant).format()
|
||||
if !unamb {
|
||||
return "<@" + v.value.(Variant).sig.str + " " + s + ">", true
|
||||
}
|
||||
return "<" + s + ">", true
|
||||
case 'y':
|
||||
return fmt.Sprintf("%#x", v.value.(byte)), false
|
||||
}
|
||||
rv := reflect.ValueOf(v.value)
|
||||
switch rv.Kind() {
|
||||
case reflect.Slice:
|
||||
if rv.Len() == 0 {
|
||||
return "[]", false
|
||||
}
|
||||
unamb := true
|
||||
buf := bytes.NewBuffer([]byte("["))
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
// TODO: slooow
|
||||
s, b := MakeVariant(rv.Index(i).Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.WriteString(s)
|
||||
if i != rv.Len()-1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String(), unamb
|
||||
case reflect.Map:
|
||||
if rv.Len() == 0 {
|
||||
return "{}", false
|
||||
}
|
||||
unamb := true
|
||||
var buf bytes.Buffer
|
||||
kvs := make([]string, rv.Len())
|
||||
for i, k := range rv.MapKeys() {
|
||||
s, b := MakeVariant(k.Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.Reset()
|
||||
buf.WriteString(s)
|
||||
buf.WriteString(": ")
|
||||
s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.WriteString(s)
|
||||
kvs[i] = buf.String()
|
||||
}
|
||||
buf.Reset()
|
||||
buf.WriteByte('{')
|
||||
sort.Strings(kvs)
|
||||
for i, kv := range kvs {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(kv)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.String(), unamb
|
||||
}
|
||||
return `"INVALID"`, true
|
||||
}
|
||||
|
||||
// Signature returns the D-Bus signature of the underlying value of v.
|
||||
func (v Variant) Signature() Signature {
|
||||
return v.sig
|
||||
}
|
||||
|
||||
// String returns the string representation of the underlying value of v as
|
||||
// described at https://developer.gnome.org/glib/stable/gvariant-text.html.
|
||||
func (v Variant) String() string {
|
||||
s, unamb := v.format()
|
||||
if !unamb {
|
||||
return "@" + v.sig.str + " " + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Value returns the underlying value of v.
|
||||
func (v Variant) Value() interface{} {
|
||||
return v.value
|
||||
}
|
||||
|
||||
// Store converts the variant into a native go type using the same
|
||||
// mechanism as the "Store" function.
|
||||
func (v Variant) Store(value interface{}) error {
|
||||
return storeInterfaces(v.value, value)
|
||||
}
|
||||
284
vendor/github.com/godbus/dbus/v5/variant_lexer.go
generated
vendored
Normal file
284
vendor/github.com/godbus/dbus/v5/variant_lexer.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Heavily inspired by the lexer from text/template.
|
||||
|
||||
type varToken struct {
|
||||
typ varTokenType
|
||||
val string
|
||||
}
|
||||
|
||||
type varTokenType byte
|
||||
|
||||
const (
|
||||
tokEOF varTokenType = iota
|
||||
tokError
|
||||
tokNumber
|
||||
tokString
|
||||
tokBool
|
||||
tokArrayStart
|
||||
tokArrayEnd
|
||||
tokDictStart
|
||||
tokDictEnd
|
||||
tokVariantStart
|
||||
tokVariantEnd
|
||||
tokComma
|
||||
tokColon
|
||||
tokType
|
||||
tokByteString
|
||||
)
|
||||
|
||||
type varLexer struct {
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
width int
|
||||
tokens []varToken
|
||||
}
|
||||
|
||||
type lexState func(*varLexer) lexState
|
||||
|
||||
func varLex(s string) []varToken {
|
||||
l := &varLexer{input: s}
|
||||
l.run()
|
||||
return l.tokens
|
||||
}
|
||||
|
||||
func (l *varLexer) accept(valid string) bool {
|
||||
if strings.ContainsRune(valid, l.next()) {
|
||||
return true
|
||||
}
|
||||
l.backup()
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *varLexer) backup() {
|
||||
l.pos -= l.width
|
||||
}
|
||||
|
||||
func (l *varLexer) emit(t varTokenType) {
|
||||
l.tokens = append(l.tokens, varToken{t, l.input[l.start:l.pos]})
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
func (l *varLexer) errorf(format string, v ...interface{}) lexState {
|
||||
l.tokens = append(l.tokens, varToken{
|
||||
tokError,
|
||||
fmt.Sprintf(format, v...),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *varLexer) ignore() {
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
func (l *varLexer) next() rune {
|
||||
var r rune
|
||||
|
||||
if l.pos >= len(l.input) {
|
||||
l.width = 0
|
||||
return -1
|
||||
}
|
||||
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
|
||||
l.pos += l.width
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *varLexer) run() {
|
||||
for state := varLexNormal; state != nil; {
|
||||
state = state(l)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *varLexer) peek() rune {
|
||||
r := l.next()
|
||||
l.backup()
|
||||
return r
|
||||
}
|
||||
|
||||
func varLexNormal(l *varLexer) lexState {
|
||||
for {
|
||||
r := l.next()
|
||||
switch {
|
||||
case r == -1:
|
||||
l.emit(tokEOF)
|
||||
return nil
|
||||
case r == '[':
|
||||
l.emit(tokArrayStart)
|
||||
case r == ']':
|
||||
l.emit(tokArrayEnd)
|
||||
case r == '{':
|
||||
l.emit(tokDictStart)
|
||||
case r == '}':
|
||||
l.emit(tokDictEnd)
|
||||
case r == '<':
|
||||
l.emit(tokVariantStart)
|
||||
case r == '>':
|
||||
l.emit(tokVariantEnd)
|
||||
case r == ':':
|
||||
l.emit(tokColon)
|
||||
case r == ',':
|
||||
l.emit(tokComma)
|
||||
case r == '\'' || r == '"':
|
||||
l.backup()
|
||||
return varLexString
|
||||
case r == '@':
|
||||
l.backup()
|
||||
return varLexType
|
||||
case unicode.IsSpace(r):
|
||||
l.ignore()
|
||||
case unicode.IsNumber(r) || r == '+' || r == '-':
|
||||
l.backup()
|
||||
return varLexNumber
|
||||
case r == 'b':
|
||||
pos := l.start
|
||||
if n := l.peek(); n == '"' || n == '\'' {
|
||||
return varLexByteString
|
||||
}
|
||||
// not a byte string; try to parse it as a type or bool below
|
||||
l.pos = pos + 1
|
||||
l.width = 1
|
||||
fallthrough
|
||||
default:
|
||||
// either a bool or a type. Try bools first.
|
||||
l.backup()
|
||||
if l.pos+4 <= len(l.input) {
|
||||
if l.input[l.pos:l.pos+4] == "true" {
|
||||
l.pos += 4
|
||||
l.emit(tokBool)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if l.pos+5 <= len(l.input) {
|
||||
if l.input[l.pos:l.pos+5] == "false" {
|
||||
l.pos += 5
|
||||
l.emit(tokBool)
|
||||
continue
|
||||
}
|
||||
}
|
||||
// must be a type.
|
||||
return varLexType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var varTypeMap = map[string]string{
|
||||
"boolean": "b",
|
||||
"byte": "y",
|
||||
"int16": "n",
|
||||
"uint16": "q",
|
||||
"int32": "i",
|
||||
"uint32": "u",
|
||||
"int64": "x",
|
||||
"uint64": "t",
|
||||
"double": "f",
|
||||
"string": "s",
|
||||
"objectpath": "o",
|
||||
"signature": "g",
|
||||
}
|
||||
|
||||
func varLexByteString(l *varLexer) lexState {
|
||||
q := l.next()
|
||||
Loop:
|
||||
for {
|
||||
switch l.next() {
|
||||
case '\\':
|
||||
if r := l.next(); r != -1 {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case -1:
|
||||
return l.errorf("unterminated bytestring")
|
||||
case q:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
l.emit(tokByteString)
|
||||
return varLexNormal
|
||||
}
|
||||
|
||||
func varLexNumber(l *varLexer) lexState {
|
||||
l.accept("+-")
|
||||
digits := "0123456789"
|
||||
if l.accept("0") {
|
||||
if l.accept("x") {
|
||||
digits = "0123456789abcdefABCDEF"
|
||||
} else {
|
||||
digits = "01234567"
|
||||
}
|
||||
}
|
||||
for strings.ContainsRune(digits, l.next()) {
|
||||
}
|
||||
l.backup()
|
||||
if l.accept(".") {
|
||||
for strings.ContainsRune(digits, l.next()) {
|
||||
}
|
||||
l.backup()
|
||||
}
|
||||
if l.accept("eE") {
|
||||
l.accept("+-")
|
||||
for strings.ContainsRune("0123456789", l.next()) {
|
||||
}
|
||||
l.backup()
|
||||
}
|
||||
if r := l.peek(); unicode.IsLetter(r) {
|
||||
l.next()
|
||||
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
||||
}
|
||||
l.emit(tokNumber)
|
||||
return varLexNormal
|
||||
}
|
||||
|
||||
func varLexString(l *varLexer) lexState {
|
||||
q := l.next()
|
||||
Loop:
|
||||
for {
|
||||
switch l.next() {
|
||||
case '\\':
|
||||
if r := l.next(); r != -1 {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case -1:
|
||||
return l.errorf("unterminated string")
|
||||
case q:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
l.emit(tokString)
|
||||
return varLexNormal
|
||||
}
|
||||
|
||||
func varLexType(l *varLexer) lexState {
|
||||
at := l.accept("@")
|
||||
for {
|
||||
r := l.next()
|
||||
if r == -1 {
|
||||
break
|
||||
}
|
||||
if unicode.IsSpace(r) {
|
||||
l.backup()
|
||||
break
|
||||
}
|
||||
}
|
||||
if at {
|
||||
if _, err := ParseSignature(l.input[l.start+1 : l.pos]); err != nil {
|
||||
return l.errorf("%s", err)
|
||||
}
|
||||
} else {
|
||||
if _, ok := varTypeMap[l.input[l.start:l.pos]]; ok {
|
||||
l.emit(tokType)
|
||||
return varLexNormal
|
||||
}
|
||||
return l.errorf("unrecognized type %q", l.input[l.start:l.pos])
|
||||
}
|
||||
l.emit(tokType)
|
||||
return varLexNormal
|
||||
}
|
||||
817
vendor/github.com/godbus/dbus/v5/variant_parser.go
generated
vendored
Normal file
817
vendor/github.com/godbus/dbus/v5/variant_parser.go
generated
vendored
Normal file
@@ -0,0 +1,817 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type varParser struct {
|
||||
tokens []varToken
|
||||
i int
|
||||
}
|
||||
|
||||
func (p *varParser) backup() {
|
||||
p.i--
|
||||
}
|
||||
|
||||
func (p *varParser) next() varToken {
|
||||
if p.i < len(p.tokens) {
|
||||
t := p.tokens[p.i]
|
||||
p.i++
|
||||
return t
|
||||
}
|
||||
return varToken{typ: tokEOF}
|
||||
}
|
||||
|
||||
type varNode interface {
|
||||
Infer() (Signature, error)
|
||||
String() string
|
||||
Sigs() sigSet
|
||||
Value(Signature) (interface{}, error)
|
||||
}
|
||||
|
||||
func varMakeNode(p *varParser) (varNode, error) {
|
||||
var sig Signature
|
||||
|
||||
for {
|
||||
t := p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokNumber:
|
||||
return varMakeNumNode(t, sig)
|
||||
case tokString:
|
||||
return varMakeStringNode(t, sig)
|
||||
case tokBool:
|
||||
if sig.str != "" && sig.str != "b" {
|
||||
return nil, varTypeError{t.val, sig}
|
||||
}
|
||||
b, err := strconv.ParseBool(t.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return boolNode(b), nil
|
||||
case tokArrayStart:
|
||||
return varMakeArrayNode(p, sig)
|
||||
case tokVariantStart:
|
||||
return varMakeVariantNode(p, sig)
|
||||
case tokDictStart:
|
||||
return varMakeDictNode(p, sig)
|
||||
case tokType:
|
||||
if sig.str != "" {
|
||||
return nil, errors.New("unexpected type annotation")
|
||||
}
|
||||
if t.val[0] == '@' {
|
||||
sig.str = t.val[1:]
|
||||
} else {
|
||||
sig.str = varTypeMap[t.val]
|
||||
}
|
||||
case tokByteString:
|
||||
if sig.str != "" && sig.str != "ay" {
|
||||
return nil, varTypeError{t.val, sig}
|
||||
}
|
||||
b, err := varParseByteString(t.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return byteStringNode(b), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type varTypeError struct {
|
||||
val string
|
||||
sig Signature
|
||||
}
|
||||
|
||||
func (e varTypeError) Error() string {
|
||||
return fmt.Sprintf("dbus: can't parse %q as type %q", e.val, e.sig.str)
|
||||
}
|
||||
|
||||
type sigSet map[Signature]bool
|
||||
|
||||
func (s sigSet) Empty() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
func (s sigSet) Intersect(s2 sigSet) sigSet {
|
||||
r := make(sigSet)
|
||||
for k := range s {
|
||||
if s2[k] {
|
||||
r[k] = true
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (s sigSet) Single() (Signature, bool) {
|
||||
if len(s) == 1 {
|
||||
for k := range s {
|
||||
return k, true
|
||||
}
|
||||
}
|
||||
return Signature{}, false
|
||||
}
|
||||
|
||||
func (s sigSet) ToArray() sigSet {
|
||||
r := make(sigSet, len(s))
|
||||
for k := range s {
|
||||
r[Signature{"a" + k.str}] = true
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type numNode struct {
|
||||
sig Signature
|
||||
str string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
var numSigSet = sigSet{
|
||||
Signature{"y"}: true,
|
||||
Signature{"n"}: true,
|
||||
Signature{"q"}: true,
|
||||
Signature{"i"}: true,
|
||||
Signature{"u"}: true,
|
||||
Signature{"x"}: true,
|
||||
Signature{"t"}: true,
|
||||
Signature{"d"}: true,
|
||||
}
|
||||
|
||||
func (n numNode) Infer() (Signature, error) {
|
||||
if strings.ContainsAny(n.str, ".e") {
|
||||
return Signature{"d"}, nil
|
||||
}
|
||||
return Signature{"i"}, nil
|
||||
}
|
||||
|
||||
func (n numNode) String() string {
|
||||
return n.str
|
||||
}
|
||||
|
||||
func (n numNode) Sigs() sigSet {
|
||||
if n.sig.str != "" {
|
||||
return sigSet{n.sig: true}
|
||||
}
|
||||
if strings.ContainsAny(n.str, ".e") {
|
||||
return sigSet{Signature{"d"}: true}
|
||||
}
|
||||
return numSigSet
|
||||
}
|
||||
|
||||
func (n numNode) Value(sig Signature) (interface{}, error) {
|
||||
if n.sig.str != "" && n.sig != sig {
|
||||
return nil, varTypeError{n.str, sig}
|
||||
}
|
||||
if n.val != nil {
|
||||
return n.val, nil
|
||||
}
|
||||
return varNumAs(n.str, sig)
|
||||
}
|
||||
|
||||
func varMakeNumNode(tok varToken, sig Signature) (varNode, error) {
|
||||
if sig.str == "" {
|
||||
return numNode{str: tok.val}, nil
|
||||
}
|
||||
num, err := varNumAs(tok.val, sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return numNode{sig: sig, val: num}, nil
|
||||
}
|
||||
|
||||
func varNumAs(s string, sig Signature) (interface{}, error) {
|
||||
isUnsigned := false
|
||||
size := 32
|
||||
switch sig.str {
|
||||
case "n":
|
||||
size = 16
|
||||
case "i":
|
||||
case "x":
|
||||
size = 64
|
||||
case "y":
|
||||
size = 8
|
||||
isUnsigned = true
|
||||
case "q":
|
||||
size = 16
|
||||
isUnsigned = true
|
||||
case "u":
|
||||
isUnsigned = true
|
||||
case "t":
|
||||
size = 64
|
||||
isUnsigned = true
|
||||
case "d":
|
||||
d, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d, nil
|
||||
default:
|
||||
return nil, varTypeError{s, sig}
|
||||
}
|
||||
base := 10
|
||||
if strings.HasPrefix(s, "0x") {
|
||||
base = 16
|
||||
s = s[2:]
|
||||
}
|
||||
if strings.HasPrefix(s, "0") && len(s) != 1 {
|
||||
base = 8
|
||||
s = s[1:]
|
||||
}
|
||||
if isUnsigned {
|
||||
i, err := strconv.ParseUint(s, base, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v interface{} = i
|
||||
switch sig.str {
|
||||
case "y":
|
||||
v = byte(i)
|
||||
case "q":
|
||||
v = uint16(i)
|
||||
case "u":
|
||||
v = uint32(i)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
i, err := strconv.ParseInt(s, base, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v interface{} = i
|
||||
switch sig.str {
|
||||
case "n":
|
||||
v = int16(i)
|
||||
case "i":
|
||||
v = int32(i)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
type stringNode struct {
|
||||
sig Signature
|
||||
str string // parsed
|
||||
val interface{} // has correct type
|
||||
}
|
||||
|
||||
var stringSigSet = sigSet{
|
||||
Signature{"s"}: true,
|
||||
Signature{"g"}: true,
|
||||
Signature{"o"}: true,
|
||||
}
|
||||
|
||||
func (n stringNode) Infer() (Signature, error) {
|
||||
return Signature{"s"}, nil
|
||||
}
|
||||
|
||||
func (n stringNode) String() string {
|
||||
return n.str
|
||||
}
|
||||
|
||||
func (n stringNode) Sigs() sigSet {
|
||||
if n.sig.str != "" {
|
||||
return sigSet{n.sig: true}
|
||||
}
|
||||
return stringSigSet
|
||||
}
|
||||
|
||||
func (n stringNode) Value(sig Signature) (interface{}, error) {
|
||||
if n.sig.str != "" && n.sig != sig {
|
||||
return nil, varTypeError{n.str, sig}
|
||||
}
|
||||
if n.val != nil {
|
||||
return n.val, nil
|
||||
}
|
||||
switch {
|
||||
case sig.str == "g":
|
||||
return Signature{n.str}, nil
|
||||
case sig.str == "o":
|
||||
return ObjectPath(n.str), nil
|
||||
case sig.str == "s":
|
||||
return n.str, nil
|
||||
default:
|
||||
return nil, varTypeError{n.str, sig}
|
||||
}
|
||||
}
|
||||
|
||||
func varMakeStringNode(tok varToken, sig Signature) (varNode, error) {
|
||||
if sig.str != "" && sig.str != "s" && sig.str != "g" && sig.str != "o" {
|
||||
return nil, fmt.Errorf("invalid type %q for string", sig.str)
|
||||
}
|
||||
s, err := varParseString(tok.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := stringNode{str: s}
|
||||
if sig.str == "" {
|
||||
return stringNode{str: s}, nil
|
||||
}
|
||||
n.sig = sig
|
||||
switch sig.str {
|
||||
case "o":
|
||||
n.val = ObjectPath(s)
|
||||
case "g":
|
||||
n.val = Signature{s}
|
||||
case "s":
|
||||
n.val = s
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func varParseString(s string) (string, error) {
|
||||
// quotes are guaranteed to be there
|
||||
s = s[1 : len(s)-1]
|
||||
buf := new(bytes.Buffer)
|
||||
for len(s) != 0 {
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
return "", errors.New("invalid UTF-8")
|
||||
}
|
||||
s = s[size:]
|
||||
if r != '\\' {
|
||||
buf.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
r, size = utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
return "", errors.New("invalid UTF-8")
|
||||
}
|
||||
s = s[size:]
|
||||
switch r {
|
||||
case 'a':
|
||||
buf.WriteRune(0x7)
|
||||
case 'b':
|
||||
buf.WriteRune(0x8)
|
||||
case 'f':
|
||||
buf.WriteRune(0xc)
|
||||
case 'n':
|
||||
buf.WriteRune('\n')
|
||||
case 'r':
|
||||
buf.WriteRune('\r')
|
||||
case 't':
|
||||
buf.WriteRune('\t')
|
||||
case '\n':
|
||||
case 'u':
|
||||
if len(s) < 4 {
|
||||
return "", errors.New("short unicode escape")
|
||||
}
|
||||
r, err := strconv.ParseUint(s[:4], 16, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteRune(rune(r))
|
||||
s = s[4:]
|
||||
case 'U':
|
||||
if len(s) < 8 {
|
||||
return "", errors.New("short unicode escape")
|
||||
}
|
||||
r, err := strconv.ParseUint(s[:8], 16, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteRune(rune(r))
|
||||
s = s[8:]
|
||||
default:
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
var boolSigSet = sigSet{Signature{"b"}: true}
|
||||
|
||||
type boolNode bool
|
||||
|
||||
func (boolNode) Infer() (Signature, error) {
|
||||
return Signature{"b"}, nil
|
||||
}
|
||||
|
||||
func (b boolNode) String() string {
|
||||
if b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (boolNode) Sigs() sigSet {
|
||||
return boolSigSet
|
||||
}
|
||||
|
||||
func (b boolNode) Value(sig Signature) (interface{}, error) {
|
||||
if sig.str != "b" {
|
||||
return nil, varTypeError{b.String(), sig}
|
||||
}
|
||||
return bool(b), nil
|
||||
}
|
||||
|
||||
type arrayNode struct {
|
||||
set sigSet
|
||||
children []varNode
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func (n arrayNode) Infer() (Signature, error) {
|
||||
for _, v := range n.children {
|
||||
csig, err := varInfer(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return Signature{"a" + csig.str}, nil
|
||||
}
|
||||
return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
|
||||
}
|
||||
|
||||
func (n arrayNode) String() string {
|
||||
s := "["
|
||||
for i, v := range n.children {
|
||||
s += v.String()
|
||||
if i != len(n.children)-1 {
|
||||
s += ", "
|
||||
}
|
||||
}
|
||||
return s + "]"
|
||||
}
|
||||
|
||||
func (n arrayNode) Sigs() sigSet {
|
||||
return n.set
|
||||
}
|
||||
|
||||
func (n arrayNode) Value(sig Signature) (interface{}, error) {
|
||||
if n.set.Empty() {
|
||||
// no type information whatsoever, so this must be an empty slice
|
||||
return reflect.MakeSlice(typeFor(sig.str), 0, 0).Interface(), nil
|
||||
}
|
||||
if !n.set[sig] {
|
||||
return nil, varTypeError{n.String(), sig}
|
||||
}
|
||||
s := reflect.MakeSlice(typeFor(sig.str), len(n.children), len(n.children))
|
||||
for i, v := range n.children {
|
||||
rv, err := v.Value(Signature{sig.str[1:]})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Index(i).Set(reflect.ValueOf(rv))
|
||||
}
|
||||
return s.Interface(), nil
|
||||
}
|
||||
|
||||
func varMakeArrayNode(p *varParser, sig Signature) (varNode, error) {
|
||||
var n arrayNode
|
||||
if sig.str != "" {
|
||||
n.set = sigSet{sig: true}
|
||||
}
|
||||
if t := p.next(); t.typ == tokArrayEnd {
|
||||
return n, nil
|
||||
} else {
|
||||
p.backup()
|
||||
}
|
||||
Loop:
|
||||
for {
|
||||
t := p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
}
|
||||
p.backup()
|
||||
cn, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cset := cn.Sigs(); !cset.Empty() {
|
||||
if n.set.Empty() {
|
||||
n.set = cset.ToArray()
|
||||
} else {
|
||||
nset := cset.ToArray().Intersect(n.set)
|
||||
if nset.Empty() {
|
||||
return nil, fmt.Errorf("can't parse %q with given type information", cn.String())
|
||||
}
|
||||
n.set = nset
|
||||
}
|
||||
}
|
||||
n.children = append(n.children, cn)
|
||||
switch t := p.next(); t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokArrayEnd:
|
||||
break Loop
|
||||
case tokComma:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type variantNode struct {
|
||||
n varNode
|
||||
}
|
||||
|
||||
var variantSet = sigSet{
|
||||
Signature{"v"}: true,
|
||||
}
|
||||
|
||||
func (variantNode) Infer() (Signature, error) {
|
||||
return Signature{"v"}, nil
|
||||
}
|
||||
|
||||
func (n variantNode) String() string {
|
||||
return "<" + n.n.String() + ">"
|
||||
}
|
||||
|
||||
func (variantNode) Sigs() sigSet {
|
||||
return variantSet
|
||||
}
|
||||
|
||||
func (n variantNode) Value(sig Signature) (interface{}, error) {
|
||||
if sig.str != "v" {
|
||||
return nil, varTypeError{n.String(), sig}
|
||||
}
|
||||
sig, err := varInfer(n.n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := n.n.Value(sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return MakeVariant(v), nil
|
||||
}
|
||||
|
||||
func varMakeVariantNode(p *varParser, sig Signature) (varNode, error) {
|
||||
n, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t := p.next(); t.typ != tokVariantEnd {
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
vn := variantNode{n}
|
||||
if sig.str != "" && sig.str != "v" {
|
||||
return nil, varTypeError{vn.String(), sig}
|
||||
}
|
||||
return variantNode{n}, nil
|
||||
}
|
||||
|
||||
type dictEntry struct {
|
||||
key, val varNode
|
||||
}
|
||||
|
||||
type dictNode struct {
|
||||
kset, vset sigSet
|
||||
children []dictEntry
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func (n dictNode) Infer() (Signature, error) {
|
||||
for _, v := range n.children {
|
||||
ksig, err := varInfer(v.key)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
vsig, err := varInfer(v.val)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return Signature{"a{" + ksig.str + vsig.str + "}"}, nil
|
||||
}
|
||||
return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
|
||||
}
|
||||
|
||||
func (n dictNode) String() string {
|
||||
s := "{"
|
||||
for i, v := range n.children {
|
||||
s += v.key.String() + ": " + v.val.String()
|
||||
if i != len(n.children)-1 {
|
||||
s += ", "
|
||||
}
|
||||
}
|
||||
return s + "}"
|
||||
}
|
||||
|
||||
func (n dictNode) Sigs() sigSet {
|
||||
r := sigSet{}
|
||||
for k := range n.kset {
|
||||
for v := range n.vset {
|
||||
sig := "a{" + k.str + v.str + "}"
|
||||
r[Signature{sig}] = true
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (n dictNode) Value(sig Signature) (interface{}, error) {
|
||||
set := n.Sigs()
|
||||
if set.Empty() {
|
||||
// no type information -> empty dict
|
||||
return reflect.MakeMap(typeFor(sig.str)).Interface(), nil
|
||||
}
|
||||
if !set[sig] {
|
||||
return nil, varTypeError{n.String(), sig}
|
||||
}
|
||||
m := reflect.MakeMap(typeFor(sig.str))
|
||||
ksig := Signature{sig.str[2:3]}
|
||||
vsig := Signature{sig.str[3 : len(sig.str)-1]}
|
||||
for _, v := range n.children {
|
||||
kv, err := v.key.Value(ksig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vv, err := v.val.Value(vsig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
||||
}
|
||||
return m.Interface(), nil
|
||||
}
|
||||
|
||||
func varMakeDictNode(p *varParser, sig Signature) (varNode, error) {
|
||||
var n dictNode
|
||||
|
||||
if sig.str != "" {
|
||||
if len(sig.str) < 5 {
|
||||
return nil, fmt.Errorf("invalid signature %q for dict type", sig)
|
||||
}
|
||||
ksig := Signature{string(sig.str[2])}
|
||||
vsig := Signature{sig.str[3 : len(sig.str)-1]}
|
||||
n.kset = sigSet{ksig: true}
|
||||
n.vset = sigSet{vsig: true}
|
||||
}
|
||||
if t := p.next(); t.typ == tokDictEnd {
|
||||
return n, nil
|
||||
} else {
|
||||
p.backup()
|
||||
}
|
||||
Loop:
|
||||
for {
|
||||
t := p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
}
|
||||
p.backup()
|
||||
kn, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if kset := kn.Sigs(); !kset.Empty() {
|
||||
if n.kset.Empty() {
|
||||
n.kset = kset
|
||||
} else {
|
||||
n.kset = kset.Intersect(n.kset)
|
||||
if n.kset.Empty() {
|
||||
return nil, fmt.Errorf("can't parse %q with given type information", kn.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
t = p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokColon:
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
t = p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
}
|
||||
p.backup()
|
||||
vn, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vset := vn.Sigs(); !vset.Empty() {
|
||||
if n.vset.Empty() {
|
||||
n.vset = vset
|
||||
} else {
|
||||
n.vset = n.vset.Intersect(vset)
|
||||
if n.vset.Empty() {
|
||||
return nil, fmt.Errorf("can't parse %q with given type information", vn.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
n.children = append(n.children, dictEntry{kn, vn})
|
||||
t = p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokDictEnd:
|
||||
break Loop
|
||||
case tokComma:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type byteStringNode []byte
|
||||
|
||||
var byteStringSet = sigSet{
|
||||
Signature{"ay"}: true,
|
||||
}
|
||||
|
||||
func (byteStringNode) Infer() (Signature, error) {
|
||||
return Signature{"ay"}, nil
|
||||
}
|
||||
|
||||
func (b byteStringNode) String() string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (b byteStringNode) Sigs() sigSet {
|
||||
return byteStringSet
|
||||
}
|
||||
|
||||
func (b byteStringNode) Value(sig Signature) (interface{}, error) {
|
||||
if sig.str != "ay" {
|
||||
return nil, varTypeError{b.String(), sig}
|
||||
}
|
||||
return []byte(b), nil
|
||||
}
|
||||
|
||||
func varParseByteString(s string) ([]byte, error) {
|
||||
// quotes and b at start are guaranteed to be there
|
||||
b := make([]byte, 0, 1)
|
||||
s = s[2 : len(s)-1]
|
||||
for len(s) != 0 {
|
||||
c := s[0]
|
||||
s = s[1:]
|
||||
if c != '\\' {
|
||||
b = append(b, c)
|
||||
continue
|
||||
}
|
||||
c = s[0]
|
||||
s = s[1:]
|
||||
switch c {
|
||||
case 'a':
|
||||
b = append(b, 0x7)
|
||||
case 'b':
|
||||
b = append(b, 0x8)
|
||||
case 'f':
|
||||
b = append(b, 0xc)
|
||||
case 'n':
|
||||
b = append(b, '\n')
|
||||
case 'r':
|
||||
b = append(b, '\r')
|
||||
case 't':
|
||||
b = append(b, '\t')
|
||||
case 'x':
|
||||
if len(s) < 2 {
|
||||
return nil, errors.New("short escape")
|
||||
}
|
||||
n, err := strconv.ParseUint(s[:2], 16, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = append(b, byte(n))
|
||||
s = s[2:]
|
||||
case '0':
|
||||
if len(s) < 3 {
|
||||
return nil, errors.New("short escape")
|
||||
}
|
||||
n, err := strconv.ParseUint(s[:3], 8, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = append(b, byte(n))
|
||||
s = s[3:]
|
||||
default:
|
||||
b = append(b, c)
|
||||
}
|
||||
}
|
||||
return append(b, 0), nil
|
||||
}
|
||||
|
||||
func varInfer(n varNode) (Signature, error) {
|
||||
if sig, ok := n.Sigs().Single(); ok {
|
||||
return sig, nil
|
||||
}
|
||||
return n.Infer()
|
||||
}
|
||||
10
vendor/modules.txt
vendored
10
vendor/modules.txt
vendored
@@ -1,3 +1,13 @@
|
||||
# fyne.io/systray v1.12.0
|
||||
## explicit; go 1.19
|
||||
fyne.io/systray
|
||||
fyne.io/systray/internal/generated/menu
|
||||
fyne.io/systray/internal/generated/notifier
|
||||
# github.com/godbus/dbus/v5 v5.1.0
|
||||
## explicit; go 1.12
|
||||
github.com/godbus/dbus/v5
|
||||
github.com/godbus/dbus/v5/introspect
|
||||
github.com/godbus/dbus/v5/prop
|
||||
# github.com/mattn/go-sqlite3 v1.14.24
|
||||
## explicit; go 1.19
|
||||
github.com/mattn/go-sqlite3
|
||||
|
||||
Reference in New Issue
Block a user