Tray applet howto
April 08, 2010 —
BarryK
A guy named Rodrigo De Castro posted a neat little skeleton tray applet on his blog back in 2007. Reproduced here:
/*a simple systray applet example by Rodrigo De Castro, 2007*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gstdio.h>
void tray_icon_on_click(GtkStatusIcon *status_icon,
gpointer user_data)
{
printf("Clicked on tray icon\n");
}
void tray_icon_on_menu(GtkStatusIcon *status_icon, guint button,
guint activate_time, gpointer user_data)
{
printf("Popup menu\n");
}
static GtkStatusIcon *create_tray_icon() {
GtkStatusIcon *tray_icon;
tray_icon = gtk_status_icon_new();
g_signal_connect(G_OBJECT(tray_icon), "activate",
G_CALLBACK(tray_icon_on_click), NULL);
g_signal_connect(G_OBJECT(tray_icon),
"popup-menu",
G_CALLBACK(tray_icon_on_menu), NULL);
gtk_status_icon_set_from_icon_name(tray_icon,
GTK_STOCK_MEDIA_STOP);
gtk_status_icon_set_tooltip(tray_icon,
"Example Tray Icon");
gtk_status_icon_set_visible(tray_icon, TRUE);
return tray_icon;
}
int main(int argc, char **argv) {
GtkStatusIcon *tray_icon;
gtk_init(&argc, &argv);
tray_icon = create_tray_icon();
gtk_main();
return 0;
}
To compile, assuming filename is ssa.c:
gcc `pkg-config --cflags --libs gtk+-2.0` ssa.c -o ssa
Our older tray applets are more complicated as they use the "eggtray" library, but from GTK 2.10 the simpler "statusicon" library is available.
To add to this skeleton, you need online references:
http://library.gnome.org/devel/gtk/2.11/GtkStatusIcon.html
http://www.elook.org/programming/c/
To be able to monitor something periodically, I used the gtk_timeout_add() function, and in a few hours I knocked up a new "freememapplet":
/*BK based on a simple systray applet example by Rodrigo De Castro, 2007*/
/*sys tray applet to monitor free personal storage space
passed parameter is the PUPMODE*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gstdio.h>
GtkStatusIcon *tray_icon;
unsigned int interval = 10000; /*update interval in milliseconds*/
FILE *fp;
char meminfo[16];
char *memfree;
char *memtotal;
int mylength;
char memdisplayfree[8];
char memdisplaytotal[8];
int sizefree;
int sizefreeprev = 0;
int sizetotal;
char memdisplaylong[64];
unsigned int pupmode;
unsigned int ptoffset;
char *ptsecond;
int percentfree;
gboolean Update(gpointer ptr);
gboolean Update(gpointer ptr) {
/* /sbin/pup_event_frontend_d writes to this file, but deprecated...
if((fp = fopen("/tmp/pup_event_sizefreem","r")) == NULL) { return; }
fgets(memfree,sizeof memfree,fp);
fclose(fp);*/
//read free personal storage...
//would prefer to do this entirely in C code...
if (pupmode!=6 && pupmode!=12) {fp = (FILE *)popen("df -m | grep ' /$' | tr -s ' ' | cut -f 2,4 -d ' '","r");}
else {fp = (FILE *)popen("df -m | grep ' /initrd/pup_rw$' | tr -s ' ' | cut -f 2,4 -d ' '","r");}
fgets(meminfo,sizeof meminfo,fp);
pclose(fp);
//separate out max and available space...
ptsecond = strchr(meminfo,' ');
ptoffset=ptsecond - meminfo;
meminfo[ptoffset] = 0;
memtotal=&meminfo[0];
memfree=&meminfo[ptoffset+1];
//need them in both string and integer...
mylength=strlen(memfree);
mylength=mylength-1;
memfree[mylength]=0; //seems to have newline char on end.
strcpy(memdisplayfree,memfree);
mylength=strlen(memfree);
sizefree=atoi(memfree);
strcpy(memdisplaytotal,memtotal);
mylength=strlen(memtotal);
sizetotal=atoi(memtotal);
if (sizefreeprev == sizefree) return; //unchanged.
sizefreeprev=sizefree;
//format display...
if ( sizefree < 1000 ) strcat(memdisplayfree,"M");
else if (sizefree >= 10240) sprintf(memdisplayfree, "%dG", (sizefree/1024)); //>=10G
else sprintf(memdisplayfree, "%.1fG", (float)(sizefree/1024.0));
if ( sizetotal < 1000 ) strcat(memdisplaytotal,"M");
else if (sizetotal >= 10240) sprintf(memdisplaytotal, "%dG", (sizetotal/1024)); //>=10G
else sprintf(memdisplaytotal, "%.1fG", (float)(sizetotal/1024.0));
//display tooltip...
memdisplaylong[0]=0;
strcat (memdisplaylong,memdisplaytotal);
strcat (memdisplaylong," personal storage, free space = ");
strcat (memdisplaylong,memdisplayfree);
gtk_status_icon_set_tooltip(tray_icon, memdisplaylong);
//update icon... (sizefree,sizetotal are in MB)
if (gtk_status_icon_get_blinking(tray_icon)==TRUE) gtk_status_icon_set_blinking(tray_icon,FALSE);
percentfree=(sizefree*100)/sizetotal;
if (sizefree < 20) {
gtk_status_icon_set_from_file(tray_icon, "/usr/local/lib/X11/mini-icons/level-critical.png");
gtk_status_icon_set_blinking(tray_icon,TRUE);
}
else if (percentfree < 20) {gtk_status_icon_set_from_file(tray_icon, "/usr/local/lib/X11/mini-icons/level-critical.png");}
else if (percentfree < 45) {gtk_status_icon_set_from_file(tray_icon, "/usr/local/lib/X11/mini-icons/level-ok.png");}
else if (percentfree < 70) {gtk_status_icon_set_from_file(tray_icon, "/usr/local/lib/X11/mini-icons/level-good.png");}
else {gtk_status_icon_set_from_file(tray_icon, "/usr/local/lib/X11/mini-icons/level-excellent.png");}
}
void tray_icon_on_click(GtkStatusIcon *status_icon, gpointer user_data)
{
printf("Clicked on tray icon\n");
}
void tray_icon_on_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
{
printf("Popup menu\n");
}
static GtkStatusIcon *create_tray_icon() {
tray_icon = gtk_status_icon_new();
g_signal_connect(G_OBJECT(tray_icon), "activate", G_CALLBACK(tray_icon_on_click), NULL);
g_signal_connect(G_OBJECT(tray_icon), "popup-menu", G_CALLBACK(tray_icon_on_menu), NULL);
// gtk_status_icon_set_from_icon_name(tray_icon, GTK_STOCK_MEDIA_STOP);
gtk_status_icon_set_from_file(tray_icon, "/usr/local/lib/X11/mini-icons/level-grey.png");
// gtk_status_icon_set_tooltip(tray_icon, "Example Tray Icon");
gtk_status_icon_set_visible(tray_icon, TRUE);
return tray_icon;
}
int main(int argc, char **argv) {
pupmode=atoi(argv[1]);
GtkStatusIcon *tray_icon;
gtk_init(&argc, &argv);
tray_icon = create_tray_icon();
gtk_timeout_add(interval, Update, NULL);
Update(NULL);
gtk_main();
return 0;
}
...I found it easy, even with my mediocre C coding skill.
Comments
freememapplet source pkgUsername: BarryK
I have uploaded my freememapplet_tray-2.tar.gz package to my source repository: http://bkhome.org/sources/alphabetical/f/
retrovol
Username: Pizzasgood
"The downside to GTK statusicon is that, until around GTK 2.15 or so, it did not support scrolling the scrollwheel on the icon. The eggtrayicon method does. FYI, the bug you were having with Retrvol is fixed in SVN now. I had changed the size of a character array to fix a different bug, and forgot to update a second array to match, so the null terminator was overflowing and modifying the flag that told it to remove the pid file on exit. I haven't sorted out why it's being left as a zombie process in the first place yet, but my testing yesterday makes it seem like it might be a JWM issue. I'll need to play with it some more to sort that out. But it doesn't hurt anything as long as it exits correctly, making the 'Exit' option safe now. (I still need to add more robustness for when it doesn't exit correctly.) I haven't had time to do anything regarding the placement of the exit menu item yet, nor what happens when the taskbar is on the top of the screen. I'll start working on those this afternoon.
Freememapplet
Username: BarryK
"Jemimah, I added a printf() if argument is forgotten. ...I suppose should really open /etc/rc.d/PUPSTATE and read the PUPMODE variable from it, then wouldn't need to pass a parameter -- I didn't do that due to my general unfamiliarity with doing stuff in C. I have changed the icons to xpm images built into the executable. So now, only the 7.7KB executable is required. Now, if you click on the icon, 'partview' runs. Haven't got around to a menu yet. 'freememapplet_tray-2.1.tar.gz' is here: http://bkhome.org/sources/alphabetical/f/ P.S. if you think my icon looks stupid, you're welcome to redesign it! My artwork is usually primitive. Here is another useful online reference, for embedded xpm images: http://library.gnome.org/devel/gdk-pixbuf/stable/gdk-pixbuf-creating.html
Freememapplet
Username: BarryK
"Well, it is easy enough to run a one-line shell to grab PUPMODE, I'll do that soon, gotta eat first.
Freememapplet
Username: BarryK
"Ok, a passed parameter is no longer needed. freememapplet_tray opens /etc/rc.d/PUPSTATE and reads the PUPMODE variable. I left the version number at 2.1 and re-uploaded the source tarball.
freememapplet
Username: Jemimah
"The new version works well. I actually like the icon, though it's not immediately apparent what it represents - it's clear enough once you see the tooltip. I like it a lot better than the old applet. I'm going put this and the blinky applet into Puppeee's next version so I can have the same applets in both FLWM and JWM (though I may switch to Openbox or IceWM as JWM has some bugs that become really obvious when you run it on a small screen).
Openbox
Username: BarryK
"Jemimah, Openbox is working nicely. My PET packages of Openbox and Obconf are here: http://distro.ibiblio.org/pub/linux/distributions/quirky/pet_packages-quirky/
Tray applet
Username: BarryK
"Another useful reference: http://library.gnome.org/devel/gdk-pixbuf/unstable/gdk-pixbuf-file-loading.html Username: 28 May 2010, 8:17
"01489"114.129.167.148'Tray applet"ttuuxxx"Hi Barry I kind of like your older one better, Well the numbers showing instead of the icons, is there anyway we could get the newer one with numbers? The older freememory applet doesn't work on xfce-panel but your newer one does swallow just fine. Thanks for your time ttuuxxx
Re old freemem applet
Username: BarryK
"ttuuxxx, There were a few variations on the old one. One version is not a tray applet just a window that JWM is able to swallow but not other window managers. Then I think that there were some "egg tray" versions, one of them created by MU I think, that other trays can swallow. I have tried to keep sources of all the variants here: http://bkhome.org/sources/alphabetical/f/ The "xlib" ones are just windows. The others are egg-tray applets I think. However, the latest freemem applet uses more modern sys-tray GTK code. It is basically designed for drawing icons in the systray, but I don't know if it can be made to draw text.
Tags: puppy