aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xlib/Makefile40
-rw-r--r--lib/libsystray_darwin_386.abin0 -> 29632 bytes
-rw-r--r--lib/libsystray_darwin_amd64.abin0 -> 29632 bytes
-rw-r--r--lib/systray_darwin.c (renamed from systray_darwin.m)5
-rw-r--r--lib/systray_linux.c148
-rw-r--r--lib/systray_windows.c255
-rw-r--r--libsystray_darwin_386.abin0 -> 29632 bytes
-rw-r--r--libsystray_darwin_amd64.abin0 -> 29632 bytes
-rwxr-xr-xsystray.dllbin9728 -> 9728 bytes
-rw-r--r--systray.libbin0 -> 2450 bytes
-rwxr-xr-xsystray/systray.opensdfbin58 -> 0 bytes
-rwxr-xr-xsystray/systray.sdfbin25055232 -> 11423744 bytes
-rwxr-xr-xsystray/systray.suobin9728 -> 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.a
new file mode 100644
index 0000000..e34ebba
--- /dev/null
+++ b/lib/libsystray_darwin_386.a
Binary files differ
diff --git a/lib/libsystray_darwin_amd64.a b/lib/libsystray_darwin_amd64.a
new file mode 100644
index 0000000..79ae71f
--- /dev/null
+++ b/lib/libsystray_darwin_amd64.a
Binary files differ
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.a
new file mode 100644
index 0000000..e34ebba
--- /dev/null
+++ b/libsystray_darwin_386.a
Binary files differ
diff --git a/libsystray_darwin_amd64.a b/libsystray_darwin_amd64.a
new file mode 100644
index 0000000..79ae71f
--- /dev/null
+++ b/libsystray_darwin_amd64.a
Binary files differ
diff --git a/systray.dll b/systray.dll
index 29bf832..5fcbf1f 100755
--- a/systray.dll
+++ b/systray.dll
Binary files differ
diff --git a/systray.lib b/systray.lib
new file mode 100644
index 0000000..d6b210f
--- /dev/null
+++ b/systray.lib
Binary files differ
diff --git a/systray/systray.opensdf b/systray/systray.opensdf
deleted file mode 100755
index 9826695..0000000
--- a/systray/systray.opensdf
+++ /dev/null
Binary files differ
diff --git a/systray/systray.sdf b/systray/systray.sdf
index 75623c3..431663e 100755
--- a/systray/systray.sdf
+++ b/systray/systray.sdf
Binary files differ
diff --git a/systray/systray.suo b/systray/systray.suo
index 4d132fd..f2c9d40 100755
--- a/systray/systray.suo
+++ b/systray/systray.suo
Binary files differ