1
0
mirror of https://github.com/moparisthebest/FireTray synced 2024-12-22 05:48:49 -05:00

proof of concept version

This commit is contained in:
foudfou 2011-07-04 00:00:07 +02:00
parent 1366a19410
commit c842690051
14 changed files with 3255 additions and 368 deletions

View File

@ -1,6 +1,6 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Components.utils.import("resource://sce/commons.js"); Components.utils.import("resource://mozt/commons.js");
const Cc = Components.classes; const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE prefwindow SYSTEM "chrome://sce/locale/options.dtd"> <!DOCTYPE prefwindow SYSTEM "chrome://mozt/locale/options.dtd">
<prefwindow id="sce-preferences" <prefwindow id="mozt-preferences"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&prefwindow.title;" title="&prefwindow.title;"
onload= "mozt.UIOptions.onLoad()"> onload= "mozt.UIOptions.onLoad()">

View File

@ -1,63 +1,39 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* Components.utils.import("resource://mozt/commons.js");
* GLOBAL APPROACH: Components.utils.import("resource://mozt/LibGtkStatusIcon.js");
*
* since we can't avoid the about:certerr page (1), and can't shortcut the
* internal request to about:certerr gracefully (2), we:
*
* - add the cert exception
* - wait for the full load of the about:certerr page (that's the tricky part)
* - load the initially requested URL
*
* (1) certerror is hardly avoidable since it may be displayed whenever a
* newsocket is created, see: nsNSSIOLayer.cpp: dialogs->ShowCertError,
* nsNSSBadCertHandler, nsSSLIOLayerNewSocket,
* ./netwerk/base/src/nsSocketTransport2.cpp
*
* (2) a raw reload of the requested https page works, but is not very clean
* since it shortcuts the internal request to about:certerr, and produces a
* harmless *no element found* error (displayed shortly and not too noticeable
* though)
*/
Components.utils.import("resource://sce/commons.js");
mozt.Main = { mozt.Main = {
onLoad: function() { onLoad: function() {
// initialization code // initialization code
this.initialized = null; this.initialized = null;
this.strings = document.getElementById("sce-strings"); this.strings = document.getElementById("mozt-strings");
this.overrideService = null;
this.recentCertsService = null;
this.notification = {};
this.stash = {};
try { try {
// Set up preference change observer // Set up preference change observer
mozt.Utils.prefService.QueryInterface(Ci.nsIPrefBranch2); mozt.Utils.prefService.QueryInterface(Ci.nsIPrefBranch2);
// must stay out of _toggle() // must stay out of _toggle()
mozt.Utils.prefService.addObserver("", this, false); mozt.Utils.prefService.addObserver("", this, false);
// Get cert services
this.overrideService =
Cc["@mozilla.org/security/certoverride;1"]
.getService(Components.interfaces.nsICertOverrideService);
this.recentCertsService = Cc["@mozilla.org/security/recentbadcerts;1"]
.getService(Ci.nsIRecentBadCertsService);
} }
catch (ex) { catch (ex) {
Components.utils.reportError(ex); Components.utils.reportError(ex);
return false; return false;
} }
var enabled = mozt.Utils.prefService.getBoolPref('enabled');
mozt.Debug.dump('enabled: '+enabled);
if (enabled)
this._toggle(true);
mozt.Debug.dump('SkipCertError LOADED !'); LibGtkStatusIcon.init();
/*
GtkStatusIcon *tray_icon = gtk_status_icon_new();
GdkPixbuf *default_icon = gdk_pixbuf_new_from_xpm_data(firefox_xpm);
gtk_status_icon_set_from_pixbuf(GTK_STATUS_ICON(tray_icon),
GDK_PIXBUF(default_icon));
*/
this.tray_icon = LibGtkStatusIcon.gtk_status_icon_new();
// var pixmap = "hi"; // TODO: read pixmap from file
// LibGtkStatusIcon.gdk_pixbuf_new_from_xpm_data(pixmap);
mozt.Debug.dump('Moztray LOADED !');
this.initialized = true; this.initialized = true;
return true; return true;
}, },
@ -65,30 +41,12 @@ mozt.Main = {
onQuit: function() { onQuit: function() {
// Remove observer // Remove observer
mozt.Utils.prefService.removeObserver("", this); mozt.Utils.prefService.removeObserver("", this);
LibGtkStatusIcon.shutdown();
this._toogle(false);
mozt.Debug.dump('SkipCertError UNLOADED !'); mozt.Debug.dump('SkipCertError UNLOADED !');
this.initialized = false; this.initialized = false;
}, },
// since we are using a TabsProgressListener, it seems we do not need to keep
// track of WebProgressListeners as indicated on
// https://developer.mozilla.org/en/XUL_School/Intercepting_Page_Loads#WebProgressListeners
_toggle: function (enable) {
mozt.Debug.dump('toggle: '+enable);
try {
if (enable) {
gBrowser.addTabsProgressListener(this.TabsProgressListener);
} else {
gBrowser.removeTabsProgressListener(this.TabsProgressListener);
}
} catch (ex) {
Components.utils.reportError(ex);
return false;
}
},
observe: function(subject, topic, data) { observe: function(subject, topic, data) {
// Observer for pref changes // Observer for pref changes
if (topic != "nsPref:changed") return; if (topic != "nsPref:changed") return;
@ -102,301 +60,6 @@ mozt.Main = {
} }
}, },
_getCertException: function(uri, cert) {
var outFlags = {};
var outTempException = {};
var knownCert = mozt.Main.overrideService.hasMatchingOverride(
uri.asciiHost,
uri.port,
cert,
outFlags,
outTempException);
return knownCert;
},
_addCertException: function(SSLStatus, uri, cert) {
var flags = 0;
if(SSLStatus.isUntrusted)
flags |= mozt.Main.overrideService.ERROR_UNTRUSTED;
if(SSLStatus.isDomainMismatch)
flags |= mozt.Main.overrideService.ERROR_MISMATCH;
if(SSLStatus.isNotValidAtThisTime)
flags |= mozt.Main.overrideService.ERROR_TIME;
mozt.Main.overrideService.rememberValidityOverride(
uri.asciiHost, uri.port,
cert,
flags,
mozt.Utils.prefService.getBoolPref('add_temporary_exceptions'));
mozt.Debug.dump("CertEx added");
mozt.Main.TabsProgressListener._certExceptionJustAdded = true;
mozt.Debug.dump("certEx changed: " + mozt.Main.TabsProgressListener._certExceptionJustAdded);
mozt.Main.TabsProgressListener._goto = uri.spec; // never reset
},
_parseBadCertFlags: function(flags) {
var tag = '';
var ns = Ci.nsIX509Cert;
if (flags & ns.NOT_VERIFIED_UNKNOWN)
tag += ', ' + mozt.Main.strings.getString('NOT_VERIFIED_UNKNOWN');
if (flags & ns.CERT_REVOKED)
tag += ', ' + mozt.Main.strings.getString('CERT_REVOKED');
if (flags & ns.CERT_EXPIRED)
tag += ', ' + mozt.Main.strings.getString('CERT_EXPIRED');
if (flags & ns.CERT_NOT_TRUSTED)
tag += ', ' + mozt.Main.strings.getString('CERT_NOT_TRUSTED');
if (flags & ns.ISSUER_NOT_TRUSTED)
tag += ', ' + mozt.Main.strings.getString('ISSUER_NOT_TRUSTED');
if (flags & ns.ISSUER_UNKNOWN)
tag += ', ' + mozt.Main.strings.getString('ISSUER_UNKNOWN');
if (flags & ns.INVALID_CA)
tag += ', ' + mozt.Main.strings.getString('INVALID_CA');
if (flags & ns.USAGE_NOT_ALLOWED)
tag += ', ' + mozt.Main.strings.getString('USAGE_NOT_ALLOWED');
if (flags & SCE_CERT_SELF_SIGNED)
tag += ', ' + mozt.Main.strings.getString('CERT_SELF_SIGNED');
if (tag != "") tag = tag.substr(2);
return tag;
},
notify: function(abrowser) {
// find the correct tab to display notification on
var mainWindow = window
.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
var notificationBox = mainWindow.gBrowser.getNotificationBox(abrowser);
mozt.Main.stash.notificationBox = notificationBox; // stash for later use
// check notification not already here
var notificationValue = mozt.Main.notification.type + '_' + mozt.Main.notification.host;
if (notificationBox.getNotificationWithValue(notificationValue)) {
mozt.Debug.dump("notificationBox already here");
return;
}
// build notification
var temporaryException = mozt.Utils.prefService.getBoolPref('add_temporary_exceptions') ?
mozt.Main.strings.getString('temporaryException') : mozt.Main.strings.getString('permanentException');
var msgArgs = [];
var priority = null; // notificationBox.PRIORITY_INFO_LOW not working ??
switch (mozt.Main.notification.type) {
case 'exceptionAdded':
msgArgs = [temporaryException, mozt.Main.notification.host];
priority = 'PRIORITY_INFO_LOW';
break;
case 'exceptionNotAdded':
msgArgs = [mozt.Main.notification.dontBypassFlags];
priority = 'PRIORITY_WARNING_LOW';
break;
default:
break;
}
var message = mozt.Main.strings.getFormattedString(
mozt.Main.notification.type, msgArgs);
// appendNotification( label , value , image , priority , buttons )
var notification = notificationBox.appendNotification(
message, notificationValue, null, notificationBox[priority], null);
// close notificatioBox if needed (will close automatically if reload)
var exceptionDialogButton = abrowser.webProgress.DOMWindow
.document.getElementById('exceptionDialogButton');
exceptionDialogButton.addEventListener(
"click", mozt.Main.exceptionDialogButtonOnClick, false);
mozt.Main.notification = {}; // reset
},
exceptionDialogButtonOnClick: function(event) {
mozt.Main._closeNotificationMaybe();
event.originalTarget.removeEventListener(
"click", mozt.Main.exceptionDialogButtonOnClick, false);
},
_closeNotificationMaybe: function() {
if (!mozt.Main.stash.notificationBox)
return;
mozt.Main.stash.notificationBox.currentNotification.close();
mozt.Main.stash.notificationBox = null;
},
// a TabProgressListner seems more appropriate than an Observer, which only
// gets notified for document requests (not internal requests)
TabsProgressListener: {
// can't see the necessity of having QueryInterface(aIID) implemented...
_certExceptionJustAdded: null, // used for communication btw
// onSecurityChange, onStateChange, ...
_certerrorCount: 0, // certerr seems called more than once...
// This method will be called on security transitions (eg HTTP -> HTTPS,
// HTTPS -> HTTP, FOO -> HTTPS) and *after document load* completion. It
// might also be called if an error occurs during network loading.
onSecurityChange: function (aBrowser, aWebProgress, aRequest, aState) {
var uri = aBrowser.currentURI;
mozt.Debug.dump("onSecurityChange: uri=" + uri.prePath);
if (!uri.schemeIs("https")) return;
this._certerrorCount = 0; // reset
// retrieve bad cert from nsIRecentBadCertsService
// NOTE: experience shows that nsIRecentBadCertsService will not provide
// SSLStatus when cert is known or trusted. That's why we don't try to
// get it from aRequest
var port = uri.port;
if (port == -1) port = 443; // thx http://gitorious.org/perspectives-notary-server/
var hostWithPort = uri.host + ":" + port;
mozt.Main.notification.host = uri.host;
var SSLStatus = mozt.Main.recentCertsService.getRecentBadCert(hostWithPort);
if (!SSLStatus) {
mozt.Debug.dump("no SSLStatus for: " + hostWithPort);
return;
}
mozt.Debug.dump("SSLStatus");
mozt.Debug.dumpObj(SSLStatus);
var cert = SSLStatus.serverCert;
mozt.Debug.dump("cert");
mozt.Debug.dumpObj(cert);
// check if cert already known/added
var knownCert = mozt.Main._getCertException(uri, cert);
if (knownCert) {
mozt.Debug.dump("known cert: " + knownCert);
return;
}
// Determine cert problems
var dontBypassFlags = 0;
// we're only interested in certs with characteristics
// defined in options (self-signed, issuer unknown, ...)
cert.QueryInterface(Ci.nsIX509Cert3);
var isSelfSigned = cert.isSelfSigned;
mozt.Debug.dump("isSelfSigned:" + isSelfSigned);
if (isSelfSigned
&& !mozt.Utils.prefService.getBoolPref("bypass_self_signed"))
dontBypassFlags |= SCE_CERT_SELF_SIGNED;
// NOTE: isSelfSigned *implies* ISSUER_UNKNOWN (should be handled
// correctly in option dialog)
var verificationResult = cert.verifyForUsage(Ci.nsIX509Cert.CERT_USAGE_SSLServer);
switch (verificationResult) {
case Ci.nsIX509Cert.ISSUER_NOT_TRUSTED: // including self-signed
mozt.Debug.dump("issuer not trusted");
case Ci.nsIX509Cert.ISSUER_UNKNOWN:
mozt.Debug.dump("issuer unknown");
mozt.Debug.dump("bypass_issuer_unknown: " + mozt.Utils.prefService.getBoolPref("bypass_issuer_unknown"));
if (!mozt.Utils.prefService.getBoolPref("bypass_issuer_unknown"))
dontBypassFlags |= Ci.nsIX509Cert.ISSUER_UNKNOWN;
default:
mozt.Debug.dump("verificationResult: " + verificationResult);
break;
}
var dontBypassTag = mozt.Main._parseBadCertFlags(dontBypassFlags);
mozt.Debug.dump("dontBypassFlags=" + dontBypassFlags + ", " + dontBypassTag);
// trigger notification
if (mozt.Utils.prefService.getBoolPref('notify')) {
mozt.Main.notification.willNotify = true;
mozt.Debug.dump("onSecurityChange: willNotify");
}
// Add cert exception (if bypass allowed by options)
if (dontBypassFlags == 0) {
mozt.Main._addCertException(SSLStatus, uri, cert);
mozt.Main.notification.type = 'exceptionAdded';
} else {
mozt.Main.notification.type = 'exceptionNotAdded';
mozt.Main.notification.dontBypassFlags = dontBypassTag;
}
}, // END onSecurityChange
_getTabIndex: function(abrowser) {
var tabbrowser = abrowser.getTabBrowser();
var tabContainer = tabbrowser.tabs;
var tabIndex = null;
for (var i = 0; i < tabContainer.length; ++i) {
if (abrowser == tabbrowser.getBrowserAtIndex(i)) {
tabIndex = i;
break;
}
}
return tabIndex;
},
// "We can't look for this during onLocationChange since at that point the
// document URI is not yet the about:-uri of the error page." (browser.js)
// Experience shows that the order is as follows: badcert
// (onSecurityChange) leading to about:blank, then request of
// about:document-onload-blocker, leading to about:certerror (called at
// least twice)
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// aProgress.DOMWindow is the tab/window which triggered the change.
var originDoc = aWebProgress.DOMWindow.document;
var originURI = originDoc.documentURI;
mozt.Debug.dump("onStateChange " + this._getTabIndex(aBrowser) + ": originURI=" + originURI);
var safeRequestName = mozt.Utils.safeGetName(aRequest);
mozt.Debug.dump("safeRequestName: " + safeRequestName);
// WE JUST CAN'T CANCEL THE REQUEST FOR about:certerr |
// about:document-onload-blocker ...SO WE WAIT FOR IT !
if (aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP
|Ci.nsIWebProgressListener.STATE_IS_REQUEST)) {
if (/^about:certerr/.test(originURI)) {
this._certerrorCount++;
mozt.Debug.dump("certerrorCount=" + this._certerrorCount);
if (this._certerrorCount < 2) {
if (aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP
|Ci.nsIWebProgressListener.STATE_RESTORING)) {
// experienced only one certerr call during sessoin restore
mozt.Debug.dump("restoring");
} else {
mozt.Debug.dump("certerrorCount not sufficient");
return; // wait for last (?) call
}
}
if (this._certExceptionJustAdded) {
this._certExceptionJustAdded = false; // reset
mozt.Debug.dump("certEx changed: " + this._certExceptionJustAdded);
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
aBrowser.loadURI(this._goto, null, null);
}
if (mozt.Main.notification.willNotify) {
mozt.Debug.dump("onStateChange: willNotify");
mozt.Main.notify.willNotify = false; // reset
mozt.Main.notify(aBrowser);
}
}
}
}, // END onStateChange
onLocationChange: function() { },
onProgressChange: function() { },
onStatusChange: function() { },
}, // END TabsProgressListener
}; };

