diff options
| -rwxr-xr-x | lib/Makefile | 40 | ||||
| -rw-r--r-- | lib/libsystray_darwin_386.a | bin | 0 -> 29632 bytes | |||
| -rw-r--r-- | lib/libsystray_darwin_amd64.a | bin | 0 -> 29632 bytes | |||
| -rw-r--r-- | lib/systray_darwin.c (renamed from systray_darwin.m) | 5 | ||||
| -rw-r--r-- | lib/systray_linux.c | 148 | ||||
| -rw-r--r-- | lib/systray_windows.c | 255 | ||||
| -rw-r--r-- | libsystray_darwin_386.a | bin | 0 -> 29632 bytes | |||
| -rw-r--r-- | libsystray_darwin_amd64.a | bin | 0 -> 29632 bytes | |||
| -rwxr-xr-x | systray.dll | bin | 9728 -> 9728 bytes | |||
| -rw-r--r-- | systray.lib | bin | 0 -> 2450 bytes | |||
| -rwxr-xr-x | systray/systray.opensdf | bin | 58 -> 0 bytes | |||
| -rwxr-xr-x | systray/systray.sdf | bin | 25055232 -> 11423744 bytes | |||
| -rwxr-xr-x | systray/systray.suo | bin | 9728 -> 9728 bytes | 
13 files changed, 446 insertions, 2 deletions
| diff --git a/lib/Makefile b/lib/Makefile new file mode 100755 index 0000000..f7ea313 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,40 @@ +CC = gcc + +ifndef os +   os = linux +endif + +ifndef arch +   arch = 386 +endif + +CCFLAGS = -fPIC -Wall -Wextra -D$(os) -finline-functions -O3 -fno-strict-aliasing -fvisibility=hidden + +ifeq ($(os), darwin) +   CCFLAGS += -DDARWIN -x objective-c -fobjc-arc +endif + +ifeq ($(arch), 386) +   CCFLAGS += -DIA32 +endif + +ifeq ($(arch), amd64) +   CCFLAGS += -DAMD64 +endif + +OBJS = systray_$(os)_$(arch).o +DIR = $(shell pwd) + +all: ../libsystray_$(os)_$(arch).a + +%_$(os)_$(arch).o: %_$(os).c +	$(CC) $(CCFLAGS) -o $@ $< -c + +libsystray_$(os)_$(arch).a: $(OBJS) +	ar -rcs $@ $^ + +../libsystray_$(os)_$(arch).a: libsystray_$(os)_$(arch).a +	cp libsystray_$(os)_$(arch).a ../ + +clean: +	rm -f *.o *.a ../libsystray*.a diff --git a/lib/libsystray_darwin_386.a b/lib/libsystray_darwin_386.aBinary files differ new file mode 100644 index 0000000..e34ebba --- /dev/null +++ b/lib/libsystray_darwin_386.a diff --git a/lib/libsystray_darwin_amd64.a b/lib/libsystray_darwin_amd64.aBinary files differ new file mode 100644 index 0000000..79ae71f --- /dev/null +++ b/lib/libsystray_darwin_amd64.a diff --git a/systray_darwin.m b/lib/systray_darwin.c index 010143f..fffc49e 100644 --- a/systray_darwin.m +++ b/lib/systray_darwin.c @@ -1,5 +1,5 @@  #import <Cocoa/Cocoa.h> -#include "systray.h" +#include "../systray.h"  @interface MenuItem : NSObject  { @@ -48,7 +48,7 @@  }  @synthesize window = _window; - +#pragma GCC diagnostic ignored "-Wunused-parameter"  - (void)applicationDidFinishLaunching:(NSNotification *)aNotification  {    self->statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; @@ -63,6 +63,7 @@  {    [[NSStatusBar systemStatusBar] removeStatusItem: statusItem];  } +#pragma GCC diagnostic warning "-Wunused-parameter"  - (void)setIcon:(NSImage *)image {    [statusItem setImage:image]; diff --git a/lib/systray_linux.c b/lib/systray_linux.c new file mode 100644 index 0000000..9358890 --- /dev/null +++ b/lib/systray_linux.c @@ -0,0 +1,148 @@ +#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 GArray *global_temp_icon_file_names = NULL; + +typedef struct { +	GtkWidget *menu_item; +	char *menu_id; +} MenuItemNode; + +typedef struct { +	char* menu_id; +	char* title; +	char* tooltip; +	short disabled; +	short checked; +} MenuItemInfo; + +int nativeLoop(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)); +	global_temp_icon_file_names = g_array_new(TRUE, FALSE, sizeof(char*)); +	systray_ready(); +	gtk_main(); +	return; +} + +// runs in main thread, should always return FALSE to prevent gtk to execute it again +gboolean do_set_icon(gpointer data) { +	GBytes* bytes = (GBytes*)data; +	char* temp_file_name = malloc(PATH_MAX); +	strcpy(temp_file_name, "/tmp/systray_XXXXXX"); +	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; +	} +	g_array_append_val(global_temp_icon_file_names, temp_file_name); +	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; +} + +// 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(strcmp(item->menu_id, mii->menu_id) == 0){ +			gtk_menu_item_set_label(GTK_MENU_ITEM(item->menu_item), mii->title); +			free(mii->menu_id); +			break; +		} +	} + +	// menu id doesn't exist, add new item +	if(it == NULL) { +		GtkWidget *menu_item = gtk_menu_item_new_with_label(mii->title); +		g_signal_connect_swapped(G_OBJECT(menu_item), "activate", G_CALLBACK(systray_menu_item_selected), mii->menu_id); +		gtk_menu_shell_append(GTK_MENU_SHELL(global_tray_menu), menu_item); + +		MenuItemNode* new_item = malloc(sizeof(MenuItemNode)); +		new_item->menu_id = mii->menu_id; +		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 ? FALSE : TRUE); +	gtk_widget_show_all(global_tray_menu); + +	free(mii->title); +	free(mii->tooltip); +	free(mii); +	return FALSE; +} + +// runs in main thread, should always return FALSE to prevent gtk to execute it again +gboolean do_quit(gpointer data) { +	int i; +	for (i = 0; i < INT_MAX; ++i) { +		char * temp_file_name = g_array_index(global_temp_icon_file_names, char*, i); +		if (temp_file_name == NULL) { +			break; +		} +		int ret = unlink(temp_file_name); +		if (ret == -1) { +			printf("failed to remove temp icon file %s: %s\n", temp_file_name, strerror(errno)); +		} +	} +	gtk_main_quit(); +	return FALSE; +} + +void setIcon(const char* iconBytes, int length) { +	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 add_or_update_menu_item(char* menu_id, char* title, char* tooltip, short disabled, short checked) { +	MenuItemInfo *mii = malloc(sizeof(MenuItemInfo)); +	mii->menu_id = menu_id; +	mii->title = title; +	mii->tooltip = tooltip; +	mii->disabled = disabled; +	mii->checked = checked; +	g_idle_add(do_add_or_update_menu_item, mii); +} + +void quit() { +	g_idle_add(do_quit, NULL); +} diff --git a/lib/systray_windows.c b/lib/systray_windows.c new file mode 100644 index 0000000..a60448a --- /dev/null +++ b/lib/systray_windows.c @@ -0,0 +1,255 @@ +#include <stdio.h> +#include <stdlib.h> +#include <windows.h> +#include <shellapi.h> +#include "../systray.h" + +// Message posted into message loop when Notification Icon is clicked +#define WM_SYSTRAY_MESSAGE (WM_USER + 1) + +static NOTIFYICONDATA nid; +static HWND hWnd; +static HMENU hTrayMenu; + +void reportWindowsError(const char* action) { +	LPTSTR pErrMsg = NULL; +	DWORD errCode = GetLastError(); +	DWORD result = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER| +			FORMAT_MESSAGE_FROM_SYSTEM| +			FORMAT_MESSAGE_ARGUMENT_ARRAY, +			NULL, +			errCode, +			LANG_NEUTRAL, +			pErrMsg, +			0, +			NULL); +	printf("Systray error %s: %d %s\n", action, errCode, pErrMsg); +} + +wchar_t* UTF8ToUnicode(const char* str) { +	wchar_t* result; +	int textLen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL ,0); +	result = (wchar_t *)calloc((textLen+1), sizeof(wchar_t)); +	int converted = MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textLen); +	// Ensure result is alway zero terminated in case either syscall failed +	if (converted == 0) { +		reportWindowsError("convert UTF8 to UNICODE"); +		result[0] = L'\0'; +	} +	return result; +} + +void ShowMenu(HWND hWnd) { +	POINT p; +	if (0 == GetCursorPos(&p)) { +		reportWindowsError("get tray menu position"); +		return; +	}; +	SetForegroundWindow(hWnd); // Win32 bug work-around +	TrackPopupMenu(hTrayMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, p.x, p.y, 0, hWnd, NULL); + +} + +char* GetMenuItemId(int index) { +	MENUITEMINFO menuItemInfo; +	menuItemInfo.cbSize = sizeof(MENUITEMINFO); +	menuItemInfo.fMask = MIIM_DATA; +	if (0 == GetMenuItemInfo(hTrayMenu, index, TRUE, &menuItemInfo)) { +		reportWindowsError("get menu item id"); +		return NULL; +	} +	return (char*)menuItemInfo.dwItemData; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { +	switch (message) { +		case WM_MENUCOMMAND: +			systray_menu_item_selected(GetMenuItemId(wParam)); +			break; +		case WM_DESTROY: +			Shell_NotifyIcon(NIM_DELETE, &nid); +			PostQuitMessage(0); +			break; +		case WM_SYSTRAY_MESSAGE: +			switch(lParam) { +				case WM_RBUTTONUP: +					ShowMenu(hWnd); +					break; +				case WM_LBUTTONUP: +					ShowMenu(hWnd); +					break; +				default: +					return DefWindowProc(hWnd, message, wParam, lParam); +			}; +			break; +		default: +			return DefWindowProc(hWnd, message, wParam, lParam); +	} +	return 0; +} + +void MyRegisterClass(HINSTANCE hInstance, TCHAR* szWindowClass) { +	WNDCLASSEX wcex; + +	wcex.cbSize = sizeof(WNDCLASSEX); +	wcex.style          = CS_HREDRAW | CS_VREDRAW; +	wcex.lpfnWndProc    = WndProc; +	wcex.cbClsExtra     = 0; +	wcex.cbWndExtra     = 0; +	wcex.hInstance      = hInstance; +	wcex.hIcon          = LoadIcon(NULL, IDI_APPLICATION); +	wcex.hCursor        = LoadCursor(NULL, IDC_ARROW); +	wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); +	wcex.lpszMenuName   = 0; +	wcex.lpszClassName  = szWindowClass; +	wcex.hIconSm        = LoadIcon(NULL, IDI_APPLICATION); + +	RegisterClassEx(&wcex); +} + +HWND InitInstance(HINSTANCE hInstance, int nCmdShow, TCHAR* szWindowClass) { +	HWND hWnd = CreateWindow(szWindowClass, TEXT(""), WS_OVERLAPPEDWINDOW, +			CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); +	if (!hWnd) { +		return 0; +	} + +	ShowWindow(hWnd, nCmdShow); +	UpdateWindow(hWnd); + +	return hWnd; +} + + +BOOL createMenu() { +	hTrayMenu = CreatePopupMenu(); +	MENUINFO menuInfo; +	menuInfo.cbSize = sizeof(MENUINFO); +	menuInfo.fMask = MIM_APPLYTOSUBMENUS | MIM_STYLE; +	menuInfo.dwStyle = MNS_NOTIFYBYPOS; +	return SetMenuInfo(hTrayMenu, &menuInfo); +} + +BOOL addNotifyIcon() { +	nid.cbSize = sizeof(NOTIFYICONDATA); +	nid.hWnd = hWnd; +	nid.uID = 100; +	nid.uCallbackMessage = WM_SYSTRAY_MESSAGE; +	nid.uFlags = NIF_MESSAGE; +	return Shell_NotifyIcon(NIM_ADD, &nid); +} + +int nativeLoop(void) { +	HINSTANCE hInstance = GetModuleHandle(NULL); +	TCHAR* szWindowClass = TEXT("SystrayClass"); +	MyRegisterClass(hInstance, szWindowClass); +	hWnd = InitInstance(hInstance, FALSE, szWindowClass); // Don't show window +	if (!hWnd) { +		return EXIT_FAILURE; +	} +	if (!createMenu() || !addNotifyIcon()) { +		return EXIT_FAILURE; +	} +	systray_ready(); + +	MSG msg; +	while (GetMessage(&msg, NULL, 0, 0)) { +		TranslateMessage(&msg); +		DispatchMessage(&msg); +	}    +	return EXIT_SUCCESS; +} + + +void setIcon(const char* iconBytes, int length) { +	HICON hIcon; +	{ +		// This is really hacky, but LoadImage won't let me load an image from memory. +		// So we have to write out a temporary file, load it from there, then delete the file. + +		// From http://msdn.microsoft.com/en-us/library/windows/desktop/aa363875.aspx +		TCHAR szTempFileName[MAX_PATH+1]; +		TCHAR lpTempPathBuffer[MAX_PATH+1]; +		int dwRetVal = GetTempPath(MAX_PATH+1, lpTempPathBuffer); +		if (dwRetVal > MAX_PATH+1 || (dwRetVal == 0)) { +			reportWindowsError("get temp icon path"); +			return; +		} + +		int uRetVal = GetTempFileName(lpTempPathBuffer, TEXT("systray_"), 0, szTempFileName); +		if (uRetVal == 0) { +			reportWindowsError("get temp icon file name"); +			return; +		} + +		FILE* fIcon = _wfopen(szTempFileName, TEXT("wb")); +		if (length != fwrite(iconBytes, 1, length, fIcon)) { +			printf("error write temp icon file\n"); +		}; +		fclose(fIcon); + +		hIcon = LoadImage(NULL, szTempFileName, IMAGE_ICON, 64, 64, LR_LOADFROMFILE); + +		_wremove(szTempFileName); +	} + +	nid.hIcon = hIcon; +	nid.uFlags = NIF_ICON; +	Shell_NotifyIcon(NIM_MODIFY, &nid); +} + +// Don't support for Windows +void setTitle(char* ctitle) { +	free(ctitle); +} + +void setTooltip(char* ctooltip) { +	wchar_t* tooltip = UTF8ToUnicode(ctooltip); +	wcsncpy(nid.szTip, tooltip, 64); +	nid.uFlags = NIF_TIP; +	Shell_NotifyIcon(NIM_MODIFY, &nid); +	free(tooltip); +	free(ctooltip); +} + +void add_or_update_menu_item(char* menuId, char* ctitle, char* ctooltip, short disabled, short checked) { +	wchar_t* title = UTF8ToUnicode(ctitle); +	MENUITEMINFO menuItemInfo; +	menuItemInfo.cbSize = sizeof(MENUITEMINFO); +	menuItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_STATE; +	menuItemInfo.fType = MFT_STRING; +	menuItemInfo.dwTypeData = title; +	menuItemInfo.cch = wcslen(title) + 1; +	menuItemInfo.dwItemData = (ULONG_PTR)menuId; +	menuItemInfo.fState = 0; +	if (disabled == 1) { +		menuItemInfo.fState |= MFS_DISABLED; +	} +	if (checked == 1) { +		menuItemInfo.fState |= MFS_CHECKED; +	} + +	int itemCount = GetMenuItemCount(hTrayMenu); +	int i; +	for (i = 0; i < itemCount; i++) { +		char * idString = GetMenuItemId(i); +		if (NULL == idString) { +			continue; +		} +		if (strcmp(menuId, idString) == 0) { +			free(idString); +			SetMenuItemInfo(hTrayMenu, i, TRUE, &menuItemInfo); +			break; +		} +	} +	if (i == itemCount) { +		InsertMenuItem(hTrayMenu, -1, TRUE, &menuItemInfo); +	} +	free(title); +	free(ctitle); +	free(ctooltip); +} + +void quit() { +	PostMessage(hWnd, WM_DESTROY, 0, 0); +} diff --git a/libsystray_darwin_386.a b/libsystray_darwin_386.aBinary files differ new file mode 100644 index 0000000..e34ebba --- /dev/null +++ b/libsystray_darwin_386.a diff --git a/libsystray_darwin_amd64.a b/libsystray_darwin_amd64.aBinary files differ new file mode 100644 index 0000000..79ae71f --- /dev/null +++ b/libsystray_darwin_amd64.a diff --git a/systray.dll b/systray.dllBinary files differ index 29bf832..5fcbf1f 100755 --- a/systray.dll +++ b/systray.dll diff --git a/systray.lib b/systray.libBinary files differ new file mode 100644 index 0000000..d6b210f --- /dev/null +++ b/systray.lib diff --git a/systray/systray.opensdf b/systray/systray.opensdfBinary files differ deleted file mode 100755 index 9826695..0000000 --- a/systray/systray.opensdf +++ /dev/null diff --git a/systray/systray.sdf b/systray/systray.sdfBinary files differ index 75623c3..431663e 100755 --- a/systray/systray.sdf +++ b/systray/systray.sdf diff --git a/systray/systray.suo b/systray/systray.suoBinary files differ index 4d132fd..f2c9d40 100755 --- a/systray/systray.suo +++ b/systray/systray.suo | 
