1
0
mirror of https://github.com/moparisthebest/FireTray synced 2024-08-13 15:53:47 -04:00

implement experimental cross-fading for blinking chat icon

This commit is contained in:
foudfou 2013-04-01 02:58:15 +02:00
parent 4360e3f499
commit 208db6f906
7 changed files with 148 additions and 20 deletions

View File

@ -84,27 +84,28 @@ firetray.Chat = {
log.debug('startGetAttentionMaybe');
let convIsCurrentlyShown = this.isConvCurrentlyShown(conv);
log.debug("convIsCurrentlyShown="+convIsCurrentlyShown);
if (!convIsCurrentlyShown) { // don't blink when conv tab already on top
this.acknowledgeOnFocus.must = true;
this.acknowledgeOnFocus.conv = conv;
if (convIsCurrentlyShown) // don't blink when conv tab already on top
return;
/* there can potentially be multiple windows, each with a Chat tab and
the same conv open... so we need to handle all windows */
for (let xid in firetray.Handler.windows) {
let win = firetray.Handler.windows[xid].chromeWin;
let contactlist = win.document.getElementById("contactlistbox");
for (let i=0; i<contactlist.itemCount; ++i) {
let item = contactlist.getItemAtIndex(i);
if (item.localName !== 'imconv')
continue;
if (item.hasOwnProperty('conv') && item.conv.target === conv) {
firetray.ChatStatusIcon.setUrgency(xid, true);
}
this.acknowledgeOnFocus.must = true;
this.acknowledgeOnFocus.conv = conv;
/* there can potentially be multiple windows, each with a Chat tab and
the same conv open... so we need to handle all windows */
for (let xid in firetray.Handler.windows) {
let win = firetray.Handler.windows[xid].chromeWin;
let contactlist = win.document.getElementById("contactlistbox");
for (let i=0; i<contactlist.itemCount; ++i) {
let item = contactlist.getItemAtIndex(i);
if (item.localName !== 'imconv')
continue;
if (item.hasOwnProperty('conv') && item.conv.target === conv) {
firetray.ChatStatusIcon.setUrgency(xid, true);
}
}
firetray.ChatStatusIcon.startIconBlinking();
}
firetray.ChatStatusIcon.startIconBlinking();
},
/**

View File

@ -140,6 +140,8 @@ function gdk_defines(lib) {
this.GDK_SUBSTRUCTURE_MASK = 1 << 20,
this.GDK_SCROLL_MASK = 1 << 21,
this.GDK_ALL_EVENTS_MASK = 0x3FFFFE
this.GdkColorspace = ctypes.int; // enum
this.GDK_COLORSPACE_RGB = 0;
this.GdkWindow = ctypes.StructType("GdkWindow");
this.GdkByteOrder = ctypes.int; // enum
@ -262,9 +264,21 @@ function gdk_defines(lib) {
lib.lazy_bind("gdk_screen_get_toplevel_windows", gobject.GList.ptr, this.GdkScreen.ptr);
lib.lazy_bind("gdk_pixbuf_new_from_file", this.GdkPixbuf.ptr, gobject.gchar.ptr, glib.GError.ptr.ptr);
lib.lazy_bind("gdk_pixbuf_copy", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_composite", ctypes.void_t, this.GdkPixbuf.ptr, this.GdkPixbuf.ptr, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.double, ctypes.double, ctypes.double, ctypes.double, ctypes.int, ctypes.int);
lib.lazy_bind("gdk_pixbuf_get_has_alpha", gobject.gboolean, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_add_alpha", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr, gobject.gboolean, gobject.guchar, gobject.guchar, gobject.guchar);
lib.lazy_bind("gdk_pixbuf_get_colorspace", this.GdkColorspace, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_get_n_channels", ctypes.int, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_get_has_alpha", gobject.gboolean, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_get_bits_per_sample", ctypes.int, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_get_pixels", gobject.guchar.ptr, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_get_width", ctypes.int, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_get_height", ctypes.int, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_composite", ctypes.void_t, this.GdkPixbuf.ptr, this.GdkPixbuf.ptr, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.double, ctypes.double, ctypes.double, ctypes.double, ctypes.int, ctypes.int);
lib.lazy_bind("gdk_pixbuf_get_rowstride", ctypes.int, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_get_byte_length", gobject.gsize, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_pixbuf_copy", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr);
lib.lazy_bind("gdk_screen_get_system_colormap", this.GdkColormap.ptr, this.GdkScreen.ptr);
lib.lazy_bind("gdk_colormap_get_visual", this.GdkVisual.ptr, this.GdkColormap.ptr);
lib.lazy_bind("gdk_color_parse", gobject.gboolean, gobject.gchar.ptr, this.GdkColor.ptr);

View File

@ -11,6 +11,7 @@ const Ci = Components.interfaces;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
function gio_defines(lib) {
this.GIcon = ctypes.StructType("GIcon");
@ -18,6 +19,7 @@ function gio_defines(lib) {
lib.lazy_bind("g_themed_icon_new", this.GIcon.ptr, ctypes.char.ptr);
lib.lazy_bind("g_themed_icon_new_from_names", this.GIcon.ptr, ctypes.char.ptr.ptr, ctypes.int);
lib.lazy_bind("g_themed_icon_get_names", gobject.gchar.ptr.ptr, this.GThemedIcon.ptr);
}

View File

@ -21,7 +21,15 @@ function gtk_defines(lib) {
this.FIRETRAY_REQUIRED_GTK_MINOR_VERSION = 20;
this.FIRETRAY_REQUIRED_GTK_MICRO_VERSION = 0;
this.GTK_ICON_SIZE_MENU = 1; // enum GtkIconSize
this.GtkIconSize = ctypes.int; // enum
this.GTK_ICON_SIZE_INVALID = 0;
this.GTK_ICON_SIZE_MENU = 1;
this.GTK_ICON_SIZE_SMALL_TOOLBAR = 2;
this.GTK_ICON_SIZE_LARGE_TOOLBAR = 3;
this.GTK_ICON_SIZE_BUTTON = 4;
this.GTK_ICON_SIZE_DND = 5;
this.GTK_ICON_SIZE_DIALOG = 6;
this.GTK_WINDOW_TOPLEVEL = 0; // enum GtkWindowType
this.GtkStatusIcon = ctypes.StructType("GtkStatusIcon");
@ -56,6 +64,13 @@ function gtk_defines(lib) {
this.GtkWindow = ctypes.StructType("GtkWindow");
this.GtkWindowType = ctypes.int; // enum
this.GtkSeparatorMenuItem = ctypes.StructType("GtkSeparatorMenuItem");
this.GtkIconInfo = ctypes.StructType("GtkIconInfo");
this.GtkIconLookupFlags = ctypes.int; // enum
this.GTK_ICON_LOOKUP_NO_SVG = 1 << 0;
this.GTK_ICON_LOOKUP_FORCE_SVG = 1 << 1;
this.GTK_ICON_LOOKUP_USE_BUILTIN = 1 << 2;
this.GTK_ICON_LOOKUP_GENERIC_FALLBACK = 1 << 3;
this.GTK_ICON_LOOKUP_FORCE_SIZE = 1 << 4;
this.GtkMenuPositionFunc_t = ctypes.FunctionType(
ctypes.default_abi, ctypes.void_t,
@ -88,6 +103,9 @@ function gtk_defines(lib) {
lib.lazy_bind("gtk_icon_theme_get_for_screen", this.GtkIconTheme.ptr, gdk.GdkScreen.ptr);
lib.lazy_bind("gtk_icon_theme_append_search_path", ctypes.void_t, this.GtkIconTheme.ptr, gobject.gchar.ptr);
lib.lazy_bind("gtk_icon_theme_prepend_search_path", ctypes.void_t, this.GtkIconTheme.ptr, gobject.gchar.ptr);
lib.lazy_bind("gtk_icon_theme_choose_icon", this.GtkIconInfo.ptr, this.GtkIconTheme.ptr, gobject.gchar.ptr.array(), gobject.gint, this.GtkIconLookupFlags);
lib.lazy_bind("gtk_icon_info_load_icon", gdk.GdkPixbuf.ptr, this.GtkIconInfo.ptr, glib.GError.ptr.ptr);
lib.lazy_bind("gtk_icon_info_free", ctypes.void_t, this.GtkIconInfo.ptr);
lib.lazy_bind("gtk_status_icon_new", this.GtkStatusIcon.ptr);
lib.lazy_bind("gtk_status_icon_set_from_file", ctypes.void_t, this.GtkStatusIcon.ptr, ctypes.char.ptr);
@ -112,6 +130,9 @@ function gtk_defines(lib) {
lib.lazy_bind("gtk_widget_create_pango_layout", pango.PangoLayout.ptr, this.GtkWidget.ptr, gobject.gchar.ptr);
lib.lazy_bind("gtk_widget_destroy", ctypes.void_t, this.GtkWidget.ptr);
lib.lazy_bind("gtk_status_icon_set_from_pixbuf", ctypes.void_t, this.GtkStatusIcon.ptr, gdk.GdkPixbuf.ptr);
lib.lazy_bind("gtk_status_icon_get_pixbuf", gdk.GdkPixbuf.ptr, this.GtkStatusIcon.ptr);
lib.lazy_bind("gtk_status_icon_get_gicon", gio.GIcon.ptr, this.GtkStatusIcon.ptr);
lib.lazy_bind("gtk_status_icon_get_storage_type", ctypes.int, this.GtkStatusIcon.ptr); // TEST
lib.lazy_bind("gtk_window_list_toplevels", gobject.GList.ptr);
lib.lazy_bind("gtk_window_get_title", gobject.gchar.ptr, this.GtkWindow.ptr);
lib.lazy_bind("gtk_window_is_active", gobject.gboolean, this.GtkWindow.ptr);

View File

@ -16,6 +16,7 @@ Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
Cu.import("resource://firetray/linux/FiretrayWindow.jsm");
Cu.import("resource://firetray/commons.js");
Cu.import("resource://firetray/promise.js");
firetray.Handler.subscribeLibsForClosing([gobject, gio, gtk]);
if ("undefined" == typeof(firetray.Handler))
@ -88,6 +89,95 @@ firetray.ChatStatusIcon = {
gtk.gtk_status_icon_set_from_pixbuf(this.trayIcon, null);
},
/**
* EXPERIMENTAL fancy blinking.
* TODO: how to wait for last fade in to restore themedIconNameCurrent
*/
crossFade: function() {
/* borrowed from mozmill utils.js*/
function sleep(milliseconds) {
var timeup = false;
function wait() { timeup = true; }
let timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
timer.initWithCallback(wait, milliseconds, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
var thread = Components.classes["@mozilla.org/thread-manager;1"].
getService().currentThread;
while(!timeup) {
thread.processNextEvent(true);
}
}
let icon_theme = gtk.gtk_icon_theme_get_for_screen(gdk.gdk_screen_get_default());
firetray.ChatStatusIcon.timers['cross-fade'] = firetray.Utils.timer(
500, Ci.nsITimer.TYPE_REPEATING_SLACK, function() {
// get pixbuf
let arry = gobject.gchar.ptr.array()(2);
arry[0] = gobject.gchar.array()(firetray.ChatStatusIcon.themedIconNameCurrent);
arry[1] = null;
log.debug("theme="+icon_theme+", arry="+arry);
let icon_info = gtk.gtk_icon_theme_choose_icon(icon_theme, arry, 22, gtk.GTK_ICON_LOOKUP_FORCE_SIZE);
// create pixbuf
let pixbuf = gdk.gdk_pixbuf_copy(gtk.gtk_icon_info_load_icon(icon_info, null));
gtk.gtk_icon_info_free(icon_info); // gobject.g_object_unref(icon_info) in 3.8
// checks
if (gdk.gdk_pixbuf_get_colorspace(pixbuf) != gdk.GDK_COLORSPACE_RGB)
log.error("wrong colorspace for pixbuf");
if (gdk.gdk_pixbuf_get_bits_per_sample(pixbuf) != 8)
log.error("wrong bits_per_sample for pixbuf");
if (!gdk.gdk_pixbuf_get_has_alpha(pixbuf))
log.error("pixbuf doesn't have alpha");
let n_channels = gdk.gdk_pixbuf_get_n_channels(pixbuf);
if (n_channels != 4)
log.error("wrong nb of channels for pixbuf");
// init transform
let width = gdk.gdk_pixbuf_get_width(pixbuf);
let height = gdk.gdk_pixbuf_get_height(pixbuf);
log.warn("width="+width+", height="+height);
let rowstride = gdk.gdk_pixbuf_get_rowstride(pixbuf);
log.warn("rowstride="+rowstride);
let length = width*height*n_channels;
let pixels = ctypes.cast(gdk.gdk_pixbuf_get_pixels(pixbuf),
gobject.guchar.array(length).ptr);
log.warn("pixels="+pixels);
// backup alpha for later fade-in
let buffer = new ArrayBuffer(width*height);
let alpha_bak = new Uint8Array(buffer);
for (let i=3; i<length; i+=n_channels)
alpha_bak[(i-3)/n_channels] = pixels.contents[i];
const ALPHA_STEP = 5;
// fade out
for (let a=255; a>0; a-=ALPHA_STEP) {
for(let i=3; i<length; i+=n_channels)
if (pixels.contents[i]-ALPHA_STEP>0)
pixels.contents[i] -= ALPHA_STEP;
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf);
sleep(10);
}
// fade in
for (let a=255; a>0; a-=ALPHA_STEP) {
for(let i=3; i<length; i+=n_channels)
if (pixels.contents[i]+ALPHA_STEP<=alpha_bak[(i-3)/n_channels]) {
pixels.contents[i] += ALPHA_STEP;
}
gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf);
sleep(10);
}
gobject.g_object_unref(pixbuf);
});
},
startIconBlinking: function() { // gtk_status_icon_set_blinking() deprecated
this.on = true;
firetray.ChatStatusIcon.timers['blink'] = firetray.Utils.timer(

View File

@ -26,7 +26,6 @@ firetray.PopupMenu = {
menu: null,
menuShell: null,
menuSeparatorWindows: null,
MIN_FONT_SIZE: 4,
init: function() {
this.menu = gtk.gtk_menu_new();

View File

@ -26,6 +26,7 @@ if ("undefined" == typeof(firetray.Handler))
firetray.StatusIcon = {
MIN_FONT_SIZE: 4,
FILENAME_BLANK: null,
initialized: false,