BIN
src/chrome/skin/icon32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -1,22 +1,22 @@
/* This is just an example. You shouldn't do this. */ /* This is just an example. You shouldn't do this. */
#sce-hello #mozt-hello
{ {
color: red ! important; color: red ! important;
} }
#sce-toolbar-button #mozt-toolbar-button
{ {
list-style-image: url("chrome://sce/skin/toolbar-button.png"); list-style-image: url("chrome://mozt/skin/toolbar-button.png");
-moz-image-region: rect(0px 24px 24px 0px); -moz-image-region: rect(0px 24px 24px 0px);
} }
#sce-toolbar-button:hover #mozt-toolbar-button:hover
{ {
-moz-image-region: rect(24px 24px 48px 0px); -moz-image-region: rect(24px 24px 48px 0px);
} }
[iconsize="small"] #sce-toolbar-button [iconsize="small"] #mozt-toolbar-button
{ {
-moz-image-region: rect( 0px 40px 16px 24px); -moz-image-region: rect( 0px 40px 16px 24px);
} }
[iconsize="small"] #sce-toolbar-button:hover [iconsize="small"] #mozt-toolbar-button:hover
{ {
-moz-image-region: rect(24px 40px 40px 24px); -moz-image-region: rect(24px 40px 40px 24px);
} }

