/*systray.h*/
/* most of this ripped from gaim */
#include <gtk/gtkplug.h>
#include <gdk/gdkx.h>
G_BEGIN_DECLS
#define TYPE_TRAY_ICON (tray_icon_get_type ())
#define TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TRAY_ICON, TrayIcon))
#define TRAY_ICON_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), TYPE_TRAY_ICON, TrayIconClass))
#define IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TRAY_ICON))
#define TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_TRAY_ICON, TrayIconClass))
typedef struct {
GtkPlug parent_instance;
guint stamp;
Atom selection_atom;
Atom manager_atom;
Atom system_tray_opcode_atom;
Window manager_window;
} TrayIcon;
typedef struct {
GtkPlugClass parent_class;
} TrayIconClass;
GType tray_icon_get_type (void);
TrayIcon *tray_icon_new (const gchar *name);
guint tray_icon_send_message (TrayIcon *icon, gint timeout, const gchar *msg, gint len);
void tray_icon_cancel_messsage (TrayIcon *icon, guint id);
gboolean docklet_create (void);
G_END_DECLS
#######################################
/*systray.c*/
/* most of this ripped from gaim */
#include <string.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "systray.h"
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
static GtkPlugClass *parent_class = NULL;
static void tray_icon_init (TrayIcon *icon);
static void tray_icon_class_init (TrayIconClass *class);
static void tray_icon_unrealize (GtkWidget *widget);
static void tray_icon_update_manager_window (TrayIcon *icon);
enum systray_status {
offline,
online
};
gboolean systray_create ();
static gboolean systray_update_status ();
static TrayIcon *systray = NULL;
static GtkWidget *image = NULL;
static enum systray_status status;
static enum systray_status icon, is_online=online, is_offline=0;
GType tray_icon_get_type (void)
{
static GType our_type = 0;
our_type = g_type_from_name ("TrayIcon");
if (our_type == 0) {
static const GTypeInfo our_info = {
sizeof (TrayIconClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL,
(GClassInitFunc) tray_icon_class_init, NULL, NULL, sizeof (TrayIcon), 0,
(GInstanceInitFunc) tray_icon_init};
our_type = g_type_register_static (GTK_TYPE_PLUG, "TrayIcon", &our_info, 0);
} else if (parent_class == NULL) {
tray_icon_class_init ((TrayIconClass *)g_type_class_peek (our_type));
}
return our_type;
}
static void tray_icon_init (TrayIcon *icon)
{
icon->stamp = 1;
gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
}
static void tray_icon_class_init (TrayIconClass *class)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
parent_class = g_type_class_peek_parent (class);
widget_class->unrealize = tray_icon_unrealize;
}
static GdkFilterReturn tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
{
TrayIcon *icon = data;
XEvent *xev = (XEvent *)xevent;
if (xev->xany.type == ClientMessage && xev->xclient.message_type == icon->manager_atom &&
xev->xclient.data.l[1] == icon->selection_atom) {
tray_icon_update_manager_window (icon);
} else if (xev->xany.window == icon->manager_window) {
if (xev->xany.type == DestroyNotify) {
tray_icon_update_manager_window (icon);
}
}
return GDK_FILTER_CONTINUE;
}
static void tray_icon_unrealize (GtkWidget *widget)
{
TrayIcon *icon = TRAY_ICON (widget);
GdkWindow *root_window;
if (icon->manager_window != None) {
GdkWindow *gdkwin;
gdkwin = gdk_window_lookup (icon->manager_window);
gdk_window_remove_filter (gdkwin, tray_icon_manager_filter, icon);
}
root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
gdk_window_remove_filter (root_window, tray_icon_manager_filter, icon);
if (GTK_WIDGET_CLASS (parent_class)->unrealize) {
(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}
}
static void tray_icon_send_manager_message (TrayIcon *icon, long message, Window window,long data1, long data2, long data3)
{
XClientMessageEvent ev;
Display *display;
ev.type = ClientMessage;
ev.window = window;
ev.message_type = icon->system_tray_opcode_atom;
ev.format = 32;
ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
ev.data.l[1] = message;
ev.data.l[2] = data1;
ev.data.l[3] = data2;
ev.data.l[4] = data3;
display = gdk_display;
gdk_error_trap_push ();
XSendEvent (display, icon->manager_window, False, NoEventMask, (XEvent *)&ev);
gdk_error_trap_pop ();
}
static void tray_icon_send_dock_request (TrayIcon *icon)
{
tray_icon_send_manager_message (icon, SYSTEM_TRAY_REQUEST_DOCK,icon->manager_window, gtk_plug_get_id (GTK_PLUG (icon)), 0, 0);
}
static void tray_icon_update_manager_window (TrayIcon *icon)
{
Display *xdisplay;
xdisplay = gdk_display;
if (icon->manager_window != None) {
GdkWindow *gdkwin;
gdkwin = gdk_window_lookup (icon->manager_window);
gdk_window_remove_filter (gdkwin, tray_icon_manager_filter, icon);
}
XGrabServer (xdisplay);
icon->manager_window = XGetSelectionOwner (xdisplay, icon->selection_atom);
if (icon->manager_window != None) {
XSelectInput (xdisplay, icon->manager_window, StructureNotifyMask);
}
XUngrabServer (xdisplay);
XFlush (xdisplay);
if (icon->manager_window != None) {
GdkWindow *gdkwin;
gdkwin = gdk_window_lookup (icon->manager_window);
gdk_window_add_filter (gdkwin, tray_icon_manager_filter, icon);
tray_icon_send_dock_request (icon);
}
}
TrayIcon *tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
{
TrayIcon *icon;
char buffer[256];
GdkWindow *root_window;
g_return_val_if_fail (xscreen != NULL, NULL);
icon = g_object_new (TYPE_TRAY_ICON, NULL);
gtk_window_set_title (GTK_WINDOW (icon), name);
gtk_plug_construct (GTK_PLUG (icon), 0);
gtk_widget_realize (GTK_WIDGET (icon));
g_snprintf (buffer, sizeof (buffer), "_NET_SYSTEM_TRAY_S%d", XScreenNumberOfScreen (xscreen));
icon->selection_atom = XInternAtom (DisplayOfScreen (xscreen), buffer, False);
icon->system_tray_opcode_atom = XInternAtom (DisplayOfScreen (xscreen), "_NET_SYSTEM_TRAY_OPCODE", False);
tray_icon_update_manager_window (icon);
root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
gdk_window_add_filter (root_window, tray_icon_manager_filter, icon);
return icon;
}
TrayIcon *tray_icon_new (const gchar *name)
{
TrayIcon *icon;
icon = tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_display), name);
//systray_create ();
return icon;
}
guint tray_icon_send_message (TrayIcon *icon, gint timeout, const gchar *msg, gint len)
{
guint stamp;
g_return_val_if_fail (IS_TRAY_ICON (icon), 0);
g_return_val_if_fail (timeout >= 0, 0);
g_return_val_if_fail (msg != NULL, 0);
if (icon->manager_window == None) {
return 0;
}
if (len < 0) {
len = strlen (msg);
}
stamp = icon->stamp++;
tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE, (Window)gtk_plug_get_id (GTK_PLUG (icon)),timeout, len, stamp);
gdk_error_trap_push ();
while (len > 0) {
XClientMessageEvent ev;
Display *xdisplay;
xdisplay = gdk_display;
ev.type = ClientMessage;
ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
ev.format = 8;
ev.message_type = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
if (len > 20) {
memcpy (&ev.data, msg, 20);
len -= 20;
msg += 20;
} else {
memcpy (&ev.data, msg, len);
len = 0;
}
XSendEvent (xdisplay, icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
XSync (xdisplay, False);
}
gdk_error_trap_pop ();
return stamp;
}
void tray_icon_cancel_message (TrayIcon *icon, guint id)
{
g_return_if_fail (IS_TRAY_ICON (icon));
g_return_if_fail (id > 0);
tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE, (Window)gtk_plug_get_id (GTK_PLUG (icon)),id, 0, 0);
}
static void connect (GtkWidget *w, void *data)
{
}
static void connect_last (GtkWidget *w, void *data)
{
}
static void create_connect_server (GtkWidget *w, void *data)
{
printf ("%s: Unimplementedn", __FUNCTION__);
}
static void do_quit (GtkWidget *w, void *data)
{
//printf ("%s: Unimplementedn", __FUNCTION__);
exit (0);
}
static void systray_menu (GdkEventButton *event)
{
static GtkWidget *menu = NULL;
GtkWidget *entry;
if (menu) {
gtk_widget_destroy (menu);
}
menu = gtk_menu_new ();
switch (status) {
case offline: {
entry = gtk_menu_item_new_with_label ("Connect to last Server");
g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (connect_last), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
break;
}
default:
// do nothing for now
break;
}
switch (status) {
case offline:
break;
case online: {
GtkWidget *systray_connect_menu;
GSList *connect_list = NULL;
GSList *connect_servers = NULL;
struct connect_server *something = NULL;
systray_connect_menu = gtk_menu_new ();
connect_list = connect_servers;
while (connect_list) {
entry = gtk_menu_item_new_with_label ("test");
g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (connect), something);
gtk_menu_shell_append (GTK_MENU_SHELL (systray_connect_menu), entry);
connect_list = g_slist_next (connect_list);
}
if (connect_servers) {
entry = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (systray_connect_menu), entry);
}
entry = gtk_menu_item_new_with_label ("New...");
g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (create_connect_server), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (systray_connect_menu), entry);
break;
}
default:
printf ("Uh oh, unhandled switch in %sn", __FUNCTION__);
}
entry = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
entry = gtk_menu_item_new_with_label ("Quit");
g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (do_quit), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
gtk_widget_show_all (menu);
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time);
}
static void systray_clicked (GtkWidget *button, GdkEventButton *event, void *data)
{
static int showing = 1;
/*
if (event->type != GDK_BUTTON_PRESS) {
printf ("Ooops (%d), got something other than a button press (%s) %dn", __LINE__, __FUNCTION__,
event->type);
}
*/
switch (event->button) {
case 1: {
GtkWidget *w;
GtkWidgetFlags flags;
w = (GtkWidget *)gtk_window_list_toplevels ()->data;
if (GTK_WIDGET_VISIBLE (w)) {
gtk_widget_hide_all (w);
} else {
gtk_widget_show_all (w);
}
break;
}
case 2:
break;
case 3:
systray_menu (event);
break;
}
}
static void systray_update_icon ()
{
const gchar *icon_name = NULL;
switch (icon) {
case offline:
icon_name = GTK_STOCK_NO;
break;
case online:
icon_name = GTK_STOCK_YES;
break;
default:
printf ("Ooops unhandled switch case in %sn", __FUNCTION__);
}
gtk_image_set_from_stock (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
}
static gboolean systray_update_status ()
{
enum systray_status oldstatus;
//enum systray_status is_online, is_offline;
oldstatus = status;
if (is_online)
status = online;
if (is_offline)
status = offline;
if (status != oldstatus) {
icon = status;
systray_update_icon ();
}
return FALSE;
}
static void systray_embedded (GtkWidget *widget, void *data)
{
printf ("Systray: Tray icon is embedded (unimplemented)n");
//gtk_blist_systray_add ();
}
static void systray_remove_callbacks ()
{
printf ("Systray: Removing callbacks");
while (g_source_remove_by_user_data (&systray)) {
printf (".");
}
printf ("n");
}
static void systray_destroyed (GtkWidget *widget, void *data)
{
printf ("Systray: Tray icon destroyedn");
//gtk_blist_systray_remove ();
//systray_flush_queue ();
systray_remove_callbacks ();
g_object_unref (G_OBJECT (systray));
systray = NULL;
//g_idle_add (systray_create, &systray);
}
gboolean systray_create (void)
{
GtkWidget *box;
if (systray) {
/* something is screwy if this is called on an already created systray */
printf ("Systray: Already created!n");
}
systray = tray_icon_new ("Test");
box = gtk_event_box_new ();
image = gtk_image_new ();
g_signal_connect (G_OBJECT (systray), "embedded", G_CALLBACK (systray_embedded), NULL);
g_signal_connect (G_OBJECT (systray), "destroy", G_CALLBACK (systray_destroyed), NULL);
g_signal_connect (G_OBJECT (box), "button-press-event", G_CALLBACK (systray_clicked), NULL);
gtk_container_add (GTK_CONTAINER (box), image);
gtk_container_add (GTK_CONTAINER (systray), box);
gtk_widget_show_all (GTK_WIDGET (systray));
g_object_ref (G_OBJECT (systray));
systray_update_status ();
systray_update_icon ();
return FALSE;
}