This commit is contained in:
parent
81a5404a55
commit
0264e7ce07
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Serge Zaitsev
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined (_WIN32) || defined (_WIN64)
|
||||||
|
#define TRAY_WINAPI 1
|
||||||
|
#elif defined (__linux__) || defined (linux) || defined (__linux)
|
||||||
|
#define TRAY_APPINDICATOR 1
|
||||||
|
#elif defined (__APPLE__) || defined (__MACH__)
|
||||||
|
#define TRAY_APPKIT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "tray.h"
|
||||||
|
|
||||||
|
#if TRAY_APPINDICATOR
|
||||||
|
#define TRAY_ICON1 "indicator-messages"
|
||||||
|
#define TRAY_ICON2 "indicator-messages-new"
|
||||||
|
#elif TRAY_APPKIT
|
||||||
|
#define TRAY_ICON1 "icon.png"
|
||||||
|
#define TRAY_ICON2 "icon.png"
|
||||||
|
#elif TRAY_WINAPI
|
||||||
|
#define TRAY_ICON1 "icon.ico"
|
||||||
|
#define TRAY_ICON2 "icon.ico"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct tray tray;
|
||||||
|
|
||||||
|
static void toggle_cb(struct tray_menu *item) {
|
||||||
|
printf("toggle cb\n");
|
||||||
|
item->checked = !item->checked;
|
||||||
|
tray_update(&tray);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hello_cb(struct tray_menu *item) {
|
||||||
|
(void)item;
|
||||||
|
printf("hello cb\n");
|
||||||
|
if (strcmp(tray.icon, TRAY_ICON1) == 0) {
|
||||||
|
tray.icon = TRAY_ICON2;
|
||||||
|
} else {
|
||||||
|
tray.icon = TRAY_ICON1;
|
||||||
|
}
|
||||||
|
tray_update(&tray);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void quit_cb(struct tray_menu *item) {
|
||||||
|
(void)item;
|
||||||
|
printf("quit cb\n");
|
||||||
|
tray_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void submenu_cb(struct tray_menu *item) {
|
||||||
|
(void)item;
|
||||||
|
printf("submenu: clicked on %s\n", item->text);
|
||||||
|
tray_update(&tray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test tray init
|
||||||
|
static struct tray tray = {
|
||||||
|
.icon = TRAY_ICON1,
|
||||||
|
.menu =
|
||||||
|
(struct tray_menu[]){
|
||||||
|
{.text = "Hello", .cb = hello_cb},
|
||||||
|
{.text = "Checked", .checked = 1, .cb = toggle_cb},
|
||||||
|
{.text = "Disabled", .disabled = 1},
|
||||||
|
{.text = "-"},
|
||||||
|
{.text = "SubMenu",
|
||||||
|
.submenu =
|
||||||
|
(struct tray_menu[]){
|
||||||
|
{.text = "FIRST", .checked = 1, .cb = submenu_cb},
|
||||||
|
{.text = "SECOND",
|
||||||
|
.submenu =
|
||||||
|
(struct tray_menu[]){
|
||||||
|
{.text = "THIRD",
|
||||||
|
.submenu =
|
||||||
|
(struct tray_menu[]){
|
||||||
|
{.text = "7", .cb = submenu_cb},
|
||||||
|
{.text = "-"},
|
||||||
|
{.text = "8", .cb = submenu_cb},
|
||||||
|
{.text = NULL}}},
|
||||||
|
{.text = "FOUR",
|
||||||
|
.submenu =
|
||||||
|
(struct tray_menu[]){
|
||||||
|
{.text = "5", .cb = submenu_cb},
|
||||||
|
{.text = "6", .cb = submenu_cb},
|
||||||
|
{.text = NULL}}},
|
||||||
|
{.text = NULL}}},
|
||||||
|
{.text = NULL}}},
|
||||||
|
{.text = "-"},
|
||||||
|
{.text = "Quit", .cb = quit_cb},
|
||||||
|
{.text = NULL}},
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (tray_init(&tray) < 0) {
|
||||||
|
printf("failed to create tray\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while (tray_loop(1) == 0) {
|
||||||
|
printf("iteration\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include "api.h"
|
||||||
|
|
||||||
|
void print_stats(enum ps ps) {
|
||||||
|
int procs = SDL_GetCPUCount();
|
||||||
|
int d ;
|
||||||
|
int L1;
|
||||||
|
char L1suf[6];
|
||||||
|
char dsuf[3];
|
||||||
|
SDL_PowerState pwr;
|
||||||
|
int psec;//seconds of battery life left
|
||||||
|
int ppct;//percentage of battery life left
|
||||||
|
char psecsuf[8];
|
||||||
|
const char*pwrdesc;
|
||||||
|
/*--system RAM--*/
|
||||||
|
d = SDL_GetSystemRAM();
|
||||||
|
if(d>1024) {
|
||||||
|
d /= 1024;
|
||||||
|
memcpy(dsuf,"gb",3);
|
||||||
|
}
|
||||||
|
else memcpy(dsuf,"mb",3);
|
||||||
|
/*--L1--*/
|
||||||
|
L1 = SDL_GetCPUCacheLineSize();
|
||||||
|
if(L1>1024*1024) {
|
||||||
|
L1 /= 1024 * 1024;
|
||||||
|
memcpy(L1suf,"mb",3);
|
||||||
|
} else if(L1>1024) {
|
||||||
|
L1 /= 1024;
|
||||||
|
memcpy(L1suf,"kb",3);
|
||||||
|
} else {
|
||||||
|
memcpy(L1suf,"bytes",6);
|
||||||
|
}
|
||||||
|
/*--power state--*/
|
||||||
|
pwr = SDL_GetPowerInfo(&psec, &ppct);
|
||||||
|
if(psec == -1) psec = 60*60;
|
||||||
|
if(psec>=60*60) {
|
||||||
|
psec /= 60*60;
|
||||||
|
memcpy(psecsuf,psec>1?"hours":"hour\0\0",6);
|
||||||
|
} else if(psec>=60) {
|
||||||
|
psec /= 60;
|
||||||
|
memcpy(psecsuf,psec>1?"minutes":"minute\0\0",8);
|
||||||
|
} else {
|
||||||
|
memcpy(psecsuf,psec>1?"seconds":"second\0\0",8);
|
||||||
|
}
|
||||||
|
switch(pwr){
|
||||||
|
case SDL_POWERSTATE_ON_BATTERY: pwrdesc="not plugged in, running on the battery";break;
|
||||||
|
case SDL_POWERSTATE_NO_BATTERY: pwrdesc="plugged in, no battery available";break;
|
||||||
|
case SDL_POWERSTATE_CHARGING: pwrdesc="plugged in, charging battery";break;
|
||||||
|
case SDL_POWERSTATE_CHARGED: pwrdesc="plugged in, battery charged";break;
|
||||||
|
default:
|
||||||
|
case SDL_POWERSTATE_UNKNOWN: pwrdesc="cannot determine power status";
|
||||||
|
}
|
||||||
|
/*--print stats--*/
|
||||||
|
switch(ps){
|
||||||
|
case ps_shutdown:
|
||||||
|
printf("SDL Quit Battery %d%% (%d%s)\n",ppct,psec,psecsuf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case ps_startup:
|
||||||
|
printf("SDL %s %d%sRAM %dProc%s (L1align=%d%s) Battery %d%% (%d%s)\n",SDL_GetPlatform(),d,dsuf,procs,procs>1?"s":"",L1,L1suf,
|
||||||
|
ppct,psec,psecsuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
set GITDIR=D:\dev\db\purrforce\
|
set GITDIR=D:\dev\db\purrforce\
|
||||||
copy /B /Y api.h %GITDIR%
|
copy /B /Y api.h %GITDIR%
|
||||||
copy /B /Y error.c %GITDIR%
|
|
||||||
copy /B /Y ls.bat %GITDIR%
|
copy /B /Y ls.bat %GITDIR%
|
||||||
copy /B /Y main.c %GITDIR%
|
copy /B /Y *.c %GITDIR%
|
||||||
|
copy /B /Y images %GITDIR%
|
||||||
|
copy /B /Y tray %GITDIR%
|
||||||
copy /B /Y make.bat %GITDIR%
|
copy /B /Y make.bat %GITDIR%
|
||||||
copy /B /Y Makefile %GITDIR%
|
copy /B /Y Makefile %GITDIR%
|
||||||
copy /B /Y pull_fromgit.bat %GITDIR%
|
copy /B /Y pull_fromgit.bat %GITDIR%
|
||||||
|
|
|
@ -0,0 +1,370 @@
|
||||||
|
#ifndef TRAY_H
|
||||||
|
#define TRAY_H
|
||||||
|
|
||||||
|
struct tray_menu;
|
||||||
|
|
||||||
|
struct tray {
|
||||||
|
char *icon;
|
||||||
|
struct tray_menu *menu;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tray_menu {
|
||||||
|
char *text;
|
||||||
|
int disabled;
|
||||||
|
int checked;
|
||||||
|
|
||||||
|
void (*cb)(struct tray_menu *);
|
||||||
|
void *context;
|
||||||
|
|
||||||
|
struct tray_menu *submenu;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void tray_update(struct tray *tray);
|
||||||
|
|
||||||
|
#if defined(TRAY_APPINDICATOR)
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <libappindicator/app-indicator.h>
|
||||||
|
|
||||||
|
#define TRAY_APPINDICATOR_ID "tray-id"
|
||||||
|
|
||||||
|
static AppIndicator *indicator = NULL;
|
||||||
|
static int loop_result = 0;
|
||||||
|
|
||||||
|
static void _tray_menu_cb(GtkMenuItem *item, gpointer data) {
|
||||||
|
(void)item;
|
||||||
|
struct tray_menu *m = (struct tray_menu *)data;
|
||||||
|
m->cb(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GtkMenuShell *_tray_menu(struct tray_menu *m) {
|
||||||
|
GtkMenuShell *menu = (GtkMenuShell *)gtk_menu_new();
|
||||||
|
for (; m != NULL && m->text != NULL; m++) {
|
||||||
|
GtkWidget *item;
|
||||||
|
if (strcmp(m->text, "-") == 0) {
|
||||||
|
item = gtk_separator_menu_item_new();
|
||||||
|
} else {
|
||||||
|
if (m->submenu != NULL) {
|
||||||
|
item = gtk_menu_item_new_with_label(m->text);
|
||||||
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
|
||||||
|
GTK_WIDGET(_tray_menu(m->submenu)));
|
||||||
|
} else {
|
||||||
|
item = gtk_check_menu_item_new_with_label(m->text);
|
||||||
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), !!m->checked);
|
||||||
|
}
|
||||||
|
gtk_widget_set_sensitive(item, !m->disabled);
|
||||||
|
if (m->cb != NULL) {
|
||||||
|
g_signal_connect(item, "activate", G_CALLBACK(_tray_menu_cb), m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gtk_widget_show(item);
|
||||||
|
gtk_menu_shell_append(menu, item);
|
||||||
|
}
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tray_init(struct tray *tray) {
|
||||||
|
if (gtk_init_check(0, NULL) == FALSE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
indicator = app_indicator_new(TRAY_APPINDICATOR_ID, tray->icon,
|
||||||
|
APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
|
||||||
|
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE);
|
||||||
|
tray_update(tray);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tray_loop(int blocking) {
|
||||||
|
gtk_main_iteration_do(blocking);
|
||||||
|
return loop_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tray_update(struct tray *tray) {
|
||||||
|
app_indicator_set_icon(indicator, tray->icon);
|
||||||
|
// GTK is all about reference counting, so previous menu should be destroyed
|
||||||
|
// here
|
||||||
|
app_indicator_set_menu(indicator, GTK_MENU(_tray_menu(tray->menu)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tray_exit() { loop_result = -1; }
|
||||||
|
|
||||||
|
#elif defined(TRAY_APPKIT)
|
||||||
|
|
||||||
|
#include <objc/objc-runtime.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
static id app;
|
||||||
|
static id pool;
|
||||||
|
static id statusBar;
|
||||||
|
static id statusItem;
|
||||||
|
static id statusBarButton;
|
||||||
|
|
||||||
|
static id _tray_menu(struct tray_menu *m) {
|
||||||
|
id menu = objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("new"));
|
||||||
|
objc_msgSend(menu, sel_registerName("autorelease"));
|
||||||
|
objc_msgSend(menu, sel_registerName("setAutoenablesItems:"), false);
|
||||||
|
|
||||||
|
for (; m != NULL && m->text != NULL; m++) {
|
||||||
|
if (strcmp(m->text, "-") == 0) {
|
||||||
|
objc_msgSend(menu, sel_registerName("addItem:"),
|
||||||
|
objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem")));
|
||||||
|
} else {
|
||||||
|
id menuItem = objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
|
||||||
|
objc_msgSend(menuItem, sel_registerName("autorelease"));
|
||||||
|
objc_msgSend(menuItem, sel_registerName("initWithTitle:action:keyEquivalent:"),
|
||||||
|
objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), m->text),
|
||||||
|
sel_registerName("menuCallback:"),
|
||||||
|
objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), ""));
|
||||||
|
|
||||||
|
objc_msgSend(menuItem, sel_registerName("setEnabled:"), (m->disabled ? false : true));
|
||||||
|
objc_msgSend(menuItem, sel_registerName("setState:"), (m->checked ? 1 : 0));
|
||||||
|
objc_msgSend(menuItem, sel_registerName("setRepresentedObject:"),
|
||||||
|
objc_msgSend((id)objc_getClass("NSValue"), sel_registerName("valueWithPointer:"), m));
|
||||||
|
|
||||||
|
objc_msgSend(menu, sel_registerName("addItem:"), menuItem);
|
||||||
|
|
||||||
|
if (m->submenu != NULL) {
|
||||||
|
objc_msgSend(menu, sel_registerName("setSubmenu:forItem:"), _tray_menu(m->submenu), menuItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menu_callback(id self, SEL cmd, id sender) {
|
||||||
|
struct tray_menu *m =
|
||||||
|
(struct tray_menu *)objc_msgSend(objc_msgSend(sender, sel_registerName("representedObject")),
|
||||||
|
sel_registerName("pointerValue"));
|
||||||
|
|
||||||
|
if (m != NULL && m->cb != NULL) {
|
||||||
|
m->cb(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tray_init(struct tray *tray) {
|
||||||
|
pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"),
|
||||||
|
sel_registerName("new"));
|
||||||
|
|
||||||
|
objc_msgSend((id)objc_getClass("NSApplication"),
|
||||||
|
sel_registerName("sharedApplication"));
|
||||||
|
|
||||||
|
Class trayDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "Tray", 0);
|
||||||
|
class_addProtocol(trayDelegateClass, objc_getProtocol("NSApplicationDelegate"));
|
||||||
|
class_addMethod(trayDelegateClass, sel_registerName("menuCallback:"), (IMP)menu_callback, "v@:@");
|
||||||
|
objc_registerClassPair(trayDelegateClass);
|
||||||
|
|
||||||
|
id trayDelegate = objc_msgSend((id)trayDelegateClass,
|
||||||
|
sel_registerName("new"));
|
||||||
|
|
||||||
|
app = objc_msgSend((id)objc_getClass("NSApplication"),
|
||||||
|
sel_registerName("sharedApplication"));
|
||||||
|
|
||||||
|
objc_msgSend(app, sel_registerName("setDelegate:"), trayDelegate);
|
||||||
|
|
||||||
|
statusBar = objc_msgSend((id)objc_getClass("NSStatusBar"),
|
||||||
|
sel_registerName("systemStatusBar"));
|
||||||
|
|
||||||
|
statusItem = objc_msgSend(statusBar, sel_registerName("statusItemWithLength:"), -1.0);
|
||||||
|
|
||||||
|
objc_msgSend(statusItem, sel_registerName("retain"));
|
||||||
|
objc_msgSend(statusItem, sel_registerName("setHighlightMode:"), true);
|
||||||
|
statusBarButton = objc_msgSend(statusItem, sel_registerName("button"));
|
||||||
|
tray_update(tray);
|
||||||
|
objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tray_loop(int blocking) {
|
||||||
|
id until = (blocking ?
|
||||||
|
objc_msgSend((id)objc_getClass("NSDate"), sel_registerName("distantFuture")) :
|
||||||
|
objc_msgSend((id)objc_getClass("NSDate"), sel_registerName("distantPast")));
|
||||||
|
|
||||||
|
id event = objc_msgSend(app, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
|
||||||
|
ULONG_MAX,
|
||||||
|
until,
|
||||||
|
objc_msgSend((id)objc_getClass("NSString"),
|
||||||
|
sel_registerName("stringWithUTF8String:"),
|
||||||
|
"kCFRunLoopDefaultMode"),
|
||||||
|
true);
|
||||||
|
if (event) {
|
||||||
|
objc_msgSend(app, sel_registerName("sendEvent:"), event);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tray_update(struct tray *tray) {
|
||||||
|
objc_msgSend(statusBarButton, sel_registerName("setImage:"),
|
||||||
|
objc_msgSend((id)objc_getClass("NSImage"), sel_registerName("imageNamed:"),
|
||||||
|
objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), tray->icon)));
|
||||||
|
|
||||||
|
objc_msgSend(statusItem, sel_registerName("setMenu:"), _tray_menu(tray->menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tray_exit() { objc_msgSend(app, sel_registerName("terminate:"), app); }
|
||||||
|
|
||||||
|
#elif defined(TRAY_WINAPI)
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
#define WM_TRAY_CALLBACK_MESSAGE (WM_USER + 1)
|
||||||
|
#define WC_TRAY_CLASS_NAME "TRAY"
|
||||||
|
#define ID_TRAY_FIRST 1000
|
||||||
|
|
||||||
|
static WNDCLASSEX wc;
|
||||||
|
static NOTIFYICONDATA nid;
|
||||||
|
static HWND hwnd;
|
||||||
|
static HMENU hmenu = NULL;
|
||||||
|
|
||||||
|
static LRESULT CALLBACK _tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||||
|
LPARAM lparam) {
|
||||||
|
switch (msg) {
|
||||||
|
case WM_CLOSE:
|
||||||
|
DestroyWindow(hwnd);
|
||||||
|
return 0;
|
||||||
|
case WM_DESTROY:
|
||||||
|
PostQuitMessage(0);
|
||||||
|
return 0;
|
||||||
|
case WM_TRAY_CALLBACK_MESSAGE:
|
||||||
|
if (lparam == WM_LBUTTONUP || lparam == WM_RBUTTONUP) {
|
||||||
|
POINT p;
|
||||||
|
GetCursorPos(&p);
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
WORD cmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON |
|
||||||
|
TPM_RETURNCMD | TPM_NONOTIFY,
|
||||||
|
p.x, p.y, 0, hwnd, NULL);
|
||||||
|
SendMessage(hwnd, WM_COMMAND, cmd, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_COMMAND:
|
||||||
|
if (wparam >= ID_TRAY_FIRST) {
|
||||||
|
MENUITEMINFO item = {
|
||||||
|
.cbSize = sizeof(MENUITEMINFO), .fMask = MIIM_ID | MIIM_DATA,
|
||||||
|
};
|
||||||
|
if (GetMenuItemInfo(hmenu, wparam, FALSE, &item)) {
|
||||||
|
struct tray_menu *menu = (struct tray_menu *)item.dwItemData;
|
||||||
|
if (menu != NULL && menu->cb != NULL) {
|
||||||
|
menu->cb(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HMENU _tray_menu(struct tray_menu *m, UINT *id) {
|
||||||
|
HMENU hmenu = CreatePopupMenu();
|
||||||
|
for (; m != NULL && m->text != NULL; m++, (*id)++) {
|
||||||
|
if (strcmp(m->text, "-") == 0) {
|
||||||
|
InsertMenu(hmenu, *id, MF_SEPARATOR, TRUE, "");
|
||||||
|
} else {
|
||||||
|
MENUITEMINFO item;
|
||||||
|
memset(&item, 0, sizeof(item));
|
||||||
|
item.cbSize = sizeof(MENUITEMINFO);
|
||||||
|
item.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
|
||||||
|
item.fType = 0;
|
||||||
|
item.fState = 0;
|
||||||
|
if (m->submenu != NULL) {
|
||||||
|
item.fMask = item.fMask | MIIM_SUBMENU;
|
||||||
|
item.hSubMenu = _tray_menu(m->submenu, id);
|
||||||
|
}
|
||||||
|
if (m->disabled) {
|
||||||
|
item.fState |= MFS_DISABLED;
|
||||||
|
}
|
||||||
|
if (m->checked) {
|
||||||
|
item.fState |= MFS_CHECKED;
|
||||||
|
}
|
||||||
|
item.wID = *id;
|
||||||
|
item.dwTypeData = m->text;
|
||||||
|
item.dwItemData = (ULONG_PTR)m;
|
||||||
|
|
||||||
|
InsertMenuItem(hmenu, *id, TRUE, &item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hmenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tray_init(struct tray *tray) {
|
||||||
|
memset(&wc, 0, sizeof(wc));
|
||||||
|
wc.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wc.lpfnWndProc = _tray_wnd_proc;
|
||||||
|
wc.hInstance = GetModuleHandle(NULL);
|
||||||
|
wc.lpszClassName = WC_TRAY_CLASS_NAME;
|
||||||
|
if (!RegisterClassEx(&wc)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwnd = CreateWindowEx(0, WC_TRAY_CLASS_NAME, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
if (hwnd == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
UpdateWindow(hwnd);
|
||||||
|
|
||||||
|
memset(&nid, 0, sizeof(nid));
|
||||||
|
nid.cbSize = sizeof(NOTIFYICONDATA);
|
||||||
|
nid.hWnd = hwnd;
|
||||||
|
nid.uID = 0;
|
||||||
|
nid.uFlags = NIF_ICON | NIF_MESSAGE;
|
||||||
|
nid.uCallbackMessage = WM_TRAY_CALLBACK_MESSAGE;
|
||||||
|
Shell_NotifyIcon(NIM_ADD, &nid);
|
||||||
|
|
||||||
|
tray_update(tray);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tray_loop(int blocking) {
|
||||||
|
MSG msg;
|
||||||
|
if (blocking) {
|
||||||
|
GetMessage(&msg, NULL, 0, 0);
|
||||||
|
} else {
|
||||||
|
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
|
||||||
|
}
|
||||||
|
if (msg.message == WM_QUIT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tray_update(struct tray *tray) {
|
||||||
|
HMENU prevmenu = hmenu;
|
||||||
|
UINT id = ID_TRAY_FIRST;
|
||||||
|
hmenu = _tray_menu(tray->menu, &id);
|
||||||
|
SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0);
|
||||||
|
HICON icon;
|
||||||
|
ExtractIconEx(tray->icon, 0, NULL, &icon, 1);
|
||||||
|
if (nid.hIcon) {
|
||||||
|
DestroyIcon(nid.hIcon);
|
||||||
|
}
|
||||||
|
nid.hIcon = icon;
|
||||||
|
Shell_NotifyIcon(NIM_MODIFY, &nid);
|
||||||
|
|
||||||
|
if (prevmenu != NULL) {
|
||||||
|
DestroyMenu(prevmenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tray_exit() {
|
||||||
|
Shell_NotifyIcon(NIM_DELETE, &nid);
|
||||||
|
if (nid.hIcon != 0) {
|
||||||
|
DestroyIcon(nid.hIcon);
|
||||||
|
}
|
||||||
|
if (hmenu != 0) {
|
||||||
|
DestroyMenu(hmenu);
|
||||||
|
}
|
||||||
|
PostQuitMessage(0);
|
||||||
|
UnregisterClass(WC_TRAY_CLASS_NAME, GetModuleHandle(NULL));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int tray_init(struct tray *tray) { return -1; }
|
||||||
|
static int tray_loop(int blocking) { return -1; }
|
||||||
|
static void tray_update(struct tray *tray) {}
|
||||||
|
static void tray_exit();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* TRAY_H */
|
Loading…
Reference in New Issue