View File

@ -1,5 +1,5 @@
// https://developer.mozilla.org/en/Localizing_extension_descriptions // https://developer.mozilla.org/en/Localizing_extension_descriptions
pref("extensions.skipcerterror@foudil.fr.description", "chrome://sce/locale/overlay.properties"); pref("extensions.skipcerterror@foudil.fr.description", "chrome://mozt/locale/overlay.properties");
// Extension prefs // Extension prefs
pref("extensions.mozt.enabled", true); pref("extensions.mozt.enabled", true);

View File

@ -1,19 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest"> <Description about="urn:mozilla:install-manifest">
<em:id>skipcerterror@foudil.fr</em:id> <em:id>moztray@foudil.fr</em:id>
<em:unpack>false</em:unpack> <em:unpack>false</em:unpack>
<em:type>2</em:type> <em:type>2</em:type>
<em:name>Mozilla Tray</em:name> <em:name>Mozilla Tray</em:name>
<em:version>0.0.1</em:version> <em:version>0.0.1</em:version>
<em:creator>Foudil BRÉTEL</em:creator> <em:creator>Foudil BRÉTEL</em:creator>
<em:contributor></em:contributor>
<em:contributor>Hua Luo, Francesco Solero (Firetray original authors)</em:contributor> <em:contributor>Hua Luo, Francesco Solero (Firetray original authors)</em:contributor>
<em:homepageURL>https://github.com/foudfou/moztray</em:homepageURL> <em:homepageURL>https://github.com/foudfou/moztray</em:homepageURL>
<em:description></em:description> <em:description></em:description>
<em:optionsURL>chrome://mozt/content/options.xul</em:optionsURL> <!-- <em:optionsURL>chrome://mozt/content/options.xul</em:optionsURL> -->
<em:iconURL>chrome://mozt/skin/icon48.png</em:iconURL> <em:iconURL>chrome://mozt/skin/icon32.png</em:iconURL>
<em:icon64URL>chrome://mozt/skin/icon64.png</em:icon64URL>
<em:targetApplication> <em:targetApplication>
<Description> <Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- Firefox --> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- Firefox -->

View File

@ -0,0 +1,81 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
var EXPORTED_SYMBOLS = ["LibGtkStatusIcon"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const LIB_GTK = "libgtk-x11-2.0.so";
var LibGtkStatusIcon = {
_lib: null,
init: function() {
// If ctypes doesn't exist, try to get it
Cu.import("resource://gre/modules/ctypes.jsm");
// If we still don't have ctypes, this isn't going to work...
if (typeof(ctypes) == "undefined") {
throw ("Could not load JS-Ctypes");
}
try {
// Try to start up dependencies - if they fail, they'll throw
// exceptions. ex: GObjectLib.init();
this._lib = ctypes.open(LIB_GTK);
if (!this._lib)
throw ("Could not load " + LIB_GTK);
} catch (e) {
this.shutdown();
throw(e);
}
// Ok, we got everything - let's declare.
this._declare();
},
shutdown: function() {
// Close our connection to the library.
if (this._lib)
this._lib.close();
},
_declare: function() {
// Types
this.GtkStatusIcon = ctypes.StructType("GtkStatusIcon");
this.GtkStatusIconRef = ctypes.PointerType(this.GtkStatusIcon);
this.GdkPixbuf = ctypes.StructType("GdkPixbuf");
this.GdkPixbufRef = ctypes.PointerType(this.GdkPixbuf);
this.Pixbuf = ctypes.PointerType(ctypes.char.ptr);
// Consts
this.INDICATOR_MESSAGES_SERVER_TYPE = "message";
// Functions
this.gtk_status_icon_new = this._lib.declare(
"gtk_status_icon_new",
ctypes.default_abi,
this.GtkStatusIconRef
);
this.gdk_pixbuf_new_from_xpm_data = this._lib.declare(
"gdk_pixbuf_new_from_xpm_data",
ctypes.default_abi,
this.GdkPixbufRef,
this.Pixbuf
);
this.gtk_status_icon_set_from_pixbuf = this._lib.declare(
"gtk_status_icon_set_from_pixbuf",
ctypes.default_abi,
this.GtkStatusIconRef,
this.GdkPixbufRef
);
}
};

4
testing/Makefile Normal file
View File

@ -0,0 +1,4 @@
includes := $(shell pkg-config --libs --cflags gtk+-2.0)
all:
gcc $(includes) -o gtk_icon_example gkt_icon_example.c

3096
testing/firefox.xpm Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
#include <gtk/gtk.h>
#include "firefox.xpm"
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);
GdkPixbuf *default_icon = gdk_pixbuf_new_from_xpm_data(firefox_xpm);
gtk_status_icon_set_from_pixbuf(GTK_STATUS_ICON(tray_icon),
GDK_PIXBUF(default_icon));
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;
}

BIN
testing/gtk_icon_example Executable file

Binary file not shown.