[Pkg-mozext-commits] [firetray] 19/38: FreeBSD
David Prévot
taffit at moszumanska.debian.org
Mon Apr 6 15:58:21 UTC 2015
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository firetray.
commit 6b6e1753ef5fa9c3894097e7c9596fd3c35c9f3b
Author: Scott Furry <scott.wl.furry at gmail.com>
Date: Sun Mar 1 19:08:54 2015 -0700
FreeBSD
---
src/Makefile | 5 +-
src/chrome/skin/icons/freebsd | 1 -
.../icons/freebsd/hicolor/22x22/apps/chatzilla.png | 1 +
.../icons/freebsd/hicolor/22x22/apps/firefox.png | 1 +
.../icons/freebsd/hicolor/22x22/apps/seamonkey.png | 1 +
.../freebsd/hicolor/22x22/apps/thunderbird.png | 1 +
.../icons/freebsd/hicolor/22x22/apps/zotero.png | 1 +
.../freebsd/hicolor/22x22/status/mail-unread.png | 1 +
.../hicolor/22x22/status/user-available.png | 1 +
.../freebsd/hicolor/22x22/status/user-away.png | 1 +
.../freebsd/hicolor/22x22/status/user-busy.png | 1 +
.../freebsd/hicolor/22x22/status/user-offline.png | 1 +
src/lib/freebsd | 1 -
src/lib/freebsd/Makefile | 41 ++
src/lib/freebsd/firetray.c | 20 +
src/lib/freebsd/firetray.h | 11 +
src/modules/ctypes/freebsd | 1 -
src/modules/ctypes/freebsd/appindicator.jsm | 58 ++
src/modules/ctypes/freebsd/cairo.jsm | 26 +
src/modules/ctypes/freebsd/gdk.jsm | 333 +++++++++
src/modules/ctypes/freebsd/gio.jsm | 46 ++
src/modules/ctypes/freebsd/glib.jsm | 27 +
src/modules/ctypes/freebsd/gobject.jsm | 140 ++++
src/modules/ctypes/freebsd/gtk.jsm | 174 +++++
src/modules/ctypes/freebsd/libc.jsm | 31 +
src/modules/ctypes/freebsd/pango.jsm | 47 ++
src/modules/ctypes/freebsd/pangocairo.jsm | 22 +
src/modules/ctypes/freebsd/x11.jsm | 255 +++++++
src/modules/freebsd | 1 -
src/modules/freebsd/FiretrayAppIndicator.jsm | 157 +++++
src/modules/freebsd/FiretrayChat.jsm | 332 +++++++++
src/modules/freebsd/FiretrayChatStatusIcon.jsm | 301 ++++++++
src/modules/freebsd/FiretrayGtkIcons.jsm | 67 ++
src/modules/freebsd/FiretrayGtkStatusIcon.jsm | 341 +++++++++
src/modules/freebsd/FiretrayPopupMenu.jsm | 262 +++++++
src/modules/freebsd/FiretrayStatusIcon.jsm | 219 ++++++
src/modules/freebsd/FiretrayWindow.jsm | 761 +++++++++++++++++++++
37 files changed, 3685 insertions(+), 5 deletions(-)
diff --git a/src/Makefile b/src/Makefile
index eaa7f7d..bb30a86 100755
--- a/src/Makefile
+++ b/src/Makefile
@@ -96,7 +96,8 @@ chrome_sources := $(chrome_sources_js) \
$(wildcard $(chrome_source_root)/skin/icons/*.gif) \
$(wildcard $(chrome_source_root)/skin/icons/*.png) \
$(wildcard $(chrome_source_root)/skin/icons/*.svg) \
- $(wildcard $(chrome_source_root)/skin/icons/linux/hicolor/22x22/*/*.png) \
+ $(wildcard $(chrome_source_root)/skin/icons/freebsd/hicolor/22x22/*/*.png) \
+ $(wildcard $(chrome_source_root)/skin/icons/linux/hicolor/22x22/*/*.png) \
$(wildcard $(chrome_source_root)/skin/icons/winnt/*.bmp) \
$(wildcard $(chrome_source_root)/skin/icons/winnt/*.ico) \
$(wildcard $(chrome_source_root)/locale/*/*.dtd) \
@@ -109,8 +110,10 @@ modules_dir := modules
modules_sources := $(wildcard $(modules_dir)/*.js) \
$(wildcard $(modules_dir)/*.jsm) \
$(wildcard $(modules_dir)/ctypes/*.jsm) \
+ $(wildcard $(modules_dir)/ctypes/freebsd/*.jsm) \
$(wildcard $(modules_dir)/ctypes/linux/*.jsm) \
$(wildcard $(modules_dir)/ctypes/winnt/*.jsm) \
+ $(wildcard $(modules_dir)/freebsd/*.jsm) \
$(wildcard $(modules_dir)/linux/*.jsm) \
$(wildcard $(modules_dir)/winnt/*.jsm)
diff --git a/src/chrome/skin/icons/freebsd b/src/chrome/skin/icons/freebsd
deleted file mode 120000
index 24905c6..0000000
--- a/src/chrome/skin/icons/freebsd
+++ /dev/null
@@ -1 +0,0 @@
-./src/chrome/skin/icons/linux
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/chatzilla.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/chatzilla.png
new file mode 120000
index 0000000..0bf53df
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/chatzilla.png
@@ -0,0 +1 @@
+../../../../img/chatzilla22.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/firefox.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/firefox.png
new file mode 120000
index 0000000..be7eea5
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/firefox.png
@@ -0,0 +1 @@
+../../../../img/firefox22.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/seamonkey.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/seamonkey.png
new file mode 120000
index 0000000..25c1150
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/seamonkey.png
@@ -0,0 +1 @@
+../../../../img/seamonkey22.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/thunderbird.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/thunderbird.png
new file mode 120000
index 0000000..c7733f5
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/thunderbird.png
@@ -0,0 +1 @@
+../../../../img/thunderbird22.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/zotero.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/zotero.png
new file mode 120000
index 0000000..ed6861f
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/apps/zotero.png
@@ -0,0 +1 @@
+../../../../img/zotero22.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/status/mail-unread.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/mail-unread.png
new file mode 120000
index 0000000..50c5ecb
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/mail-unread.png
@@ -0,0 +1 @@
+../../../../img/mail-unread.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-available.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-available.png
new file mode 120000
index 0000000..4a9d15a
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-available.png
@@ -0,0 +1 @@
+../../../../img/pidgin-tray-available.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-away.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-away.png
new file mode 120000
index 0000000..00139c3
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-away.png
@@ -0,0 +1 @@
+../../../../img/pidgin-tray-away.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-busy.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-busy.png
new file mode 120000
index 0000000..2fa96b0
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-busy.png
@@ -0,0 +1 @@
+../../../../img/pidgin-tray-busy.png
\ No newline at end of file
diff --git a/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-offline.png b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-offline.png
new file mode 120000
index 0000000..55463da
--- /dev/null
+++ b/src/chrome/skin/icons/freebsd/hicolor/22x22/status/user-offline.png
@@ -0,0 +1 @@
+../../../../img/pidgin-tray-offline.png
\ No newline at end of file
diff --git a/src/lib/freebsd b/src/lib/freebsd
deleted file mode 120000
index cd6c4a0..0000000
--- a/src/lib/freebsd
+++ /dev/null
@@ -1 +0,0 @@
-./src/lib/linux
\ No newline at end of file
diff --git a/src/lib/freebsd/Makefile b/src/lib/freebsd/Makefile
new file mode 100644
index 0000000..864147f
--- /dev/null
+++ b/src/lib/freebsd/Makefile
@@ -0,0 +1,41 @@
+deps = gdk-2.0 gdk-pixbuf-2.0 atk
+platform = $(shell uname -p)
+
+CC = gcc
+GCCVERSION = $(shell gcc -dumpversion | cut -f1 -d.)
+CFLAGS += -O3 -fPIC -g -mtune=generic $(shell pkg-config --cflags $(deps))
+LDFLAGS += -shared -rdynamic
+#-Wl,--version-script -Wl,export-versionscript
+#-Wl,-soname,libmystuff.so.1 -o libgnome.so.1.0.1
+LIBS += $(shell pkg-config --libs $(deps))
+
+libs = firetray_$(platform)-gcc$(GCCVERSION).so
+
+all: $(libs)
+ @echo
+ @echo add this line to chrome.manifest:
+ @echo "resource firetray-lib lib/linux/$(libs) abi=Linux_x86_64-gcc3"
+ @echo
+ @echo and use
+ @echo 'Cu.import("resource://firetray/ctypes/libfiretray.jsm");'
+ @echo 'firetray.Handler.subscribeLibsForClosing([libfiretray]);'
+ @echo
+
+$(libs): firetray.o
+ $(CC) $(CFLAGS) $(LDFLAGS) $< $(LIBS) -o $@
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+strip: $(libs)
+ strip $<
+
+clean:
+ rm -rf $(libs) *.o
+
+cp:
+ cp $(libs) ../../lib/
+
+install: clean all strip cp
+
+.PHONY: all clean strip cp install
diff --git a/src/lib/freebsd/firetray.c b/src/lib/freebsd/firetray.c
new file mode 100644
index 0000000..d63e619
--- /dev/null
+++ b/src/lib/freebsd/firetray.c
@@ -0,0 +1,20 @@
+/* pkg-config --libs --cflags gtk+-2.0 */
+
+#include <gtk/gtk.h>
+#include "firetray.h"
+
+int gdk_is_window(void* obj) {
+ return GDK_IS_WINDOW(obj) ? 1 : 0;
+}
+
+int gtk_is_window(void* obj) {
+ return GTK_IS_WINDOW(obj) ? 1 : 0;
+}
+
+int gtk_is_widget(void* obj) {
+ return GTK_IS_WIDGET(obj) ? 1 : 0;
+}
+
+unsigned int gtk_get_major_version(void) {return (unsigned int)gtk_major_version;}
+unsigned int gtk_get_minor_version(void) {return (unsigned int)gtk_minor_version;}
+unsigned int gtk_get_micro_version(void) {return (unsigned int)gtk_micro_version;}
diff --git a/src/lib/freebsd/firetray.h b/src/lib/freebsd/firetray.h
new file mode 100644
index 0000000..ba6fd5e
--- /dev/null
+++ b/src/lib/freebsd/firetray.h
@@ -0,0 +1,11 @@
+#include <gdk/gdk.h>
+
+extern int gdk_is_window(void* obj);
+extern int gtk_is_window(void* obj);
+extern int gtk_is_widget(void* obj);
+
+/* the library version (not headers). These functions are provided natively in
+ * gtk+-3.0 */
+extern unsigned int gtk_get_major_version(void);
+extern unsigned int gtk_get_minor_version(void);
+extern unsigned int gtk_get_micro_version(void);
diff --git a/src/modules/ctypes/freebsd b/src/modules/ctypes/freebsd
deleted file mode 120000
index 182668b..0000000
--- a/src/modules/ctypes/freebsd
+++ /dev/null
@@ -1 +0,0 @@
-./src/modules/ctypes/linux
\ No newline at end of file
diff --git a/src/modules/ctypes/freebsd/appindicator.jsm b/src/modules/ctypes/freebsd/appindicator.jsm
new file mode 100644
index 0000000..ed7a10e
--- /dev/null
+++ b/src/modules/ctypes/freebsd/appindicator.jsm
@@ -0,0 +1,58 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "appind3" ];
+
+const APPINDICATOR_LIBNAME = "appindicator3";
+const APPINDICATOR_ABIS = [ 1 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+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");
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+
+function appindicator_defines(lib) {
+ this.AppIndicator = ctypes.StructType("AppIndicator");
+
+ this.INDICATOR_APPLICATION_DBUS_ADDR = "com.canonical.indicator.application";
+ this.INDICATOR_APPLICATION_DBUS_OBJ = "/com/canonical/indicator/application/service";
+ this.INDICATOR_APPLICATION_DBUS_IFACE = "com.canonical.indicator.application.service";
+ this.NOTIFICATION_WATCHER_DBUS_ADDR = "org.kde.StatusNotifierWatcher";
+ this.NOTIFICATION_WATCHER_DBUS_OBJ = "/StatusNotifierWatcher";
+ this.NOTIFICATION_WATCHER_DBUS_IFACE = "org.kde.StatusNotifierWatcher";
+ this.NOTIFICATION_ITEM_DBUS_IFACE = "org.kde.StatusNotifierItem";
+ this.NOTIFICATION_ITEM_DEFAULT_OBJ = "/StatusNotifierItem";
+ this.NOTIFICATION_APPROVER_DBUS_IFACE = "org.ayatana.StatusNotifierApprover";
+
+ this.AppIndicatorCategory = ctypes.int; // enum
+ this.APP_INDICATOR_CATEGORY_APPLICATION_STATUS = 0; /*< nick=ApplicationStatus >*/
+ this.APP_INDICATOR_CATEGORY_COMMUNICATIONS = 1; /*< nick=Communications >*/
+ this.APP_INDICATOR_CATEGORY_SYSTEM_SERVICES = 2; /*< nick=SystemServices >*/
+ this.APP_INDICATOR_CATEGORY_HARDWARE = 3; /*< nick=Hardware >*/
+ this.APP_INDICATOR_CATEGORY_OTHER = 4; /*< nick=Other >*/
+
+ this.AppIndicatorStatus = ctypes.int; // enum
+ this.APP_INDICATOR_STATUS_PASSIVE = 0; /*< nick=Passive >*/
+ this.APP_INDICATOR_STATUS_ACTIVE = 1; /*< nick=Active >*/
+ this.APP_INDICATOR_STATUS_ATTENTION = 2; /*< nick=NeedsAttention >*/
+
+ lib.lazy_bind("app_indicator_new", this.AppIndicator.ptr, gobject.gchar.ptr, gobject.gchar.ptr, this.AppIndicatorCategory);
+ lib.lazy_bind("app_indicator_set_status", ctypes.void_t, this.AppIndicator.ptr, this.AppIndicatorStatus);
+ lib.lazy_bind("app_indicator_get_status", this.AppIndicatorStatus, this.AppIndicator.ptr);
+ lib.lazy_bind("app_indicator_set_menu", ctypes.void_t, this.AppIndicator.ptr, gtk.GtkMenu.ptr);
+ lib.lazy_bind("app_indicator_set_icon", ctypes.void_t, this.AppIndicator.ptr, gobject.gchar.ptr);
+ lib.lazy_bind("app_indicator_set_attention_icon", ctypes.void_t, this.AppIndicator.ptr, gobject.gchar.ptr);
+ lib.lazy_bind("app_indicator_set_label", ctypes.void_t, this.AppIndicator.ptr, gobject.gchar.ptr, gobject.gchar.ptr);
+ lib.lazy_bind("app_indicator_set_secondary_activate_target", ctypes.void_t, this.AppIndicator.ptr, gtk.GtkWidget.ptr);
+
+ this.ConnectionChangedCb_t = ctypes.FunctionType(
+ ctypes.default_abi, ctypes.void_t, [this.AppIndicator.ptr, gobject.gboolean, gobject.gpointer]).ptr;
+
+ this.OnScrollCb_t = ctypes.FunctionType(
+ ctypes.default_abi, ctypes.void_t, [this.AppIndicator.ptr, gobject.gint, gobject.guint, gobject.gpointer]).ptr;
+};
+
+var appind3 = new ctypes_library(APPINDICATOR_LIBNAME, APPINDICATOR_ABIS, appindicator_defines, this);
diff --git a/src/modules/ctypes/freebsd/cairo.jsm b/src/modules/ctypes/freebsd/cairo.jsm
new file mode 100644
index 0000000..7a3bccc
--- /dev/null
+++ b/src/modules/ctypes/freebsd/cairo.jsm
@@ -0,0 +1,26 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "cairo" ];
+
+const CAIRO_LIBNAME = "cairo";
+const CAIRO_ABIS = [ 2 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
+
+function cairo_defines(lib) {
+ this.cairo_t = ctypes.StructType("cairo_t");
+
+ lib.lazy_bind("cairo_rectangle", ctypes.void_t, this.cairo_t.ptr, ctypes.double, ctypes.double, ctypes.double, ctypes.double);
+ lib.lazy_bind("cairo_set_source_rgb", ctypes.void_t, this.cairo_t.ptr, ctypes.double, ctypes.double, ctypes.double);
+ lib.lazy_bind("cairo_fill", ctypes.void_t, this.cairo_t.ptr);
+ lib.lazy_bind("cairo_move_to", ctypes.void_t, this.cairo_t.ptr, ctypes.double, ctypes.double);
+ lib.lazy_bind("cairo_destroy", ctypes.void_t, this.cairo_t.ptr);
+
+}
+
+new ctypes_library(CAIRO_LIBNAME, CAIRO_ABIS, cairo_defines, this);
diff --git a/src/modules/ctypes/freebsd/gdk.jsm b/src/modules/ctypes/freebsd/gdk.jsm
new file mode 100644
index 0000000..27cf198
--- /dev/null
+++ b/src/modules/ctypes/freebsd/gdk.jsm
@@ -0,0 +1,333 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Firetray
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Messaging, Ltd.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Conley <mconley at mozillamessaging.com>
+ * Foudil Brétel <foudil.newbie+amo at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var EXPORTED_SYMBOLS = [ "gdk" ];
+
+const GDK_LIBNAME = "gdk-x11-2.0";
+const GDK_ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+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/cairo.jsm");
+Cu.import("resource://firetray/ctypes/linux/glib.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/x11.jsm");
+
+function gdk_defines(lib) {
+ this.GdkInterpType = ctypes.int; // enum
+ this.GDK_INTERP_NEAREST = 0;
+ this.GDK_INTERP_TILES = 1;
+ this.GDK_INTERP_BILINEAR = 2;
+ this.GDK_INTERP_HYPE = 3;
+ this.GdkFilterReturn = ctypes.int; // enum
+ this.GDK_FILTER_CONTINUE = 0;
+ this.GDK_FILTER_TRANSLATE = 1;
+ this.GDK_FILTER_REMOVE = 2;
+ this.GdkWindowState = ctypes.int; // enum
+ this.GDK_WINDOW_STATE_WITHDRAWN = 1 << 0,
+ this.GDK_WINDOW_STATE_ICONIFIED = 1 << 1,
+ this.GDK_WINDOW_STATE_MAXIMIZED = 1 << 2,
+ this.GDK_WINDOW_STATE_STICKY = 1 << 3,
+ this.GDK_WINDOW_STATE_FULLSCREEN = 1 << 4,
+ this.GDK_WINDOW_STATE_ABOVE = 1 << 5,
+ this.GDK_WINDOW_STATE_BELOW = 1 << 6;
+ this.GdkEventType = ctypes.int; // enum
+ this.GDK_NOTHING = -1;
+ this.GDK_DELETE = 0;
+ this.GDK_DESTROY = 1;
+ this.GDK_EXPOSE = 2;
+ this.GDK_MOTION_NOTIFY = 3;
+ this.GDK_BUTTON_PRESS = 4;
+ this.GDK_2BUTTON_PRESS = 5;
+ this.GDK_3BUTTON_PRESS = 6;
+ this.GDK_BUTTON_RELEASE = 7;
+ this.GDK_KEY_PRESS = 8;
+ this.GDK_KEY_RELEASE = 9;
+ this.GDK_ENTER_NOTIFY = 10;
+ this.GDK_LEAVE_NOTIFY = 11;
+ this.GDK_FOCUS_CHANGE = 12;
+ this.GDK_CONFIGURE = 13;
+ this.GDK_MAP = 14;
+ this.GDK_UNMAP = 15;
+ this.GDK_PROPERTY_NOTIFY = 16;
+ this.GDK_SELECTION_CLEAR = 17;
+ this.GDK_SELECTION_REQUEST = 18;
+ this.GDK_SELECTION_NOTIFY = 19;
+ this.GDK_PROXIMITY_IN = 20;
+ this.GDK_PROXIMITY_OUT = 21;
+ this.GDK_DRAG_ENTER = 22;
+ this.GDK_DRAG_LEAVE = 23;
+ this.GDK_DRAG_MOTION = 24;
+ this.GDK_DRAG_STATUS = 25;
+ this.GDK_DROP_START = 26;
+ this.GDK_DROP_FINISHED = 27;
+ this.GDK_CLIENT_EVENT = 28;
+ this.GDK_VISIBILITY_NOTIFY = 29;
+ this.GDK_NO_EXPOSE = 30;
+ this.GDK_SCROLL = 31;
+ this.GDK_WINDOW_STATE = 32;
+ this.GDK_SETTING = 33;
+ this.GDK_OWNER_CHANGE = 34;
+ this.GDK_GRAB_BROKEN = 35;
+ this.GDK_DAMAGE = 36;
+ this.GDK_EVENT_LAST = 37; /* helper variable for decls */
+ this.GdkPropMode = ctypes.int; // enum
+ this.GDK_PROP_MODE_REPLACE = 0;
+ this.GDK_PROP_MODE_PREPEN = 1;
+ this.GDK_PROP_MODE_APPEND = 2;
+ this.GdkScrollDirection = ctypes.int; // enum
+ this.GDK_SCROLL_UP = 0;
+ this.GDK_SCROLL_DOWN = 1;
+ this.GDK_SCROLL_LEFT = 2;
+ this.GDK_SCROLL_RIGHT = 3;
+ this.GdkEventMask = ctypes.int; // enum
+ this.GDK_EXPOSURE_MASK = 1 << 1,
+ this.GDK_POINTER_MOTION_MASK = 1 << 2,
+ this.GDK_POINTER_MOTION_HINT_MASK = 1 << 3,
+ this.GDK_BUTTON_MOTION_MASK = 1 << 4,
+ this.GDK_BUTTON1_MOTION_MASK = 1 << 5,
+ this.GDK_BUTTON2_MOTION_MASK = 1 << 6,
+ this.GDK_BUTTON3_MOTION_MASK = 1 << 7,
+ this.GDK_BUTTON_PRESS_MASK = 1 << 8,
+ this.GDK_BUTTON_RELEASE_MASK = 1 << 9,
+ this.GDK_KEY_PRESS_MASK = 1 << 10,
+ this.GDK_KEY_RELEASE_MASK = 1 << 11,
+ this.GDK_ENTER_NOTIFY_MASK = 1 << 12,
+ this.GDK_LEAVE_NOTIFY_MASK = 1 << 13,
+ this.GDK_FOCUS_CHANGE_MASK = 1 << 14,
+ this.GDK_STRUCTURE_MASK = 1 << 15,
+ this.GDK_PROPERTY_CHANGE_MASK = 1 << 16,
+ this.GDK_VISIBILITY_NOTIFY_MASK = 1 << 17,
+ this.GDK_PROXIMITY_IN_MASK = 1 << 18,
+ this.GDK_PROXIMITY_OUT_MASK = 1 << 19,
+ 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
+ this.GdkVisualType = ctypes.int; // enum
+ this.GdkVisual = ctypes.StructType("GdkVisual", [
+ { "parent_instance": gobject.GObject },
+ { "type": this.GdkVisualType },
+ { "depth": gobject.gint },
+ { "byte": this.GdkByteOrder },
+ { "colormap": gobject.gint },
+ { "bits": gobject.gint },
+ { "red_mask": gobject.guint32 },
+ { "red_shift": gobject.gint },
+ { "red_prec": gobject.gint },
+ { "green_mask": gobject.guint32 },
+ { "green_shift": gobject.gint },
+ { "green_prec": gobject.gint },
+ { "blue_mask": gobject.guint32 },
+ { "blue_shift": gobject.gint },
+ { "blue_prec": gobject.gint }
+ ]);
+ this.GdkColor = ctypes.StructType("GdkColor", [
+ { "pixel": gobject.guint32 },
+ { "red": gobject.guint16 },
+ { "green": gobject.guint16 },
+ { "blue": gobject.guint16 }
+ ]);
+ this.GdkColormap = ctypes.StructType("GdkColormap", [
+ { "size": gobject.gint },
+ { "colors": this.GdkColor.ptr }
+ ]);
+ this.GdkWindowType = ctypes.StructType("GdkWindowType");
+ this.GdkCursor = ctypes.StructType("GdkCursor");
+ this.GdkWindowTypeHint = ctypes.StructType("GdkWindowTypeHint");
+ this.GdkWindowClass = ctypes.StructType("GdkWindowClass");
+ this.GdkWindowAttributes = ctypes.StructType("GdkWindowAttributes", [
+ { "title": gobject.gchar },
+ { "event_mask": gobject.gint },
+ { "x": gobject.gint },
+ { "y": gobject.gint },
+ { "width": gobject.gint },
+ { "height": gobject.gint },
+ { "wclass": gobject.gint },
+ { "visual": this.GdkVisual.ptr },
+ { "colormap": this.GdkColormap.ptr },
+ { "window_type": gobject.gint },
+ { "cursor": this.GdkCursor.ptr },
+ { "wmclass_name": gobject.gchar },
+ { "wmclass_class": gobject.gchar },
+ { "override_redirect": gobject.gboolean },
+ { "type_hint": gobject.gint }
+ ]);
+ this.GdkPixbuf = ctypes.StructType("GdkPixbuf");
+ this.GdkScreen = ctypes.StructType("GdkScreen");
+ this.GdkPixmap = ctypes.StructType("GdkPixmap");
+ this.GdkDrawable = ctypes.StructType("GdkDrawable");
+ this.GdkGC = ctypes.StructType("GdkGC");
+ this.GdkXEvent = ctypes.void_t; // will probably be cast to XEvent
+ this.GdkEvent = ctypes.void_t;
+ this.GdkDisplay = ctypes.StructType("GdkDisplay");
+ this.GdkFilterFunc = ctypes.voidptr_t;
+ this.GdkEventWindowState = ctypes.StructType("GdkEventWindowState", [
+ { "type": this.GdkEventType },
+ { "window": this.GdkWindow.ptr },
+ { "send_event": gobject.gint8 },
+ { "changed_mask": this.GdkWindowState },
+ { "new_window_state": this.GdkWindowState },
+ ]);
+ this.GdkDevice = ctypes.StructType("GdkDevice");
+ this.GdkEventScroll = ctypes.StructType("GdkEventScroll", [
+ { "type": this.GdkEventType },
+ { "window": this.GdkWindow.ptr },
+ { "send_event": gobject.gint8 },
+ { "time": gobject.guint32 },
+ { "x": gobject.gdouble },
+ { "y": gobject.gdouble },
+ { "state": gobject.guint },
+ { "direction": this.GdkScrollDirection },
+ { "device": this.GdkDevice.ptr },
+ { "x_root": gobject.gdouble },
+ { "y_root": gobject.gdouble }
+ ]);
+ this.GdkAtom = ctypes.StructType("GdkAtom");
+ this.GdkEventButton = ctypes.StructType("GdkEventButton", [
+ { "type": this.GdkEventType },
+ { "window": this.GdkWindow.ptr },
+ { "send_event": gobject.gint8 },
+ { "time": gobject.guint32 },
+ { "x": gobject.gdouble },
+ { "y": gobject.gdouble },
+ { "axes": gobject.gdouble.ptr },
+ { "state": gobject.guint },
+ { "button": gobject.guint },
+ { "device": this.GdkDevice.ptr },
+ { "x_root": gobject.gdouble },
+ { "y_root": gobject.gdouble }
+ ]);
+ this.GdkEventFocus = ctypes.StructType("GdkEventFocus", [
+ { "type": this.GdkEventType },
+ { "window": this.GdkWindow.ptr },
+ { "send_event": gobject.gint8 },
+ { "in": gobject.gint16 },
+ ]);
+
+ this.GdkFilterFunc_t = ctypes.FunctionType(
+ ctypes.default_abi, this.GdkFilterReturn,
+ [this.GdkXEvent.ptr, this.GdkEvent.ptr, gobject.gpointer]).ptr;
+
+ lib.lazy_bind("gdk_flush", ctypes.void_t);
+ lib.lazy_bind("gdk_error_trap_push", ctypes.void_t);
+ lib.lazy_bind("gdk_error_trap_pop", gobject.gint);
+
+ lib.lazy_bind("gdk_x11_drawable_get_xid", x11.XID, this.GdkDrawable.ptr);
+ lib.lazy_bind("gdk_window_new", this.GdkWindow.ptr, this.GdkWindow.ptr, this.GdkWindowAttributes.ptr, gobject.gint);
+ lib.lazy_bind("gdk_window_destroy", ctypes.void_t, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_x11_window_set_user_time", ctypes.void_t, this.GdkWindow.ptr, gobject.guint32);
+ lib.lazy_bind("gdk_window_hide", ctypes.void_t, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_show_unraised", ctypes.void_t, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_screen_get_default", this.GdkScreen.ptr);
+ lib.lazy_bind("gdk_screen_get_toplevel_windows", gobject.GList.ptr, this.GdkScreen.ptr);
+ lib.lazy_bind("gdk_screen_get_number", gobject.gint, this.GdkScreen.ptr);
+ lib.lazy_bind("gdk_screen_get_display", this.GdkDisplay.ptr, this.GdkScreen.ptr);
+ lib.lazy_bind("gdk_x11_get_xatom_by_name_for_display", x11.Atom, this.GdkDisplay.ptr, gobject.gchar.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_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);
+ lib.lazy_bind("gdk_colormap_alloc_color", gobject.gboolean, this.GdkColormap.ptr, this.GdkColor.ptr, gobject.gboolean, gobject.gboolean);
+ lib.lazy_bind("gdk_pixmap_new", this.GdkPixmap.ptr, this.GdkDrawable.ptr, gobject.gint, gobject.gint, gobject.gint);
+
+ // DEPRECATED
+ // lib.lazy_bind("gdk_gc_new", this.GdkGC.ptr, this.GdkDrawable.ptr);
+ // lib.lazy_bind("gdk_gc_set_foreground", ctypes.void_t, this.GdkGC.ptr, this.GdkColor.ptr);
+ // lib.lazy_bind("gdk_draw_rectangle", ctypes.void_t, this.GdkDrawable.ptr, this.GdkGC.ptr, gobject.gboolean, gobject.gint, gobject.gint, gobject.gint, gobject.gint);
+
+ lib.lazy_bind("gdk_cairo_create", cairo.cairo_t.ptr, this.GdkDrawable.ptr);
+ lib.lazy_bind("gdk_cairo_set_source_color", ctypes.void_t, cairo.cairo_t.ptr, this.GdkColor.ptr);
+ lib.lazy_bind("gdk_pixbuf_get_from_drawable", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr, this.GdkDrawable.ptr, this.GdkColormap.ptr, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.int);
+ 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_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, this.GdkInterpType, ctypes.int);
+
+ lib.lazy_bind("gdk_window_stick", ctypes.void_t, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_iconify", ctypes.void_t, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_deiconify", ctypes.void_t, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_set_title", ctypes.void_t, this.GdkWindow.ptr, gobject.gchar.ptr);
+ lib.lazy_bind("gdk_window_beep", ctypes.void_t, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_get_width", ctypes.int, this.GdkWindow.ptr);
+
+ lib.lazy_bind("gdk_window_get_events", this.GdkEventMask, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_set_events", ctypes.void_t, this.GdkWindow.ptr, this.GdkEventMask);
+ lib.lazy_bind("gdk_window_add_filter", ctypes.void_t, this.GdkWindow.ptr, this.GdkFilterFunc, gobject.gpointer);
+ lib.lazy_bind("gdk_window_remove_filter", ctypes.void_t, this.GdkWindow.ptr, this.GdkFilterFunc, gobject.gpointer);
+ lib.lazy_bind("gdk_display_get_default", this.GdkDisplay.ptr);
+ lib.lazy_bind("gdk_x11_display_get_xdisplay", x11.Display.ptr, this.GdkDisplay.ptr);
+ lib.lazy_bind("gdk_window_get_state", this.GdkWindowState, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_get_position", ctypes.void_t, this.GdkWindow.ptr, gobject.gint.ptr, gobject.gint.ptr);
+ lib.lazy_bind("gdk_drawable_get_size", ctypes.void_t, this.GdkDrawable.ptr, gobject.gint.ptr, gobject.gint.ptr);
+ // lib.lazy_bind("gdk_window_get_geometry", ctypes.void_t, this.GdkWindow.ptr, gobject.gint.ptr, gobject.gint.ptr, gobject.gint.ptr, gobject.gint.ptr, gobject.gint.ptr);
+ lib.lazy_bind("gdk_window_move_resize", ctypes.void_t, this.GdkWindow.ptr, gobject.gint, gobject.gint, gobject.gint, gobject.gint);
+ lib.lazy_bind("gdk_window_get_user_data", ctypes.void_t, this.GdkWindow.ptr, gobject.gpointer.ptr);
+ lib.lazy_bind("gdk_atom_intern", this.GdkAtom, gobject.gchar.ptr, gobject.gboolean);
+ lib.lazy_bind("gdk_property_change", ctypes.void_t, this.GdkWindow.ptr, this.GdkAtom, this.GdkAtom, gobject.gint, this.GdkPropMode, gobject.guchar.ptr, gobject.gint);
+ lib.lazy_bind("gdk_window_get_toplevel", this.GdkWindow.ptr, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_window_get_effective_toplevel", this.GdkWindow.ptr, this.GdkWindow.ptr);
+ lib.lazy_bind("gdk_screen_get_active_window", this.GdkWindow.ptr, this.GdkScreen.ptr);
+
+ lib.lazy_bind("gdk_display_get_n_screens", gobject.gint, this.GdkDisplay.ptr);
+ lib.lazy_bind("gdk_display_get_screen", this.GdkScreen.ptr, this.GdkDisplay.ptr, gobject.gint);
+}
+
+new ctypes_library(GDK_LIBNAME, GDK_ABIS, gdk_defines, this);
diff --git a/src/modules/ctypes/freebsd/gio.jsm b/src/modules/ctypes/freebsd/gio.jsm
new file mode 100644
index 0000000..ccd7e2d
--- /dev/null
+++ b/src/modules/ctypes/freebsd/gio.jsm
@@ -0,0 +1,46 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "gio" ];
+
+const GIO_LIBNAME = "gio-2.0";
+const GIO_ABIS = [ "0" ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+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/glib.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+
+function gio_defines(lib) {
+ this.GIcon = ctypes.StructType("GIcon");
+ this.GThemedIcon = ctypes.StructType("GThemedIcon");
+
+ 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);
+
+ this.GBusType = ctypes.int; // enum
+ this.G_BUS_TYPE_STARTER = -1;
+ this.G_BUS_TYPE_NONE = 0;
+ this.G_BUS_TYPE_SYSTEM = 1;
+ this.G_BUS_TYPE_SESSION = 2;
+ this.GDBusProxyFlags = ctypes.int; // enum
+ this.G_DBUS_PROXY_FLAGS_NONE = 0;
+ this.G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0);
+ this.G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1);
+ this.G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2);
+
+ this.GDBusConnection = ctypes.StructType("GDBusConnection");
+ this.GCancellable = ctypes.StructType("GCancellable");
+ this.GDBusProxy = ctypes.StructType("GDBusProxy");
+ this.GDBusInterfaceInfo = ctypes.StructType("GDBusInterfaceInfo");
+
+ lib.lazy_bind("g_bus_get_sync", this.GDBusConnection.ptr, this.GBusType, this.GCancellable.ptr, glib.GError.ptr.ptr);
+ lib.lazy_bind("g_dbus_proxy_new_for_bus_sync", this.GDBusProxy.ptr, this.GBusType, this.GDBusProxyFlags, this.GDBusInterfaceInfo.ptr, gobject.gchar.ptr, gobject.gchar.ptr, gobject.gchar.ptr, this.GCancellable.ptr, glib.GError.ptr.ptr);
+ lib.lazy_bind("g_dbus_proxy_get_name_owner", gobject.gchar.ptr, this.GDBusProxy.ptr);
+}
+
+new ctypes_library(GIO_LIBNAME, GIO_ABIS, gio_defines, this);
diff --git a/src/modules/ctypes/freebsd/glib.jsm b/src/modules/ctypes/freebsd/glib.jsm
new file mode 100644
index 0000000..963bd5b
--- /dev/null
+++ b/src/modules/ctypes/freebsd/glib.jsm
@@ -0,0 +1,27 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "glib" ];
+
+const GLIB_LIBNAME = "glib-2.0";
+const GLIB_ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
+
+function glib_defines(lib) {
+ /* mutual inclusion not possible */
+ this.GQuark = ctypes.uint32_t; // this.GQuark = gobject.guint32;
+ this.GError = ctypes.StructType("GError", [
+ { domain: this.GQuark },
+ { code: ctypes.int }, // gint
+ { message: ctypes.char.ptr } // gchar.ptr
+ ]);
+ lib.lazy_bind("g_error_free", ctypes.void_t, this.GError.ptr);
+ lib.lazy_bind("g_strfreev", ctypes.void_t, ctypes.char.ptr.ptr);
+};
+
+new ctypes_library(GLIB_LIBNAME, GLIB_ABIS, glib_defines, this);
diff --git a/src/modules/ctypes/freebsd/gobject.jsm b/src/modules/ctypes/freebsd/gobject.jsm
new file mode 100644
index 0000000..cf95946
--- /dev/null
+++ b/src/modules/ctypes/freebsd/gobject.jsm
@@ -0,0 +1,140 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Firetray
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Messaging, Ltd.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Mike Conley <mconley at mozillamessaging.com>
+ * Foudil Brétel <foudil.newbie+amo at gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+var EXPORTED_SYMBOLS = [ "gobject", "glib" ];
+
+const GOBJECT_LIBNAME = "gobject-2.0";
+const GOBJECT_ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+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/glib.jsm");
+
+function gobject_defines(lib) {
+
+ this.GSignalMatchType = ctypes.int; // enum
+ this.G_SIGNAL_MATCH_ID = 1 << 0;
+ this.G_SIGNAL_MATCH_DETAIL = 1 << 1;
+ this.G_SIGNAL_MATCH_CLOSURE = 1 << 2;
+ this.G_SIGNAL_MATCH_FUNC = 1 << 3;
+ this.G_SIGNAL_MATCH_DATA = 1 << 4;
+ this.G_SIGNAL_MATCH_UNBLOCKED = 1 << 5;
+ this.gpointer = ctypes.voidptr_t;
+ this.gulong = ctypes.unsigned_long;
+ this.guint = ctypes.unsigned_int;
+ this.guint32 = ctypes.uint32_t;
+ this.guint16 = ctypes.uint16_t;
+ this.gint = ctypes.int;
+ this.gint8 = ctypes.int8_t;
+ this.gint16 = ctypes.int16_t;
+ this.gchar = ctypes.char;
+ this.guchar = ctypes.unsigned_char;
+ this.gboolean = this.gint;
+ this.FALSE = this.gboolean(0);
+ this.gfloat = ctypes.float;
+ this.gdouble = ctypes.double;
+ this.gsize = ctypes.unsigned_long;
+ this.GCallback = ctypes.voidptr_t;
+ this.GClosureNotify = this.gpointer;
+ this.GFunc = ctypes.void_t.ptr;
+ this.GList = ctypes.StructType("GList");
+ this.GConnectFlags = this.guint; // enum
+ this.G_CONNECT_AFTER = 1 << 0;
+ this.G_CONNECT_SWAPPED = 1 << 1;
+
+ this.GType = this.gsize;
+ this.GData = ctypes.StructType("GData");
+ this._GTypeClass = ctypes.StructType("_GTypeClass", [
+ {g_type: this.GType}]);
+ this._GTypeInstance = ctypes.StructType("_GTypeInstance", [
+ {g_class: this._GTypeClass.ptr}]);
+ /* "All the fields in the GObject structure are private to the GObject
+ * implementation and should never be accessed directly." but we need to tell
+ * something about it to access GdkVisual fields */
+ this.GObject = ctypes.StructType("GObject", [
+ { g_type_instance: this._GTypeInstance },
+ { ref_count: this.guint },
+ { qdata: this.GData.ptr },
+ ]);
+ this.GClosure = ctypes.StructType("GClosure", [
+ { in_marshal: this.guint },
+ { is_invalid: this.guint },
+ ]);
+
+ /* NOTE: if we needed more/different args, we'd need to implement another
+ FunctionType */
+ this.GCallback_t = ctypes.FunctionType(
+ ctypes.default_abi, ctypes.void_t, [this.gpointer]).ptr;
+ // intended for g_list_foreach.
+ this.GFunc_t = ctypes.FunctionType(
+ ctypes.default_abi, ctypes.void_t, [this.gpointer, this.gpointer]).ptr;
+
+ lib.lazy_bind("g_object_unref", ctypes.void_t, this.gpointer);
+ lib.lazy_bind("g_signal_connect_data", this.gulong, this.gpointer, this.gchar.ptr, this.GCallback, this.gpointer, this.GClosureNotify, this.GConnectFlags);
+
+ this.g_signal_connect = function(instance, detailed_signal, handler, data) {
+ return this.g_signal_connect_data(instance, detailed_signal, handler, data, null, 0);
+ };
+ this.g_signal_connect_after = function(instance, detailed_signal, handler, data) {
+ return this.g_signal_connect_data(instance, detailed_signal, handler, data, null, this.G_CONNECT_AFTER);
+ };
+
+ lib.lazy_bind("g_free", ctypes.void_t, this.gpointer);
+ lib.lazy_bind("g_object_unref", ctypes.void_t, this.gpointer);
+ lib.lazy_bind("g_list_free", ctypes.void_t, this.GList.ptr);
+ lib.lazy_bind("g_list_length", this.guint, this.GList.ptr);
+ lib.lazy_bind("g_list_foreach", ctypes.void_t, this.GList.ptr, this.GFunc, this.gpointer);
+ lib.lazy_bind("g_signal_lookup", this.guint, this.gchar.ptr, this.GType);
+ lib.lazy_bind("g_signal_handler_find", this.gulong, this.gpointer, this.GSignalMatchType, this.guint, glib.GQuark, this.GClosure.ptr, this.gpointer, this.gpointer);
+ lib.lazy_bind("g_signal_handler_disconnect", ctypes.void_t, this.gpointer, this.gulong);
+ lib.lazy_bind("g_signal_handler_block", ctypes.void_t, this.gpointer, this.gulong);
+ lib.lazy_bind("g_signal_handler_unblock", ctypes.void_t, this.gpointer, this.gulong);
+
+ /* NOTE: we can't easily work with g_object_get_property() because it uses
+ GValue, which is an opaque struct, and thus can't be initialized by ctypes */
+ this.GValue = ctypes.StructType("GValue");
+ lib.lazy_bind("g_object_get_property", ctypes.void_t, this.GObject.ptr, this.gchar.ptr, this.GValue.ptr);
+ lib.lazy_bind("g_object_get", ctypes.void_t, this.gpointer, this.gchar.ptr, "...");
+}
+
+new ctypes_library(GOBJECT_LIBNAME, GOBJECT_ABIS, gobject_defines, this);
diff --git a/src/modules/ctypes/freebsd/gtk.jsm b/src/modules/ctypes/freebsd/gtk.jsm
new file mode 100644
index 0000000..267aabb
--- /dev/null
+++ b/src/modules/ctypes/freebsd/gtk.jsm
@@ -0,0 +1,174 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "gtk" ];
+
+const GTK_LIBNAME = "gtk-x11-2.0";
+const GTK_ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+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/gdk.jsm");
+Cu.import("resource://firetray/ctypes/linux/gio.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/pango.jsm");
+
+function gtk_defines(lib) {
+ this.FIRETRAY_REQUIRED_GTK_MAJOR_VERSION = 2;
+ this.FIRETRAY_REQUIRED_GTK_MINOR_VERSION = 20;
+ this.FIRETRAY_REQUIRED_GTK_MICRO_VERSION = 0;
+
+ 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");
+ this.GtkStyle = ctypes.StructType("GtkStyle");
+ this.GtkRequisition = ctypes.StructType("GtkRequisition", [
+ { width: gobject.gint },
+ { height: gobject.gint }
+ ]);
+ this.GtkAllocation = ctypes.StructType("GtkAllocation", [
+ { x: gobject.gint },
+ { y: gobject.gint },
+ { width: gobject.gint },
+ { height: gobject.gint }
+ ]);
+ /* NOTE: recursive struct needs define() and included structs MUST be
+ * defined ! */
+ this.GtkWidget = ctypes.StructType("GtkWidget");
+ this.GtkWidget.define([
+ { "style": this.GtkStyle.ptr },
+ { "requisition": this.GtkRequisition },
+ { "allocation": this.GtkAllocation },
+ { "window": gdk.GdkWindow.ptr },
+ { "parent": this.GtkWidget.ptr }
+ ]);
+
+ this.GtkIconTheme = ctypes.StructType("GtkIconTheme");
+ this.GtkMenu = ctypes.StructType("GtkMenu");
+ // use ctypes.cast(menu, LibGtkStatusIcon.GtkMenuShell.ptr);
+ this.GtkMenuShell = ctypes.StructType("GtkMenuShell");
+ this.GtkMenuItem = ctypes.StructType("GtkMenuItem");
+ this.GtkImageMenuItem = ctypes.StructType("GtkImageMenuItem");
+ 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,
+ [this.GtkMenu.ptr, gobject.gint.ptr, gobject.gint.ptr,
+ gobject.gboolean.ptr, gobject.gpointer]).ptr;
+ this.GCallbackStatusIconActivate_t = ctypes.FunctionType(
+ ctypes.default_abi, gobject.gboolean,
+ [this.GtkStatusIcon.ptr, gobject.gpointer]).ptr;
+ this.GCallbackMenuPopup_t = ctypes.FunctionType(
+ ctypes.default_abi, ctypes.void_t,
+ [this.GtkStatusIcon.ptr, gobject.guint, gobject.guint,
+ gobject.gpointer]).ptr;
+ this.GCallbackOnScroll_t = ctypes.FunctionType(
+ ctypes.default_abi, gobject.gboolean,
+ [this.GtkStatusIcon.ptr, gdk.GdkEvent.ptr, gobject.gpointer]).ptr;
+ this.GCallbackStatusIconMiddleClick_t = this.GCallbackOnScroll_t;
+ this.GCallbackGenericEvent_t = ctypes.FunctionType(
+ ctypes.default_abi, gobject.gboolean,
+ [this.GtkWidget.ptr, gdk.GdkEvent.ptr, gobject.gpointer]).ptr;
+ this.GCallbackWindowStateEvent_t = ctypes.FunctionType(
+ ctypes.default_abi, gobject.gboolean,
+ [this.GtkWidget.ptr, gdk.GdkEventWindowState.ptr, gobject.gpointer]).ptr;
+ this.GCallbackWidgetFocusEvent_t = ctypes.FunctionType(
+ ctypes.default_abi, gobject.gboolean,
+ [this.GtkWidget.ptr, gdk.GdkEventFocus.ptr, gobject.gpointer]).ptr;
+
+ lib.lazy_bind("gtk_check_version", gobject.gchar.ptr, gobject.guint, gobject.guint, gobject.guint);
+
+ lib.lazy_bind("gtk_icon_theme_get_default", this.GtkIconTheme.ptr);
+ lib.lazy_bind("gtk_icon_theme_get_for_screen", this.GtkIconTheme.ptr, gdk.GdkScreen.ptr);
+ lib.lazy_bind("gtk_icon_theme_get_search_path", ctypes.void_t, this.GtkIconTheme.ptr, gobject.gchar.ptr.ptr.array(), gobject.gint.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);
+ lib.lazy_bind("gtk_status_icon_set_from_icon_name", ctypes.void_t, this.GtkStatusIcon.ptr, gobject.gchar.ptr);
+ lib.lazy_bind("gtk_status_icon_set_from_gicon", ctypes.void_t, this.GtkStatusIcon.ptr, gio.GIcon.ptr);
+ lib.lazy_bind("gtk_status_icon_set_tooltip_text", ctypes.void_t, this.GtkStatusIcon.ptr, ctypes.char.ptr);
+ lib.lazy_bind("gtk_status_icon_set_blinking", ctypes.void_t, this.GtkStatusIcon.ptr, gobject.gboolean); // deprecated in gtk3
+ lib.lazy_bind("gtk_status_icon_set_visible", ctypes.void_t, this.GtkStatusIcon.ptr, gobject.gboolean);
+ lib.lazy_bind("gtk_menu_new", this.GtkMenu.ptr);
+ lib.lazy_bind("gtk_menu_item_set_label", ctypes.void_t, this.GtkMenuItem.ptr, gobject.gchar.ptr);
+ lib.lazy_bind("gtk_image_menu_item_new", this.GtkImageMenuItem.ptr);
+ lib.lazy_bind("gtk_image_menu_item_new_with_label", this.GtkImageMenuItem.ptr, gobject.gchar.ptr);
+ lib.lazy_bind("gtk_image_new_from_stock", this.GtkWidget.ptr, gobject.gchar.ptr, ctypes.int); // enum
+ lib.lazy_bind("gtk_image_menu_item_set_image", ctypes.void_t, this.GtkImageMenuItem.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_menu_shell_append", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_menu_shell_prepend", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_menu_shell_insert", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr, gobject.gint);
+ lib.lazy_bind("gtk_menu_popup", ctypes.void_t, this.GtkMenu.ptr, this.GtkWidget.ptr, this.GtkWidget.ptr, this.GtkMenuPositionFunc_t, gobject.gpointer, gobject.guint, gobject.guint);
+ lib.lazy_bind("gtk_status_icon_position_menu", ctypes.void_t, this.GtkMenu.ptr, gobject.gint.ptr, gobject.gint.ptr, gobject.gboolean.ptr, gobject.gpointer);
+ lib.lazy_bind("gtk_separator_menu_item_new", this.GtkWidget.ptr);
+
+ lib.lazy_bind("gtk_window_new", this.GtkWidget.ptr, this.GtkWindowType);
+ 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);
+ lib.lazy_bind("gtk_window_has_toplevel_focus", gobject.gboolean, this.GtkWindow.ptr);
+ lib.lazy_bind("gtk_widget_get_has_window", gobject.gboolean, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_get_window", gdk.GdkWindow.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_get_parent_window", gdk.GdkWindow.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_window_set_decorated", ctypes.void_t, this.GtkWindow.ptr, gobject.gboolean);
+ lib.lazy_bind("gtk_window_set_urgency_hint", ctypes.void_t, this.GtkWindow.ptr, gobject.gboolean);
+
+ lib.lazy_bind("gtk_widget_is_focus", gobject.gboolean, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_has_focus", gobject.gboolean, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_get_visible", gobject.gboolean, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_hide_on_delete", gobject.gboolean, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_hide", ctypes.void_t, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_show", ctypes.void_t, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_show_all", ctypes.void_t, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_get_events", gobject.gint, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_get_events", gobject.gint, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_add_events", ctypes.void_t, this.GtkWidget.ptr, gobject.gint);
+ lib.lazy_bind("gtk_widget_get_toplevel", this.GtkWidget.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_set_sensitive", ctypes.void_t, this.GtkWidget.ptr, gobject.gboolean);
+ lib.lazy_bind("gtk_window_get_type", gobject.GType);
+ lib.lazy_bind("gtk_window_get_position", ctypes.void_t, this.GtkWindow.ptr, gobject.gint.ptr, gobject.gint.ptr);
+ lib.lazy_bind("gtk_window_move", ctypes.void_t, this.GtkWindow.ptr, gobject.gint, gobject.gint);
+ lib.lazy_bind("gtk_window_get_size", ctypes.void_t, this.GtkWindow.ptr, gobject.gint.ptr, gobject.gint.ptr);
+ lib.lazy_bind("gtk_window_resize", ctypes.void_t, this.GtkWindow.ptr, gobject.gint, gobject.gint);
+ lib.lazy_bind("gtk_window_iconify", ctypes.void_t, this.GtkWindow.ptr);
+ lib.lazy_bind("gtk_window_deiconify", ctypes.void_t, this.GtkWindow.ptr);
+ lib.lazy_bind("gtk_window_stick", ctypes.void_t, this.GtkWindow.ptr);
+ lib.lazy_bind("gtk_window_maximize", ctypes.void_t, this.GtkWindow.ptr);
+ lib.lazy_bind("gtk_window_fullscreen", ctypes.void_t, this.GtkWindow.ptr);
+ lib.lazy_bind("gtk_window_present", ctypes.void_t, this.GtkWindow.ptr);
+
+}
+
+new ctypes_library(GTK_LIBNAME, GTK_ABIS, gtk_defines, this);
diff --git a/src/modules/ctypes/freebsd/libc.jsm b/src/modules/ctypes/freebsd/libc.jsm
new file mode 100644
index 0000000..a50c37a
--- /dev/null
+++ b/src/modules/ctypes/freebsd/libc.jsm
@@ -0,0 +1,31 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "libc" ];
+
+const LIBC_LIBNAME = "c";
+const LIBC_ABIS = [ 6 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
+
+function libc_defines(lib) {
+ this.FILE = ctypes.StructType("FILE");
+ // this.stderr = this.fdopen(2, "a");
+ this.pid_t = ctypes.int;
+
+ lib.lazy_bind("fdopen", this.FILE.ptr, ctypes.int, ctypes.char.ptr);
+ lib.lazy_bind("puts", ctypes.int32_t, ctypes.char.ptr);
+ lib.lazy_bind("fputs", ctypes.int32_t, ctypes.char.ptr, this.FILE.ptr);
+ lib.lazy_bind("fflush", ctypes.int32_t, this.FILE.ptr);
+ lib.lazy_bind("getpid", this.pid_t);
+ lib.lazy_bind("strcmp", ctypes.int, ctypes.char.ptr, ctypes.char.ptr);
+ lib.lazy_bind("popen", this.FILE.ptr, ctypes.char.ptr, ctypes.char.ptr);
+ lib.lazy_bind("pclose", ctypes.int, this.FILE.ptr);
+ lib.lazy_bind("fread", ctypes.size_t, ctypes.voidptr_t, ctypes.size_t, ctypes.size_t, this.FILE.ptr);
+};
+
+var libc = new ctypes_library(LIBC_LIBNAME, LIBC_ABIS, libc_defines, this);
diff --git a/src/modules/ctypes/freebsd/pango.jsm b/src/modules/ctypes/freebsd/pango.jsm
new file mode 100644
index 0000000..a19a755
--- /dev/null
+++ b/src/modules/ctypes/freebsd/pango.jsm
@@ -0,0 +1,47 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "pango", "pangocairo" ];
+
+const PANGO_LIBNAME = "pango-1.0";
+const PANGO_ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+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/cairo.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+
+function pango_defines(lib) {
+ this.PANGO_WEIGHT_THIN = 100;
+ this.PANGO_WEIGHT_ULTRALIGHT = 200;
+ this.PANGO_WEIGHT_LIGHT = 300;
+ this.PANGO_WEIGHT_BOOK = 380;
+ this.PANGO_WEIGHT_NORMAL = 400;
+ this.PANGO_WEIGHT_MEDIUM = 500;
+ this.PANGO_WEIGHT_SEMIBOLD = 600;
+ this.PANGO_WEIGHT_BOLD = 700;
+ this.PANGO_WEIGHT_ULTRABOLD = 800;
+ this.PANGO_WEIGHT_HEAVY = 900;
+ this.PANGO_WEIGHT_ULTRAHEAVY = 1000;
+ this.PANGO_SCALE = 1024;
+
+ this.PangoFontDescription = ctypes.StructType("PangoFontDescription");
+ this.PangoLayout = ctypes.StructType("PangoLayout");
+ this.PangoWeight = ctypes.int; // enum
+
+ lib.lazy_bind("pango_font_description_from_string", this.PangoFontDescription.ptr, ctypes.char.ptr);
+ lib.lazy_bind("pango_font_description_set_weight", ctypes.void_t, this.PangoFontDescription.ptr, this.PangoWeight);
+ lib.lazy_bind("pango_layout_set_spacing", ctypes.void_t, this.PangoLayout.ptr, ctypes.int);
+ lib.lazy_bind("pango_layout_set_font_description", ctypes.void_t, this.PangoLayout.ptr, this.PangoFontDescription.ptr);
+ lib.lazy_bind("pango_layout_set_text", ctypes.void_t, this.PangoLayout.ptr, ctypes.char.ptr, ctypes.int);
+ lib.lazy_bind("pango_layout_get_pixel_size", ctypes.void_t, this.PangoLayout.ptr, ctypes.int.ptr, ctypes.int.ptr);
+ lib.lazy_bind("pango_font_description_get_size", gobject.gint, this.PangoFontDescription.ptr);
+ lib.lazy_bind("pango_font_description_set_size", ctypes.void_t, this.PangoFontDescription.ptr, gobject.gint);
+ lib.lazy_bind("pango_font_description_free", ctypes.void_t, this.PangoFontDescription.ptr);
+
+}
+
+new ctypes_library(PANGO_LIBNAME, PANGO_ABIS, pango_defines, this);
diff --git a/src/modules/ctypes/freebsd/pangocairo.jsm b/src/modules/ctypes/freebsd/pangocairo.jsm
new file mode 100644
index 0000000..e763951
--- /dev/null
+++ b/src/modules/ctypes/freebsd/pangocairo.jsm
@@ -0,0 +1,22 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "pangocairo" ];
+
+const PANGOCAIRO_LIBNAME = "pangocairo-1.0";
+const PANGOCAIRO_ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+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/cairo.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/pango.jsm");
+
+function pangocairo_defines(lib) {
+ lib.lazy_bind("pango_cairo_show_layout", ctypes.void_t, cairo.cairo_t.ptr, pango.PangoLayout.ptr);
+}
+
+new ctypes_library(PANGOCAIRO_LIBNAME, PANGOCAIRO_ABIS, pangocairo_defines, this);
diff --git a/src/modules/ctypes/freebsd/x11.jsm b/src/modules/ctypes/freebsd/x11.jsm
new file mode 100644
index 0000000..2895f45
--- /dev/null
+++ b/src/modules/ctypes/freebsd/x11.jsm
@@ -0,0 +1,255 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [
+ "x11",
+ "XATOMS", "XATOMS_ICCCM", "XATOMS_EWMH_GENERAL", "XATOMS_EWMH_WM_STATES",
+ "XPROP_MAX_COUNT", "XPROP_BASE_TYPE", "XPROP_BASE_TYPE_LONG_PROPORTION"
+];
+
+const X11_LIBNAME = "X11";
+const X11_ABIS = [ 6 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
+
+const XATOMS_ICCCM = [ "WM_DELETE_WINDOW", "WM_STATE", "WM_CHANGE_STATE" ];
+const XATOMS_EWMH_GENERAL = [ "_NET_CLOSE_WINDOW", "_NET_WM_NAME",
+ "_NET_WM_VISIBLE_NAME", "_NET_WM_ICON_NAME", "_NET_WM_VISIBLE_ICON_NAME",
+ "_NET_WM_DESKTOP", "_NET_WM_WINDOW_TYPE", "_NET_WM_STATE",
+ "_NET_WM_ALLOWED_ACTIONS", "_NET_WM_STRUT", "_NET_WM_STRUT_PARTIAL",
+ "_NET_WM_ICON_GEOMETRY", "_NET_WM_ICON", "_NET_WM_PID",
+ "_NET_WM_HANDLED_ICONS", "_NET_WM_USER_TIME", "_NET_FRAME_EXTENTS"
+];
+const XATOMS_EWMH_WM_STATES = [
+ "_NET_WM_STATE_MODAL", "_NET_WM_STATE_STICKY",
+ "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ",
+ "_NET_WM_STATE_SHADED", "_NET_WM_STATE_SKIP_TASKBAR",
+ "_NET_WM_STATE_SKIP_PAGER", "_NET_WM_STATE_HIDDEN",
+ "_NET_WM_STATE_FULLSCREEN", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_BELOW",
+ "_NET_WM_STATE_DEMANDS_ATTENTION"
+];
+const XATOMS_EWMH_ROOT = [ "_NET_ACTIVE_WINDOW" ]
+const XATOMS = XATOMS_ICCCM
+ .concat(XATOMS_EWMH_WM_STATES)
+ .concat(XATOMS_EWMH_GENERAL)
+ .concat(XATOMS_EWMH_ROOT)
+ .concat(["CARDINAL"]);
+
+
+function x11_defines(lib) {
+ /* fundamental types need to be guessed :-( */
+ // http://mxr.mozilla.org/mozilla-central/source/configure.in
+ if (/^(Alpha|hppa|ia64|ppc64|s390|x86_64)-/.test(Services.appinfo.XPCOMABI)) {
+ this.CARD32 = ctypes.unsigned_int;
+ this.Atom = ctypes.unsigned_long;
+ this.Window = ctypes.unsigned_long;
+ this.Time = ctypes.unsigned_long;
+ this.XID = ctypes.unsigned_long;
+ } else {
+ this.CARD32 = ctypes.unsigned_long;
+ this.Atom = this.CARD32;
+ this.Window = this.CARD32;
+ this.Time = this.CARD32;
+ this.XID = this.CARD32;
+ }
+
+ // X.h
+ this.Success = 0;
+ this.None = 0;
+ this.AnyPropertyType = 0;
+ this.BadValue = 2;
+ this.BadWindow = 3;
+ this.BadAtom = 5;
+ this.BadMatch = 8;
+ this.BadAlloc = 11;
+ this.PropertyNewValue = 0;
+ this.PropertyDelete = 1;
+ this.PropModeReplace = 0;
+ this.PropModePrepend = 1;
+ this.PropModeAppend = 2;
+ // Event names
+ this.KeyPress = 2;
+ this.KeyRelease = 3;
+ this.ButtonPress = 4;
+ this.ButtonRelease = 5;
+ this.MotionNotify = 6;
+ this.EnterNotify = 7;
+ this.LeaveNotify = 8;
+ this.FocusIn = 9;
+ this.FocusOut = 10;
+ this.KeymapNotify = 11;
+ this.Expose = 12;
+ this.GraphicsExpose = 13;
+ this.NoExpose = 14;
+ this.VisibilityNotify = 15;
+ this.CreateNotify = 16;
+ this.DestroyNotify = 17;
+ this.UnmapNotify = 18;
+ this.MapNotify = 19;
+ this.MapRequest = 20;
+ this.ReparentNotify = 21;
+ this.ConfigureNotify = 22;
+ this.ConfigureRequest = 23;
+ this.GravityNotify = 24;
+ this.ResizeRequest = 25;
+ this.CirculateNotify = 26;
+ this.CirculateRequest = 27;
+ this.PropertyNotify = 28;
+ this.SelectionClear = 29;
+ this.SelectionRequest = 30;
+ this.SelectionNotify = 31;
+ this.ColormapNotify = 32;
+ this.ClientMessage = 33;
+ this.MappingNotify = 34;
+ this.GenericEvent = 35;
+ this.LASTEvent = 36; /* must be bigger than any event # */
+ // Xutils.h: definitions for initial window state
+ this.WithdrawnState = 0; /* for windows that are not mapped */
+ this.NormalState = 1; /* most applications want to start this way */
+ this.IconicState = 3; /* application wants to start as an icon */
+ // Xatom
+ this.XA_ATOM = 4;
+ this.XA_CARDINAL = 6;
+ // Input Event Masks
+ this.VisibilityChangeMask = 1<<16
+ this.StructureNotifyMask = 1<<17
+ this.SubstructureNotifyMask = 1<<19;
+ this.SubstructureRedirectMask = 1<<20;
+ this.FocusChangeMask = 1<<21
+ this.PropertyChangeMask = 1<<22
+
+ this.Bool = ctypes.int;
+ this.Status = ctypes.int;
+ this.Pixmap = this.XID;
+ this.Cursor = this.XID;
+ this.Colormap = this.XID;
+ this.GContext = this.XID;
+ this.KeySym = this.XID;
+ this.Visual = ctypes.StructType("Visual");
+ this.Screen = ctypes.StructType("Screen");
+ this.Display = ctypes.StructType("Display");
+ // union not supported by js-ctypes
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=535378 "You can always
+ // typecast pointers, at least as long as you know which type is the biggest"
+ this.XEvent = ctypes.void_t; // union
+ this.XAnyEvent = ctypes.StructType("XAnyEvent", [
+ { "type": ctypes.int },
+ { "serial": ctypes.unsigned_long },
+ { "send_event": this.Bool },
+ { "display": this.Display.ptr },
+ { "window": this.Window }
+ ]);
+ this.XClientMessageEvent = ctypes.StructType("XClientMessageEvent", [
+ { "type": ctypes.int },
+ { "serial": ctypes.unsigned_long },
+ { "send_event": this.Bool },
+ { "display": this.Display.ptr },
+ { "window": this.Window },
+ { "message_type": this.Atom },
+ { "format": ctypes.int },
+ { "data": ctypes.long.array(5) } // actually a union char b[20]; short s[10]; long l[5];
+ ]);
+ this.XPropertyEvent = ctypes.StructType("XPropertyEvent", [
+ { "type": ctypes.int },
+ { "serial": ctypes.unsigned_long },
+ { "send_event": this.Bool },
+ { "display": this.Display.ptr },
+ { "window": this.Window },
+ { "atom": this.Atom },
+ { "time": this.Time },
+ { "state": ctypes.int } /* NewValue or Deleted */
+ ]);
+
+ this.XWindowAttributes = ctypes.StructType("XWindowAttributes", [
+ { "x": ctypes.int },
+ { "y": ctypes.int }, /* location of window */
+ { "width": ctypes.int },
+ { "height": ctypes.int }, /* width and height of window */
+ { "border_width": ctypes.int }, /* border width of window */
+ { "depth": ctypes.int }, /* depth of window */
+ { "visual": this.Visual.ptr }, /* the associated visual structure */
+ { "root": this.Window }, /* root of screen containing window */
+ { "class": ctypes.int }, /* InputOutput, InputOnly*/
+ { "bit_gravity": ctypes.int }, /* one of bit gravity values */
+ { "win_gravity": ctypes.int }, /* one of the window gravity values */
+ { "backing_store": ctypes.int }, /* NotUseful, WhenMapped, Always */
+ { "backing_planes": ctypes.unsigned_long }, /* planes to be preserved if possible */
+ { "backing_pixel": ctypes.unsigned_long }, /* value to be used when restoring planes */
+ { "save_under": this.Bool }, /* boolean, should bits under be saved? */
+ { "colormap": this.Colormap }, /* color map to be associated with window */
+ { "map_installed": this.Bool }, /* boolean, is color map currently installed*/
+ { "map_state": ctypes.int }, /* IsUnmapped, IsUnviewable, IsViewable */
+ { "all_event_masks": ctypes.long }, /* set of events all people have interest in*/
+ { "your_event_mask": ctypes.long }, /* my event mask */
+ { "do_not_propagate_mask": ctypes.long }, /* set of events that should not propagate */
+ { "override_redirect": this.Bool }, /* boolean value for override-redirect */
+ { "screen": this.Screen.ptr } /* back pointer to correct screen */
+ ]);
+
+ this.XSetWindowAttributes = ctypes.StructType("XSetWindowAttributes", [
+ { "background_pixmap": this.Pixmap }, /* background or None or ParentRelative */
+ { "background_pixel": ctypes.unsigned_long }, /* background pixel */
+ { "border_pixmap": this.Pixmap }, /* border of the window */
+ { "border_pixel": ctypes.unsigned_long }, /* border pixel value */
+ { "bit_gravity": ctypes.int }, /* one of bit gravity values */
+ { "win_gravity": ctypes.int }, /* one of the window gravity values */
+ { "backing_store": ctypes.int }, /* NotUseful, WhenMapped, Always */
+ { "backing_planes": ctypes.unsigned_long }, /* planes to be preseved if possible */
+ { "backing_pixel": ctypes.unsigned_long }, /* value to use in restoring planes */
+ { "save_under": this.Bool }, /* should bits under be saved? (popups) */
+ { "event_mask": ctypes.long }, /* set of events that should be saved */
+ { "do_not_propagate_mask": ctypes.long }, /* set of events that should not propagate */
+ { "override_redirect": this.Bool }, /* boolean value for override-redirect */
+ { "colormap": this.Colormap }, /* color map to be associated with window */
+ { "cursor": this.Cursor } /* cursor to be displayed (or None) */
+ ]);
+
+ lib.lazy_bind("XFree", ctypes.int, ctypes.void_t.ptr);
+ lib.lazy_bind("XInternAtom", this.Atom, this.Display.ptr, ctypes.char.ptr, this.Bool); // only_if_exsits
+ lib.lazy_bind("XGetWindowProperty", ctypes.int, this.Display.ptr, this.Window, this.Atom, ctypes.long, ctypes.long, this.Bool, this.Atom, this.Atom.ptr, ctypes.int.ptr, ctypes.unsigned_long.ptr, ctypes.unsigned_long.ptr, ctypes.unsigned_char.ptr.ptr);
+ lib.lazy_bind("XChangeProperty", ctypes.int, this.Display.ptr, this.Window, this.Atom, this.Atom, ctypes.int, ctypes.int, ctypes.unsigned_char.ptr, ctypes.int);
+ lib.lazy_bind("XDefaultRootWindow", this.Window, this.Display.ptr);
+ lib.lazy_bind("XSendEvent", this.Status, this.Display.ptr, this.Window, this.Bool, ctypes.long, this.XEvent.ptr);
+ lib.lazy_bind("XRaiseWindow", ctypes.int, this.Display.ptr, this.Window);
+ lib.lazy_bind("XGetWindowAttributes", this.Status, this.Display.ptr, this.Window, this.XWindowAttributes.ptr);
+ lib.lazy_bind("XChangeWindowAttributes", ctypes.int, this.Display.ptr, this.Window, ctypes.unsigned_long, this.XSetWindowAttributes.ptr);
+ lib.lazy_bind("XGetSelectionOwner", this.Window, this.Display.ptr, this.Atom);
+ lib.lazy_bind("XGetAtomName", ctypes.char.ptr, this.Display.ptr, this.Atom);
+ lib.lazy_bind("XOpenDisplay", this.Display.ptr, ctypes.char.ptr);
+ lib.lazy_bind("XCloseDisplay", ctypes.int, this.Display.ptr);
+}
+
+new ctypes_library(X11_LIBNAME, X11_ABIS, x11_defines, this);
+
+
+/* Xorg 1.10.4
+#if defined (_LP64) || \
+ defined(__alpha) || defined(__alpha__) || \
+ defined(__ia64__) || defined(ia64) || \
+ defined(__sparc64__) || \
+ defined(__s390x__) || \
+ (defined(__hppa__) && defined(__LP64__)) || \
+ defined(__amd64__) || defined(amd64) || \
+ defined(__powerpc64__) || \
+ (defined(sgi) && (_MIPS_SZLONG == 64))
+#define LONG64
+#endif
+
+# ifdef LONG64
+typedef unsigned long CARD64;
+typedef unsigned int CARD32;
+# else
+typedef unsigned long CARD32;
+# endif
+
+# ifndef _XSERVER64
+typedef unsigned long Atom;
+# else
+typedef CARD32 Atom;
+# endif
+*/
diff --git a/src/modules/freebsd b/src/modules/freebsd
deleted file mode 120000
index 9d646dc..0000000
--- a/src/modules/freebsd
+++ /dev/null
@@ -1 +0,0 @@
-./src/modules/linux
\ No newline at end of file
diff --git a/src/modules/freebsd/FiretrayAppIndicator.jsm b/src/modules/freebsd/FiretrayAppIndicator.jsm
new file mode 100644
index 0000000..b07311c
--- /dev/null
+++ b/src/modules/freebsd/FiretrayAppIndicator.jsm
@@ -0,0 +1,157 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+// FIXME: can't subscribeLibsForClosing([appind3])
+// https://bugs.launchpad.net/ubuntu/+source/firefox/+bug/1393256
+Cu.import("resource://firetray/ctypes/linux/appindicator.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([gobject, gtk]);
+
+let log = firetray.Logging.getLogger("firetray.AppIndicator");
+
+if ("undefined" == typeof(firetray.StatusIcon))
+ log.error("This module MUST be imported from/after FiretrayStatusIcon !");
+
+
+firetray.AppIndicator = {
+ initialized: false,
+ callbacks: {},
+ indicator: null,
+
+ init: function() {
+ this.indicator = appind3.app_indicator_new(
+ FIRETRAY_APPINDICATOR_ID,
+ firetray.StatusIcon.defaultAppIconName,
+ appind3.APP_INDICATOR_CATEGORY_COMMUNICATIONS
+ );
+ appind3.app_indicator_set_status(this.indicator,
+ appind3.APP_INDICATOR_STATUS_ACTIVE);
+ appind3.app_indicator_set_menu(this.indicator,
+ firetray.PopupMenu.menu); // mandatory
+ log.debug("indicator="+this.indicator);
+
+ this.addCallbacks();
+
+ for (let item in firetray.PopupMenu.menuItem) {
+ firetray.PopupMenu.showItem(firetray.PopupMenu.menuItem[item]);
+ }
+
+ this.attachMiddleClickCallback();
+ firetray.Handler.setIconTooltipDefault();
+
+ this.initialized = true;
+ return true;
+ },
+
+ shutdown: function() {
+ log.debug("Disabling AppIndicator");
+ gobject.g_object_unref(this.indicator);
+ this.initialized = false;
+ },
+
+ addCallbacks: function() {
+ this.callbacks.connChanged = appind3.ConnectionChangedCb_t(
+ firetray.AppIndicator.onConnectionChanged); // void return, no sentinel
+ gobject.g_signal_connect(this.indicator, "connection-changed",
+ firetray.AppIndicator.callbacks.connChanged, null);
+
+ this.callbacks.onScroll = appind3.OnScrollCb_t(
+ firetray.AppIndicator.onScroll); // void return, no sentinel
+ gobject.g_signal_connect(this.indicator, "scroll-event",
+ firetray.AppIndicator.callbacks.onScroll, null);
+ },
+
+ attachMiddleClickCallback: function(pref) {
+ let pref = firetray.Utils.prefService.getIntPref("middle_click");
+ if (pref === FIRETRAY_MIDDLE_CLICK_ACTIVATE_LAST) {
+ item = firetray.PopupMenu.menuItem.activateLast;
+ firetray.PopupMenu.showItem(firetray.PopupMenu.menuItem.activateLast);
+ } else if (pref === FIRETRAY_MIDDLE_CLICK_SHOW_HIDE) {
+ item = firetray.PopupMenu.menuItem.showHide;
+ firetray.PopupMenu.hideItem(firetray.PopupMenu.menuItem.activateLast);
+ } else {
+ log.error("Unknown pref value for 'middle_click': "+pref);
+ return false;
+ }
+ let menuItemShowHideWidget = ctypes.cast(item, gtk.GtkWidget.ptr);
+ appind3.app_indicator_set_secondary_activate_target(
+ this.indicator, menuItemShowHideWidget);
+ return true;
+ },
+
+ onConnectionChanged: function(indicator, connected, data) {
+ log.debug("AppIndicator connection-changed: "+connected);
+ },
+
+ // https://bugs.kde.org/show_bug.cgi?id=340978 broken under KDE4
+ onScroll: function(indicator, delta, direction, data) { // AppIndicator*, gint, GdkScrollDirection, gpointer
+ log.debug("onScroll: "+direction);
+ firetray.StatusIcon.onScroll(direction);
+ },
+
+}; // AppIndicator
+
+firetray.StatusIcon.initImpl =
+ firetray.AppIndicator.init.bind(firetray.AppIndicator);
+
+firetray.StatusIcon.shutdownImpl =
+ firetray.AppIndicator.shutdown.bind(firetray.AppIndicator);
+
+firetray.StatusIcon.middleClickActionChanged = function() {
+ log.debug("middleClickActionChanged");
+ firetray.AppIndicator.attachMiddleClickCallback();
+};
+
+
+firetray.Handler.setIconImageDefault = function() {
+ log.debug("setIconImageDefault");
+ appind3.app_indicator_set_icon(firetray.AppIndicator.indicator,
+ firetray.StatusIcon.defaultAppIconName);
+};
+
+firetray.Handler.setIconImageNewMail = function() {
+ log.debug("setIconImageNewMail");
+ appind3.app_indicator_set_icon(firetray.AppIndicator.indicator,
+ firetray.StatusIcon.defaultNewMailIconName);
+};
+
+firetray.Handler.setIconImageCustom = function(prefname) {
+ let prefCustomIconPath = firetray.Utils.prefService.getCharPref(prefname);
+ // Undocumented: ok to pass a *path* instead of an icon name! Otherwise we
+ // should be changing the default icons (which is maybe a better
+ // implementation anyway)...
+ appind3.app_indicator_set_icon(firetray.AppIndicator.indicator, prefCustomIconPath);
+};
+
+// No tooltips in AppIndicator
+// https://bugs.launchpad.net/indicator-application/+bug/527458
+firetray.Handler.setIconTooltip = function(toolTipStr) {
+ log.debug("setIconTooltip");
+ if (!firetray.AppIndicator.indicator)
+ return false;
+ firetray.PopupMenu.setItemLabel(firetray.PopupMenu.menuItem.tip,
+ toolTipStr);
+ return true;
+};
+
+// AppIndicator doesn't support pixbuf https://bugs.launchpad.net/bugs/812067
+firetray.Handler.setIconText = function(text, color) { };
+
+firetray.Handler.setIconVisibility = function(visible) {
+ if (!firetray.AppIndicator.indicator)
+ return false;
+
+ let status = visible ?
+ appind3.APP_INDICATOR_STATUS_ACTIVE :
+ appind3.APP_INDICATOR_STATUS_PASSIVE;
+ appind3.app_indicator_set_status(firetray.AppIndicator.indicator, status);
+ return true;
+};
diff --git a/src/modules/freebsd/FiretrayChat.jsm b/src/modules/freebsd/FiretrayChat.jsm
new file mode 100644
index 0000000..539500d
--- /dev/null
+++ b/src/modules/freebsd/FiretrayChat.jsm
@@ -0,0 +1,332 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource:///modules/imServices.jsm");
+Cu.import("resource://firetray/commons.js");
+Cu.import("resource://firetray/linux/FiretrayChatStatusIcon.jsm");
+Cu.import("resource://firetray/linux/FiretrayWindow.jsm");
+
+let log = firetray.Logging.getLogger("firetray.Chat");
+
+firetray.Chat = {
+ initialized: false,
+ observedTopics: {},
+ convsToAcknowledge: {
+ ids: {},
+ length: function(){return Object.keys(this.ids).length;}
+ },
+
+ init: function() {
+ if (this.initialized) {
+ log.warn("Chat already initialized");
+ return true;
+ }
+ log.debug("Enabling Chat");
+
+ firetray.Utils.addObservers(firetray.Chat, [
+ // "*", // debugging
+ "account-connected", "account-disconnected", "idle-time-changed",
+ "new-directed-incoming-message", "status-changed",
+ "unread-im-count-changed", "new-text"
+ ]);
+
+ firetray.ChatStatusIcon.init();
+ if (firetray.Utils.prefService.getBoolPref("chat_icon_blink") &&
+ firetray.Chat.convsToAcknowledge.length())
+ this.startGetAttention();
+ this.updateIcon();
+
+ this.initialized = true;
+ return true;
+ },
+
+ shutdown: function() {
+ if (!this.initialized) return false;
+ log.debug("Disabling Chat");
+
+ if (firetray.Chat.convsToAcknowledge.length())
+ this.stopGetAttention();
+
+ firetray.ChatStatusIcon.shutdown();
+ firetray.Utils.removeAllObservers(firetray.Chat);
+
+ this.initialized = false;
+ return true;
+ },
+
+ // FIXME: the listener should probably attached on the conv entry in the
+ // contactlist during startGetAttentionMaybe
+ attachSelectListeners: function(win) {
+ log.debug("attachSelectListeners");
+ ["contactlistbox", "tabmail"].forEach(function(eltId) {
+ win.document.getElementById(eltId)
+ .addEventListener('select', firetray.Chat.onSelect);
+ });
+ },
+
+ detachSelectListeners: function(win) {
+ ["contactlistbox", "tabmail"].forEach(function(eltId) {
+ win.document.getElementById(eltId)
+ .removeEventListener('select', firetray.Chat.onSelect);
+ });
+ },
+
+ observe: function(subject, topic, data) {
+ log.debug("RECEIVED Chat: "+topic+" subject="+subject+" data="+data);
+ let conv = null;
+
+ switch (topic) {
+ case "account-connected":
+ case "account-disconnected":
+ case "idle-time-changed":
+ case "status-changed":
+ this.updateIcon();
+ break;
+
+ case "new-directed-incoming-message": // when PM or cited in channel
+ conv = subject.QueryInterface(Ci.prplIMessage).conversation;
+ log.debug("conversation name="+conv.name); // normalizedName shouldn't be necessary
+ this.startGetAttentionMaybe(conv);
+ break;
+
+ /* Twitter is obviously considered a chatroom, not a private
+ conversation. This is why we need to detect incoming messages and switch
+ to the conversation differently. The actual read should be caught by
+ focus-in-event and 'select' event on tabmail and contactlist */
+ case "new-text":
+ let msg = subject.QueryInterface(Ci.prplIMessage);
+ conv = msg.conversation;
+ log.debug("new-text from "+conv.title);
+ let account = conv.account.QueryInterface(Ci.imIAccount);
+ let proto = account.protocol;
+
+ log.debug("msg from "+msg.who+", alias="+msg.alias+", account.normalizedName="+account.normalizedName);
+ if (msg.who === account.normalizedName) break; // ignore msg from self
+ if (proto.normalizedName !== 'twitter') break;
+ this.startGetAttentionMaybe(conv);
+ break;
+
+ case "unread-im-count-changed":
+ log.debug("unread-im-count-changed");
+ let unreadMsgCount = data;
+ if (unreadMsgCount == 0)
+ this.stopGetAttentionMaybe(firetray.Handler.getActiveWindow());
+
+ let localizedTooltip = PluralForm.get(
+ unreadMsgCount,
+ firetray.Utils.strings.GetStringFromName("tooltip.unread_messages"))
+ .replace("#1", unreadMsgCount);
+ firetray.ChatStatusIcon.setIconTooltip(localizedTooltip);
+ break;
+
+ default:
+ log.warn("unhandled topic: "+topic);
+ }
+ },
+
+ startGetAttentionMaybe: function(conv) {
+ log.debug('startGetAttentionMaybe conv.id='+conv.id);
+
+ let convIsCurrentlyShown =
+ this.isConvCurrentlyShown(conv, firetray.Handler.getActiveWindow());
+ log.debug("convIsCurrentlyShown="+convIsCurrentlyShown);
+ if (convIsCurrentlyShown) return; // don't blink when conv tab already on top
+
+ log.debug("firetray.ChatStatusIcon.isBlinking="+firetray.ChatStatusIcon.isBlinking);
+ if (firetray.Utils.prefService.getBoolPref("chat_icon_blink") &&
+ !firetray.ChatStatusIcon.isBlinking)
+ this.startGetAttention(conv);
+
+ this.convsToAcknowledge.ids[conv.id] = conv;
+ log.debug(conv.id+' added to convsToAcknowledge, length='+this.convsToAcknowledge.length());
+ },
+
+ startGetAttention: function(conv) {
+ log.debug("startGetAttention");
+ if (conv)
+ this.setUrgencyMaybe(conv);
+
+ let blinkStyle = firetray.Utils.prefService.getIntPref("chat_icon_blink_style");
+ log.debug("chat_icon_blink_style="+blinkStyle);
+ if (blinkStyle === FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL)
+ firetray.ChatStatusIcon.startBlinking();
+ else if (blinkStyle === FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE)
+ firetray.ChatStatusIcon.startFading();
+ else
+ throw new Error("Undefined chat icon blink style.");
+ },
+
+ /**
+ * @param xid id of the window that MUST have initiated this event
+ */
+ stopGetAttentionMaybe: function(xid) {
+ log.debug("stopGetAttentionMaybe");
+ log.debug("convsToAcknowledgeLength="+this.convsToAcknowledge.length());
+ if (!firetray.ChatStatusIcon.isBlinking) return;
+
+ let selectedConv = this.getSelectedConv(xid);
+ if (!selectedConv) return;
+
+ for (let convId in this.convsToAcknowledge.ids) {
+ log.debug(convId+" == "+selectedConv.id);
+ if (convId == selectedConv.id) {
+ delete this.convsToAcknowledge.ids[convId];
+ break;
+ }
+ }
+
+ // don't check chat_icon_blink: stopGetAttention even if it was unset
+ log.debug("convsToAcknowledge.length()="+this.convsToAcknowledge.length());
+ if (this.convsToAcknowledge.length() === 0)
+ this.stopGetAttention(xid);
+ },
+
+ stopGetAttention: function(xid) {
+ log.debug("do stop get attention !!!");
+ if (xid)
+ firetray.ChatStatusIcon.setUrgency(xid, false);
+
+ let blinkStyle = firetray.Utils.prefService.getIntPref("chat_icon_blink_style");
+ if (blinkStyle === FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL)
+ firetray.ChatStatusIcon.stopBlinking();
+ else if (blinkStyle === FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE)
+ firetray.ChatStatusIcon.stopFading();
+ else
+ throw new Error("Undefined chat icon blink style.");
+ },
+
+ onSelect: function(event) {
+ log.debug("select event ! ");
+ firetray.Chat.stopGetAttentionMaybe(firetray.Handler.getActiveWindow());
+ },
+
+ isConvCurrentlyShown: function(conv, activeWin) {
+ log.debug("isConvCurrentlyShown");
+ let selectedConv = this.getSelectedConv(activeWin);
+ if (!selectedConv) return false;
+
+ log.debug("conv.title='"+conv.title+"' selectedConv.title='"+selectedConv.title+"'");
+ return (conv.id == selectedConv.id);
+ },
+
+ getSelectedConv: function(activeWin) {
+ if (!firetray.Handler.windows[activeWin]) return null;
+ log.debug("getSelectedConv *");
+
+ let activeChatTab = this.findSelectedChatTab(activeWin);
+ if (!activeChatTab) return null;
+ log.debug("getSelectedConv **");
+
+ /* for now there is only one Chat tab, so we don't need to
+ findSelectedChatTabFromTab(activeChatTab.tabNode). And, as there is only
+ one forlderPaneBox, there will also probably be only one contactlistbox
+ for all Chat tabs anyway */
+ let selectedConv = this.findSelectedConv(activeWin);
+ if (!selectedConv) return null;
+ log.debug("getSelectedConv ***");
+
+ return selectedConv;
+ },
+
+ findSelectedChatTab: function(xid) {
+ let win = firetray.Handler.windows[xid].chromeWin;
+ let tabmail = win.document.getElementById("tabmail");
+ let chatTabs = tabmail.tabModes.chat.tabs;
+ for each (let tab in chatTabs)
+ if (tab.tabNode.selected) return tab;
+ return null;
+ },
+
+ findSelectedConv: function(xid) {
+ let win = firetray.Handler.windows[xid].chromeWin;
+ let selectedItem = win.document.getElementById("contactlistbox").selectedItem;
+ if (!selectedItem || selectedItem.localName != "imconv") return null;
+ return selectedItem.conv;
+ },
+
+ /* there can potentially be multiple windows, each with a Chat tab and the
+ same conv open... so we need to handle urgency for all windows */
+ setUrgencyMaybe: function(conv) {
+ 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;
+ /* item.conv is only initialized if chat tab is open */
+ if (item.hasOwnProperty('conv') && item.conv.target === conv) {
+ firetray.Window.setUrgency(xid, true);
+ break;
+ }
+ }
+ }
+ },
+
+ updateIcon: function() {
+ let globalConnectedStatus = this.globalConnectedStatus();
+ let userStatus;
+ if (globalConnectedStatus)
+ userStatus = Services.core.globalUserStatus.statusType;
+ else
+ userStatus = Ci.imIStatusInfo.STATUS_OFFLINE;
+ log.debug("IM status="+userStatus);
+
+ let iconName;
+ switch (userStatus) {
+ case Ci.imIStatusInfo.STATUS_OFFLINE: // 1
+ iconName = FIRETRAY_IM_STATUS_OFFLINE;
+ break;
+ case Ci.imIStatusInfo.STATUS_IDLE: // 4
+ case Ci.imIStatusInfo.STATUS_AWAY: // 5
+ iconName = FIRETRAY_IM_STATUS_AWAY;
+ break;
+ case Ci.imIStatusInfo.STATUS_AVAILABLE: // 7
+ iconName = FIRETRAY_IM_STATUS_AVAILABLE;
+ break;
+ case Ci.imIStatusInfo.STATUS_UNAVAILABLE: // 6
+ iconName = FIRETRAY_IM_STATUS_BUSY;
+ break;
+ case Ci.imIStatusInfo.STATUS_UNKNOWN: // 0
+ case Ci.imIStatusInfo.STATUS_INVISIBLE: // 2
+ case Ci.imIStatusInfo.STATUS_MOBILE: // 3
+ default: // ignore
+ }
+
+ log.debug("IM status changed="+iconName);
+ if (iconName)
+ firetray.ChatStatusIcon.setIconImage(iconName);
+ },
+
+ globalConnectedStatus: function() {
+ /* Because we may already be connected during init (for ex. when toggling
+ the chat_icon_enable pref), we need to updateIcon() during init(). But IM
+ accounts' list is not initialized at early stage... */
+ try {
+
+ let accounts = Services.accounts.getAccounts();
+ let globalConnected = false;
+
+ while (accounts.hasMoreElements()) {
+ let account = accounts.getNext().QueryInterface(Ci.imIAccount);
+ log.debug("account="+account+" STATUS="+account.statusInfo.statusType+" connected="+account.connected);
+ globalConnected = globalConnected || account.connected;
+ }
+ log.debug("globalConnected="+globalConnected);
+ return globalConnected;
+
+ } catch (e if e instanceof Components.Exception &&
+ e.result === Components.results.NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS &&
+ /_items is undefined/.test(e.message)) {
+ return false; // ignore
+ } catch(e) {
+ log.error(e); return false;
+ }
+ }
+
+};
diff --git a/src/modules/freebsd/FiretrayChatStatusIcon.jsm b/src/modules/freebsd/FiretrayChatStatusIcon.jsm
new file mode 100644
index 0000000..17646d4
--- /dev/null
+++ b/src/modules/freebsd/FiretrayChatStatusIcon.jsm
@@ -0,0 +1,301 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/ctypesMap.jsm");
+Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
+Cu.import("resource://firetray/ctypes/linux/gio.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+Cu.import("resource://firetray/linux/FiretrayWindow.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([gdk, gio, gobject, gtk]);
+
+if ("undefined" == typeof(firetray.Handler))
+ log.error("This module MUST be imported from/after FiretrayHandler !");
+
+let log = firetray.Logging.getLogger("firetray.ChatStatusIcon");
+
+const ALPHA_STEP = 5;
+const ALPHA_STEP_SLEEP_MILLISECONDS = 10;
+const FADE_OVER_SLEEP_MILLISECONDS = 500;
+const BLINK_TOGGLE_PERIOD_MILLISECONDS = 500;
+
+
+firetray.ChatStatusIcon = {
+ GTK_THEME_ICON_PATH: null,
+
+ initialized: false,
+ trayIcon: null,
+ appId: (function(){return Services.appinfo.ID;})(),
+ themedIcons: (function(){let o = {};
+ o[FIRETRAY_IM_STATUS_AVAILABLE] = null;
+ o[FIRETRAY_IM_STATUS_AWAY] = null;
+ o[FIRETRAY_IM_STATUS_BUSY] = null;
+ o[FIRETRAY_IM_STATUS_OFFLINE] = null;
+ return o;
+ })(),
+ themedIconNameCurrent: null,
+ timers: {'blink': null, 'fade-step': null, 'fade-loop': null},
+ events: {},
+ generators: {},
+ pixBuffer: {},
+ get isBlinking () {return (firetray.Chat.convsToAcknowledge.length() > 0);},
+
+ init: function() {
+ if (!firetray.Handler.appHasChat) throw "ChatStatusIcon for chat app only";
+ firetray.GtkIcons.init();
+
+ this.trayIcon = gtk.gtk_status_icon_new();
+ this.loadThemedIcons();
+ this.setIconImage(this.themedIconNameCurrent || FIRETRAY_IM_STATUS_OFFLINE); // updated in Chat anyway
+ this.setIconTooltipDefault();
+ this.initTimers();
+
+ this.initialized = true;
+ return true;
+ },
+
+ shutdown: function() {
+ this.destroyTimers();
+ this.destroyIcons();
+ this.initialized = false;
+ },
+
+ loadThemedIcons: function() {
+ for (let name in this.themedIcons)
+ this.themedIcons[name] = gio.g_themed_icon_new(name);
+ },
+
+ destroyIcons: function() {
+ for (let name in this.themedIcons) {
+ let gicon = this.themedIcons[name];
+ gicon = gobject.g_object_unref(gicon);
+ }
+ gobject.g_object_unref(this.trayIcon);
+ },
+
+ setIconImageFromGIcon: function(gicon) {
+ if (!firetray.ChatStatusIcon.trayIcon || !gicon)
+ log.error("Icon missing");
+ gtk.gtk_status_icon_set_from_gicon(firetray.ChatStatusIcon.trayIcon, gicon);
+ },
+
+ setIconImage: function(name) {
+ this.themedIconNameCurrent = name;
+
+ let blinkStyle = firetray.Utils.prefService.getIntPref("chat_icon_blink_style");
+ if (blinkStyle === FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE &&
+ this.isBlinking) {
+ this.events['icon-changed'] = true;
+ return;
+ }
+
+ this.setIconImageFromGIcon(this.themedIcons[name]);
+ },
+
+ setIconVoid: function() {
+ gtk.gtk_status_icon_set_from_pixbuf(this.trayIcon, null);
+ },
+
+ initTimers: function() {
+ for (let tname in this.timers)
+ this.timers[tname] = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ },
+
+ destroyTimers: function() {
+ for (let tname in this.timers) {
+ this.timers[tname].cancel();
+ this.timers[tname] = null;
+ }
+ this.events = {};
+ },
+
+ buildPixBuf: function() {
+ let icon_theme = gtk.gtk_icon_theme_get_for_screen(gdk.gdk_screen_get_default());
+
+ // get pixbuf
+ let arry = gobject.gchar.ptr.array()(2);
+ arry[0] = gobject.gchar.array()(firetray.ChatStatusIcon.themedIconNameCurrent);
+ arry[1] = null;
+ log.debug("icon name="+firetray.ChatStatusIcon.themedIconNameCurrent+", 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.debug("width="+width+", height="+height);
+ let length = width*height*n_channels;
+ let pixels = ctypes.cast(gdk.gdk_pixbuf_get_pixels(pixbuf),
+ gobject.guchar.array(length).ptr);
+ log.debug("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];
+
+ log.debug("pixbuf created");
+ this.pixBuffer = {
+ pixbuf: pixbuf, // TO BE UNREFED WITH to g_object_unref() !!
+ width: width,
+ height: height,
+ length: length,
+ n_channels: n_channels,
+ pixels: pixels,
+ buffer: buffer,
+ alpha_bak: alpha_bak
+ };
+ },
+ dropPixBuf: function() {
+ gobject.g_object_unref(this.pixBuffer.pixbuf);
+ log.debug("pixbuf unref'd");
+ this.pixBuffer = {};
+ },
+
+ fadeGenerator: function() {
+ let pixbuf = firetray.ChatStatusIcon.pixBuffer;
+
+ for (let a=255; a>0; a-=ALPHA_STEP) {
+ for(let i=3; i<pixbuf.length; i+=pixbuf.n_channels)
+ if (pixbuf.pixels.contents[i]-ALPHA_STEP>0)
+ pixbuf.pixels.contents[i] -= ALPHA_STEP;
+ gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf.pixbuf);
+ yield true;
+ }
+
+ for (let a=255; a>0; a-=ALPHA_STEP) {
+ for(let i=3; i<pixbuf.length; i+=pixbuf.n_channels)
+ if (pixbuf.pixels.contents[i]+ALPHA_STEP<=pixbuf.alpha_bak[(i-3)/pixbuf.n_channels]) {
+ pixbuf.pixels.contents[i] += ALPHA_STEP;
+ }
+ gtk.gtk_status_icon_set_from_pixbuf(firetray.ChatStatusIcon.trayIcon, pixbuf.pixbuf);
+ yield true;
+ }
+ },
+
+ fadeStep: function() {
+ try {
+ if (firetray.ChatStatusIcon.generators['fade'].next())
+ firetray.ChatStatusIcon.timers['fade-step'].initWithCallback(
+ { notify: firetray.ChatStatusIcon.fadeStep },
+ ALPHA_STEP_SLEEP_MILLISECONDS, Ci.nsITimer.TYPE_ONE_SHOT);
+
+ } catch (e if e instanceof StopIteration) {
+
+ if (firetray.ChatStatusIcon.events['stop-fade']) {
+ log.debug("stop-fade");
+ delete firetray.ChatStatusIcon.events['stop-fade'];
+ delete firetray.ChatStatusIcon.generators['fade'];
+ firetray.ChatStatusIcon.setIconImage(firetray.ChatStatusIcon.themedIconNameCurrent);
+ firetray.ChatStatusIcon.dropPixBuf();
+ return;
+ }
+
+ if (firetray.ChatStatusIcon.events['icon-changed']) {
+ delete firetray.ChatStatusIcon.events['icon-changed'];
+ firetray.ChatStatusIcon.dropPixBuf();
+ firetray.ChatStatusIcon.buildPixBuf();
+ firetray.ChatStatusIcon.timers['fade-loop'].initWithCallback(
+ { notify: firetray.ChatStatusIcon.fadeLoop },
+ FADE_OVER_SLEEP_MILLISECONDS, Ci.nsITimer.TYPE_ONE_SHOT);
+
+ } else {
+ firetray.ChatStatusIcon.timers['fade-loop'].initWithCallback(
+ { notify: firetray.ChatStatusIcon.fadeLoop },
+ FADE_OVER_SLEEP_MILLISECONDS, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ };
+ },
+
+ fadeLoop: function() {
+ firetray.ChatStatusIcon.generators['fade'] = firetray.ChatStatusIcon.fadeGenerator();
+ firetray.ChatStatusIcon.fadeStep();
+ },
+
+ startFading: function() {
+ log.debug("startFading");
+ this.buildPixBuf();
+ this.fadeLoop();
+ },
+
+ stopFading: function() {
+ log.debug("stopFading");
+ this.events['stop-fade'] = true;
+ },
+
+ startBlinking: function() { // gtk_status_icon_set_blinking() deprecated
+ this.on = true;
+ firetray.ChatStatusIcon.timers['blink'].initWithCallback({
+ notify: function() {
+ if (firetray.ChatStatusIcon.on)
+ firetray.ChatStatusIcon.setIconVoid();
+ else
+ firetray.ChatStatusIcon.setIconImage(firetray.ChatStatusIcon.themedIconNameCurrent);
+ firetray.ChatStatusIcon.on = !firetray.ChatStatusIcon.on;
+ }
+ }, BLINK_TOGGLE_PERIOD_MILLISECONDS, Ci.nsITimer.TYPE_REPEATING_SLACK);
+ },
+
+ stopBlinking: function() {
+ log.debug("stopBlinking");
+ this.timers['blink'].cancel();
+ this.setIconImage(firetray.ChatStatusIcon.themedIconNameCurrent);
+ this.on = false;
+ },
+
+ toggleBlinkStyle: function(blinkStyle) {
+ switch (blinkStyle) {
+ case FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL:
+ this.stopFading();
+ this.startBlinking();
+ break;
+ case FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE:
+ this.stopBlinking();
+ this.startFading();
+ break;
+ default:
+ throw new Error("Undefined chat icon blink style.");
+ }
+ },
+
+ setUrgency: function(xid, urgent) {
+ gtk.gtk_window_set_urgency_hint(firetray.Handler.gtkWindows.get(xid), urgent);
+ },
+
+ setIconTooltip: function(txt) {
+ if (!this.trayIcon) return false;
+ gtk.gtk_status_icon_set_tooltip_text(this.trayIcon, txt);
+ return true;
+ },
+
+ setIconTooltipDefault: function() {
+ this.setIconTooltip(firetray.Handler.appName+" Chat");
+ }
+
+ // TODO: onclick/activate -> chatHandler.showCurrentConversation()
+
+}; // firetray.ChatStatusIcon
diff --git a/src/modules/freebsd/FiretrayGtkIcons.jsm b/src/modules/freebsd/FiretrayGtkIcons.jsm
new file mode 100644
index 0000000..ecdd438
--- /dev/null
+++ b/src/modules/freebsd/FiretrayGtkIcons.jsm
@@ -0,0 +1,67 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([gtk]);
+
+if ("undefined" == typeof(firetray.StatusIcon))
+ log.error("This module MUST be imported from/after StatusIcon !");
+
+let log = firetray.Logging.getLogger("firetray.GtkIcons");
+
+
+firetray.GtkIcons = {
+ initialized: false,
+
+ GTK_THEME_ICON_PATH: null,
+
+ init: function() {
+ try {
+ if (this.initialized) return true;
+
+ this.appendSearchPath();
+ this.initialized = true;
+ return true;
+ } catch (x) {
+ log.error(x);
+ return false;
+ }
+ },
+
+ shutdown: function() {
+ // FIXME: XXX destroy icon here
+ this.initialized = false;
+ },
+
+ appendSearchPath: function() {
+ this.GTK_THEME_ICON_PATH = firetray.Utils.chromeToPath("chrome://firetray/skin/icons/linux");
+ log.debug(this.GTK_THEME_ICON_PATH);
+ let gtkIconTheme = gtk.gtk_icon_theme_get_default();
+ log.debug("gtkIconTheme="+gtkIconTheme);
+ gtk.gtk_icon_theme_append_search_path(gtkIconTheme, this.GTK_THEME_ICON_PATH);
+
+ if (log.level <= firetray.Logging.LogMod.Level.Debug) {
+ Cu.import("resource://firetray/ctypes/linux/glib.jsm");
+ Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+ firetray.Handler.subscribeLibsForClosing([glib, gobject]);
+ let path = new gobject.gchar.ptr.ptr;
+ let n_elements = new gobject.gint;
+ gtk.gtk_icon_theme_get_search_path(gtkIconTheme, path.address(), n_elements.address());
+ log.debug("n_elements="+n_elements+" path="+path);
+ let pathIt = path;
+ for (let i=0, len=n_elements.value; i<len || pathIt.isNull(); ++i) {
+ log.debug("path["+i+"]="+pathIt.contents.readString());
+ pathIt = pathIt.increment();
+ }
+ log.debug("path="+path+" pathIt="+pathIt);
+ glib.g_strfreev(path);
+ }
+ }
+
+};
diff --git a/src/modules/freebsd/FiretrayGtkStatusIcon.jsm b/src/modules/freebsd/FiretrayGtkStatusIcon.jsm
new file mode 100644
index 0000000..87ceaab
--- /dev/null
+++ b/src/modules/freebsd/FiretrayGtkStatusIcon.jsm
@@ -0,0 +1,341 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/linux/cairo.jsm");
+Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
+Cu.import("resource://firetray/ctypes/linux/gio.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+Cu.import("resource://firetray/ctypes/linux/pango.jsm");
+Cu.import("resource://firetray/ctypes/linux/pangocairo.jsm");
+Cu.import("resource://firetray/linux/FiretrayGtkIcons.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([cairo, gdk, gio, gobject, gtk, pango,
+ pangocairo]);
+
+let log = firetray.Logging.getLogger("firetray.GtkStatusIcon");
+
+if ("undefined" == typeof(firetray.Handler))
+ log.error("This module MUST be imported from/after FiretrayStatusIcon !");
+
+
+firetray.GtkStatusIcon = {
+ MIN_FONT_SIZE: 4,
+ FILENAME_BLANK: null,
+ GTK_THEME_ICON_PATH: null,
+
+ initialized: false,
+ callbacks: {},
+ trayIcon: null,
+ themedIconApp: null,
+ themedIconNewMail: null,
+
+ init: function() {
+ this.FILENAME_BLANK = firetray.Utils.chromeToPath(
+ "chrome://firetray/skin/icons/blank-icon.png");
+
+ firetray.GtkIcons.init();
+ this.loadThemedIcons();
+
+ this.trayIcon = gtk.gtk_status_icon_new();
+ firetray.Handler.setIconImageDefault();
+ firetray.Handler.setIconTooltipDefault();
+ this.addCallbacks();
+
+ this.initialized = true;
+ return true;
+ },
+
+ shutdown: function() {
+ log.debug("Disabling GtkStatusIcon");
+ firetray.GtkIcons.shutdown();
+ // FIXME: XXX destroy icon here
+ this.initialized = false;
+ },
+
+ loadThemedIcons: function() {
+ if (firetray.Handler.inMailApp) {
+ let newMailIconNames = firetray.StatusIcon.getNewMailIconNames();
+ if (this.themedIconNewMail) gobject.g_object_unref(this.themedIconNewMail);
+ this.themedIconNewMail = this.initThemedIcon(newMailIconNames);
+ }
+ let appIconNames = firetray.StatusIcon.getAppIconNames();
+ if (this.themedIconApp) gobject.g_object_unref(this.themedIconApp);
+ this.themedIconApp = this.initThemedIcon(appIconNames);
+ },
+
+ initThemedIcon: function(names) {
+ if (!firetray.js.isArray(names)) throw new TypeError();
+ log.debug("themedIconNames="+names);
+ let namesLen = names.length;
+ log.debug("themedIconNamesLen="+namesLen);
+ let themedIconNames = ctypes.char.ptr.array(namesLen)();
+ for (let i=0; i<namesLen; ++i)
+ themedIconNames[i] = ctypes.char.array()(names[i]);
+ log.debug("themedIconNames="+themedIconNames);
+ let themedIcon = gio.g_themed_icon_new_from_names(themedIconNames, namesLen);
+ log.debug("themedIcon="+themedIcon);
+ return themedIcon;
+ },
+
+ addCallbacks: function() {
+ Cu.import("resource://firetray/linux/FiretrayPopupMenu.jsm");
+ /* NOTE: here we do use a function handler (instead of a function
+ definition) because we need the args passed to it ! As a consequence, we
+ need to abandon 'this' in PopupMenu.popup() */
+ this.callbacks.menuPopup = gtk.GCallbackMenuPopup_t(firetray.PopupMenu.popup); // void return, no sentinel
+ gobject.g_signal_connect(this.trayIcon, "popup-menu",
+ firetray.GtkStatusIcon.callbacks.menuPopup, firetray.PopupMenu.menu);
+ this.callbacks.onScroll = gtk.GCallbackOnScroll_t(
+ firetray.GtkStatusIcon.onScroll, null, FIRETRAY_CB_SENTINEL);
+ gobject.g_signal_connect(this.trayIcon, "scroll-event",
+ firetray.GtkStatusIcon.callbacks.onScroll, null);
+
+ log.debug("showHideAllWindows: "+firetray.Handler.hasOwnProperty("showHideAllWindows"));
+ this.callbacks.iconActivate = gtk.GCallbackStatusIconActivate_t(
+ firetray.GtkStatusIcon.onClick, null, FIRETRAY_CB_SENTINEL);
+ let handlerId = gobject.g_signal_connect(firetray.GtkStatusIcon.trayIcon,
+ "activate", firetray.GtkStatusIcon.callbacks.iconActivate, null);
+ log.debug("g_connect activate="+handlerId);
+
+ let pref = firetray.Utils.prefService.getIntPref("middle_click");
+ this.attachMiddleClickCallback(pref);
+ },
+
+ attachMiddleClickCallback: function(pref) {
+ log.debug("attachMiddleClickCallback pref="+pref);
+ if (pref === FIRETRAY_MIDDLE_CLICK_ACTIVATE_LAST) {
+ this.callbacks.iconMiddleClick = gtk.GCallbackStatusIconMiddleClick_t(
+ firetray.Handler.activateLastWindowCb, null, FIRETRAY_CB_SENTINEL);
+ } else if (pref === FIRETRAY_MIDDLE_CLICK_SHOW_HIDE) {
+ this.callbacks.iconMiddleClick = gtk.GCallbackStatusIconMiddleClick_t(
+ function(widget, event, data) {firetray.Handler.showHideAllWindows(); return true;},
+ null, FIRETRAY_CB_SENTINEL);
+ } else {
+ log.error("Unknown pref value for 'middle_click': "+pref);
+ return;
+ }
+ this.callbacks.iconMiddleClickId = gobject.g_signal_connect(
+ firetray.GtkStatusIcon.trayIcon,
+ "button-press-event", firetray.GtkStatusIcon.callbacks.iconMiddleClick,
+ null);
+ log.debug("g_connect middleClick="+this.callbacks.iconMiddleClickId);
+ },
+
+ detachMiddleClickCallback: function() {
+ log.debug("detachMiddleClickCallback");
+ gobject.g_signal_handler_disconnect(
+ firetray.GtkStatusIcon.trayIcon,
+ gobject.gulong(this.callbacks.iconMiddleClickId)
+ );
+ delete this.callbacks.iconMiddleClickId;
+ },
+
+ onScroll: function(icon, event, data) {
+ let gdkEventScroll = ctypes.cast(event, gdk.GdkEventScroll.ptr);
+ let direction = gdkEventScroll.contents.direction;
+
+ firetray.StatusIcon.onScroll(direction);
+
+ let stopPropagation = false;
+ return stopPropagation;
+ },
+
+ onClick: function(gtkStatusIcon, userData) {
+ firetray.Handler.showHideAllWindows();
+ let stopPropagation = true;
+ return stopPropagation;
+ },
+
+ setIconImageFromFile: function(filename) {
+ if (!firetray.GtkStatusIcon.trayIcon)
+ log.error("Icon missing");
+ log.debug(filename);
+ gtk.gtk_status_icon_set_from_file(firetray.GtkStatusIcon.trayIcon,
+ filename);
+ },
+
+ setIconImageFromGIcon: function(gicon) {
+ if (!firetray.GtkStatusIcon.trayIcon || !gicon)
+ log.error("Icon missing");
+ log.debug(gicon);
+ gtk.gtk_status_icon_set_from_gicon(firetray.GtkStatusIcon.trayIcon, gicon);
+ },
+
+}; // GtkStatusIcon
+
+firetray.StatusIcon.initImpl =
+ firetray.GtkStatusIcon.init.bind(firetray.GtkStatusIcon);
+
+firetray.StatusIcon.shutdownImpl =
+ firetray.GtkStatusIcon.shutdown.bind(firetray.GtkStatusIcon);
+
+firetray.StatusIcon.middleClickActionChanged = function() {
+ log.debug("middleClickActionChanged");
+ let pref = firetray.Utils.prefService.getIntPref("middle_click");
+ firetray.GtkStatusIcon.detachMiddleClickCallback();
+ firetray.GtkStatusIcon.attachMiddleClickCallback(pref);
+};
+
+
+firetray.Handler.loadIcons = firetray.GtkStatusIcon.loadThemedIcons;
+
+firetray.Handler.setIconImageDefault = function() {
+ log.debug("setIconImageDefault");
+ if (!firetray.GtkStatusIcon.themedIconApp)
+ throw "Default application themed icon not set";
+ let appIconType = firetray.Utils.prefService.getIntPref("app_icon_type");
+ if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_THEMED) {
+ firetray.GtkStatusIcon.setIconImageFromGIcon(
+ firetray.GtkStatusIcon.themedIconApp);
+ } else if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM) {
+ firetray.Handler.setIconImageCustom("app_icon_custom");
+ }
+};
+
+firetray.Handler.setIconImageNewMail = function() {
+ firetray.GtkStatusIcon.setIconImageFromGIcon(
+ firetray.GtkStatusIcon.themedIconNewMail);
+};
+
+firetray.Handler.setIconImageCustom = function(prefname) {
+ let prefCustomIconPath = firetray.Utils.prefService.getCharPref(prefname);
+ firetray.GtkStatusIcon.setIconImageFromFile(prefCustomIconPath);
+};
+
+// GTK bug: Gdk-CRITICAL **: IA__gdk_window_get_root_coords: assertion `GDK_IS_WINDOW (window)' failed
+firetray.Handler.setIconTooltip = function(toolTipStr) {
+ if (!firetray.GtkStatusIcon.trayIcon)
+ return false;
+
+ log.debug("setIconTooltip, toolTipStr="+toolTipStr);
+ try {
+ gtk.gtk_status_icon_set_tooltip_text(firetray.GtkStatusIcon.trayIcon,
+ toolTipStr);
+ } catch (x) {
+ log.error(x);
+ return false;
+ }
+ return true;
+};
+
+firetray.Handler.setIconText = function(text, color) { // FIXME: function too long
+ log.debug("setIconText, color="+color);
+ if (typeof(text) != "string")
+ throw new TypeError();
+
+ try {
+ // build background from image
+ let specialIcon = gdk.gdk_pixbuf_new_from_file(
+ firetray.GtkStatusIcon.FILENAME_BLANK, null); // GError **error);
+ let dest = gdk.gdk_pixbuf_copy(specialIcon);
+ let w = gdk.gdk_pixbuf_get_width(specialIcon);
+ let h = gdk.gdk_pixbuf_get_height(specialIcon);
+
+ // prepare colors/alpha
+ let colorMap = gdk.gdk_screen_get_system_colormap(gdk.gdk_screen_get_default());
+ let visual = gdk.gdk_colormap_get_visual(colorMap);
+ let visualDepth = visual.contents.depth;
+ log.debug("colorMap="+colorMap+" visual="+visual+" visualDepth="+visualDepth);
+ let fore = new gdk.GdkColor;
+ fore.pixel = fore.red = fore.green = fore.blue = 0;
+ let alpha = new gdk.GdkColor;
+ alpha.pixel = alpha.red = alpha.green = alpha.blue = 0xFFFF;
+ if (!fore || !alpha)
+ log.warn("Undefined GdkColor fore or alpha");
+ gdk.gdk_color_parse(color, fore.address());
+ if(fore.red == alpha.red && fore.green == alpha.green && fore.blue == alpha.blue) {
+ alpha.red=0; // make sure alpha is different from fore
+ }
+ gdk.gdk_colormap_alloc_color(colorMap, fore.address(), true, true);
+ gdk.gdk_colormap_alloc_color(colorMap, alpha.address(), true, true);
+
+ // build pixmap with rectangle
+ let pm = gdk.gdk_pixmap_new(null, w, h, visualDepth);
+ let pmDrawable = ctypes.cast(pm, gdk.GdkDrawable.ptr);
+ let cr = gdk.gdk_cairo_create(pmDrawable);
+ gdk.gdk_cairo_set_source_color(cr, alpha.address());
+ cairo.cairo_rectangle(cr, 0, 0, w, h);
+ cairo.cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo.cairo_fill(cr);
+
+ // build text
+ let scratch = gtk.gtk_window_new(gtk.GTK_WINDOW_TOPLEVEL);
+ let layout = gtk.gtk_widget_create_pango_layout(scratch, null);
+ gtk.gtk_widget_destroy(scratch);
+ let fnt = pango.pango_font_description_from_string("Sans 18");
+ pango.pango_font_description_set_weight(fnt,pango.PANGO_WEIGHT_SEMIBOLD);
+ pango.pango_layout_set_spacing(layout,0);
+ pango.pango_layout_set_font_description(layout, fnt);
+ log.debug("layout="+layout);
+ log.debug("text="+text);
+ pango.pango_layout_set_text(layout, text,-1);
+ let tw = new ctypes.int;
+ let th = new ctypes.int;
+ let sz;
+ let border = 4;
+ pango.pango_layout_get_pixel_size(layout, tw.address(), th.address());
+ log.debug("tw="+tw.value+" th="+th.value);
+ // fit text to the icon by decreasing font size
+ while ( tw.value > (w - border) || th.value > (h - border) ) {
+ sz = pango.pango_font_description_get_size(fnt);
+ if(sz < firetray.GtkStatusIcon.MIN_FONT_SIZE) {
+ sz = firetray.GtkStatusIcon.MIN_FONT_SIZE;
+ break;
+ }
+ sz -= pango.PANGO_SCALE;
+ pango.pango_font_description_set_size(fnt,sz);
+ pango.pango_layout_set_font_description(layout, fnt);
+ pango.pango_layout_get_pixel_size(layout, tw.address(), th.address());
+ }
+ log.debug("tw="+tw.value+" th="+th.value);
+ pango.pango_font_description_free(fnt);
+ // center text
+ let px = (w-tw.value)/2;
+ let py = (h-th.value)/2;
+
+ // draw text on pixmap
+ gdk.gdk_cairo_set_source_color(cr, fore.address());
+ cairo.cairo_move_to(cr, px, py);
+ pangocairo.pango_cairo_show_layout(cr, layout);
+ cairo.cairo_destroy(cr);
+ gobject.g_object_unref(layout);
+
+ let buf = gdk.gdk_pixbuf_get_from_drawable(null, pmDrawable, null, 0, 0, 0, 0, w, h);
+ gobject.g_object_unref(pm);
+ log.debug("alpha="+alpha);
+ let alphaRed = gobject.guint16(alpha.red);
+ let alphaRed_guchar = ctypes.cast(alphaRed, gobject.guchar);
+ let alphaGreen = gobject.guint16(alpha.green);
+ let alphaGreen_guchar = ctypes.cast(alphaGreen, gobject.guchar);
+ let alphaBlue = gobject.guint16(alpha.blue);
+ let alphaBlue_guchar = ctypes.cast(alphaBlue, gobject.guchar);
+ let bufAlpha = gdk.gdk_pixbuf_add_alpha(buf, true, alphaRed_guchar, alphaGreen_guchar, alphaBlue_guchar);
+ gobject.g_object_unref(buf);
+
+ // merge the rendered text on top
+ gdk.gdk_pixbuf_composite(bufAlpha,dest,0,0,w,h,0,0,1,1,gdk.GDK_INTERP_BILINEAR,255);
+ gobject.g_object_unref(bufAlpha);
+
+ log.debug("gtk_status_icon_set_from_pixbuf="+dest);
+ gtk.gtk_status_icon_set_from_pixbuf(firetray.GtkStatusIcon.trayIcon, dest);
+ } catch (x) {
+ log.error(x);
+ return false;
+ }
+
+ return true;
+};
+
+firetray.Handler.setIconVisibility = function(visible) {
+ if (!firetray.GtkStatusIcon.trayIcon)
+ return false;
+ gtk.gtk_status_icon_set_visible(firetray.GtkStatusIcon.trayIcon, visible);
+ return true;
+};
diff --git a/src/modules/freebsd/FiretrayPopupMenu.jsm b/src/modules/freebsd/FiretrayPopupMenu.jsm
new file mode 100644
index 0000000..21f0eec
--- /dev/null
+++ b/src/modules/freebsd/FiretrayPopupMenu.jsm
@@ -0,0 +1,262 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([gobject, gtk]);
+
+let log = firetray.Logging.getLogger("firetray.PopupMenu");
+
+if ("undefined" == typeof(firetray.StatusIcon))
+ log.error("This module MUST be imported from/after StatusIcon !");
+
+
+firetray.PopupMenu = {
+ MENU_ITEM_WINDOWS_POSITION: 4,
+
+ initialized: false,
+ callbacks: {menuItemWindowActivate: {}}, // FIXME: try to store them into a ctypes array/struct.
+ menu: null,
+ menuShell: null,
+ menuSeparatorWindows: null,
+ menuItem: {tip: null, showHide: null, activateLast: null, sep: null},
+
+ init: function() {
+ this.menu = gtk.gtk_menu_new();
+ this.menuShell = ctypes.cast(this.menu, gtk.GtkMenuShell.ptr);
+ var addMenuSeparator = false;
+
+ if (firetray.Handler.inMailApp) {
+ this.addItem({itemName:"ResetIcon", iconName:"gtk-apply",
+ action:"activate", callback: firetray.Handler.setIconImageDefault});
+ this.addItem({itemName:"NewMessage", iconName:"gtk-edit",
+ action:"activate", callback: firetray.Handler.openMailMessage});
+ addMenuSeparator = true;
+ }
+
+ if (firetray.Handler.inBrowserApp) {
+ this.addItem({itemName:"NewWindow", iconName:"gtk-new",
+ action:"activate", callback: firetray.Handler.openBrowserWindow});
+ addMenuSeparator = true;
+ }
+
+ var menuSeparator;
+ if (addMenuSeparator) {
+ menuSeparator = gtk.gtk_separator_menu_item_new();
+ gtk.gtk_menu_shell_append(this.menuShell, ctypes.cast(menuSeparator,
+ gtk.GtkWidget.ptr));
+ }
+
+ this.addItem({itemName:"Preferences", iconName:"gtk-preferences",
+ action:"activate", callback: firetray.Handler.openPrefWindow});
+ menuSeparator = gtk.gtk_separator_menu_item_new();
+ gtk.gtk_menu_shell_append(this.menuShell, ctypes.cast(menuSeparator,
+ gtk.GtkWidget.ptr));
+
+ this.addItem({itemName:"Quit", iconName:"gtk-quit",
+ action:"activate", callback: firetray.Handler.quitApplication});
+
+ var menuWidget = ctypes.cast(this.menu, gtk.GtkWidget.ptr);
+ gtk.gtk_widget_show_all(menuWidget);
+
+ // for hidden windows, not shown otherwise
+ this.menuSeparatorWindows = gtk.gtk_separator_menu_item_new();
+ gtk.gtk_menu_shell_prepend(
+ this.menuShell, ctypes.cast(this.menuSeparatorWindows, gtk.GtkWidget.ptr));
+ // FIXME: we better use a submenu for this: gtk_menu_new(), gtk_menu_item_set_submenu();
+
+ // for AppIndicator, not shown otherwise
+ this.prependAppIndicatorItems();
+
+ this.initialized = true;
+ return true;
+ },
+
+ shutdown: function() {
+ log.debug("Disabling PopupMenu");
+ this.initialized = false;
+ },
+
+ addItem: function(it) {
+ var menuItemLabel = firetray.Utils.strings.GetStringFromName("popupMenu.itemLabel."+it.itemName); // shouldn't need to convert to utf8 later thank to js-ctypes
+ var menuItem = gtk.gtk_image_menu_item_new_with_label(menuItemLabel);
+ var menuItemIcon = gtk.gtk_image_new_from_stock(it.iconName, gtk.GTK_ICON_SIZE_MENU);
+ gtk.gtk_image_menu_item_set_image(menuItem, menuItemIcon);
+ var menuItemWidget = ctypes.cast(menuItem, gtk.GtkWidget.ptr);
+ if (it.inFront)
+ gtk.gtk_menu_shell_prepend(this.menuShell, menuItemWidget);
+ else
+ gtk.gtk_menu_shell_append(this.menuShell, menuItemWidget);
+
+ function capitalizeFirst(str) {
+ return str.charAt(0).toUpperCase() + str.substring(1);
+ }
+
+ let cbName = "menuItem"+capitalizeFirst(it.itemName)+capitalizeFirst(it.action);
+ if (this.callbacks.hasOwnProperty(cbName))
+ log.warn("callback '"+cbName+"' already registered");
+ else
+ log.debug("cbName="+cbName);
+ this.callbacks[cbName] = gobject.GCallback_t(it.callback); // void return, no sentinel
+ gobject.g_signal_connect(menuItem, it.action,
+ firetray.PopupMenu.callbacks[cbName], null);
+
+ return menuItem;
+ },
+
+ prependAppIndicatorItems: function() {
+ this.menuItem.sep = gtk.gtk_separator_menu_item_new();
+ gtk.gtk_menu_shell_prepend(this.menuShell, ctypes.cast(this.menuItem.sep,
+ gtk.GtkWidget.ptr));
+
+ this.menuItem.activateLast = this.addItem({
+ itemName:"ActivateLast", iconName:null, action:"activate", callback:
+ firetray.Handler.showAllWindowsAndActivate, inFront: true});
+
+ this.menuItem.showHide = this.addItem({
+ itemName:"ShowHide", iconName:"gtk-go-down", action:"activate", callback:
+ firetray.Handler.showHideAllWindows, inFront: true});
+
+ this.menuItem.tip = this.createAndAddItemToMenuAt(0);
+ gtk.gtk_widget_set_sensitive(
+ ctypes.cast(this.menuItem.tip, gtk.GtkWidget.ptr), false);
+ },
+
+ popup: function(icon, button, activateTime, menu) {
+ log.debug("menu-popup");
+ log.debug("ARGS="+icon+", "+button+", "+activateTime+", "+menu);
+
+ var gtkMenuPtr = ctypes.cast(menu, gtk.GtkMenu.ptr);
+ var iconGpointer = ctypes.cast(icon, gobject.gpointer);
+ gtk.gtk_menu_popup(
+ gtkMenuPtr, null, null, gtk.gtk_status_icon_position_menu,
+ iconGpointer, button, activateTime);
+
+ let stopPropagation = false;
+ return stopPropagation;
+ },
+
+ // we'll be creating menuItems for windows (and not showing them) even if
+ // hides_single_window is false, because if hides_single_window becomes true,
+ // we'll just have to show the menuItems
+ addWindowItem: function(xid) { // on registerWindow
+ log.debug("addWindowItem");
+ var menuItemWindow = this.createAndAddItemToMenuAt(
+ this.MENU_ITEM_WINDOWS_POSITION);
+ firetray.Handler.gtkPopupMenuWindowItems.insert(xid, menuItemWindow);
+ this.setItemLabel(menuItemWindow, xid.toString()); // default to xid
+
+ let callback = gobject.GCallback_t(
+ function(){firetray.Handler.showWindow(xid);}); // void return, no sentinel
+ this.callbacks.menuItemWindowActivate[xid] = callback,
+ gobject.g_signal_connect(menuItemWindow, "activate", callback, null);
+
+ log.debug("added gtkPopupMenuWindowItems: "+firetray.Handler.gtkPopupMenuWindowItems.count);
+ },
+
+ createAndAddItemToMenuAt: function(pos) {
+ var menuItem = gtk.gtk_image_menu_item_new();
+ gtk.gtk_menu_shell_insert(this.menuShell,
+ ctypes.cast(menuItem, gtk.GtkWidget.ptr),
+ pos);
+ return menuItem;
+ },
+
+ removeWindowItem: function(xid) { // on unregisterWindow
+ let menuItemWindow = firetray.Handler.gtkPopupMenuWindowItems.get(xid);
+ firetray.Handler.gtkPopupMenuWindowItems.remove(xid);
+ this.removeItem(menuItemWindow);
+ log.debug("remove gtkPopupMenuWindowItems: "+firetray.Handler.gtkPopupMenuWindowItems.count);
+ },
+ removeItem: function(item) {
+ gtk.gtk_widget_destroy(ctypes.cast(item, gtk.GtkWidget.ptr));
+ },
+
+ showAllWindowItemsOnlyVisibleWindows: function() {
+ for (let xid in firetray.Handler.windows)
+ if (!firetray.Handler.windows[xid].visible)
+ this.showWindowItem(xid);
+ },
+
+ showWindowItem: function(xid) {
+ if (!this.windowItemsHandled())
+ return;
+
+ log.debug("showWindowItem");
+ let menuItemWindow = firetray.Handler.gtkPopupMenuWindowItems.get(xid);
+ this.showItem(menuItemWindow);
+ this.setItemLabel(menuItemWindow, firetray.Window.getWindowTitle(xid));
+ this.showWindowSeparator();
+ },
+
+ showItem: function(menuItem) {
+ gtk.gtk_widget_show(ctypes.cast(menuItem, gtk.GtkWidget.ptr));
+ },
+
+ setItemLabel: function(menuItem, label) {
+ log.debug("about to set title: "+label);
+ if (label)
+ gtk.gtk_menu_item_set_label(ctypes.cast(menuItem, gtk.GtkMenuItem.ptr), label);
+ },
+
+ hideAllWindowItems: function() {
+ for (let xid in firetray.Handler.windows)
+ this.hideWindowItemAndSeparator(xid);
+ },
+
+ hideWindowItemAndSeparator: function(xid) {
+ this.hideWindowItem(xid);
+ this.hideWindowSeparator();
+ },
+
+ hideWindowItemAndSeparatorMaybe: function(xid) {
+ if (!this.windowItemsHandled()) return;
+
+ this.hideWindowItem(xid);
+ if (firetray.Handler.visibleWindowsCount === firetray.Handler.windowsCount)
+ this.hideWindowSeparator();
+ },
+
+ hideWindowItem: function(xid) {
+ log.debug("hideWindowItem");
+ let menuItemWindow = firetray.Handler.gtkPopupMenuWindowItems.get(xid);
+ this.hideItem(menuItemWindow);
+ },
+
+ hideItem: function(menuItem) {
+ gtk.gtk_widget_hide(ctypes.cast(menuItem, gtk.GtkWidget.ptr));
+ },
+
+ showWindowSeparator: function() {
+ log.debug("showing menuSeparatorWindows");
+ gtk.gtk_widget_show(ctypes.cast(this.menuSeparatorWindows, gtk.GtkWidget.ptr));
+ },
+ hideWindowSeparator: function() {
+ log.debug("hiding menuSeparatorWindows");
+ gtk.gtk_widget_hide(ctypes.cast(this.menuSeparatorWindows, gtk.GtkWidget.ptr));
+ },
+
+ showHideWindowItems: function() {
+ if (this.windowItemsHandled())
+ this.showAllWindowItemsOnlyVisibleWindows();
+ else
+ this.hideAllWindowItems();
+ },
+
+ windowItemsHandled: function() {
+ return firetray.Utils.prefService.getBoolPref('hides_single_window');
+ }
+
+}; // firetray.PopupMenu
+
+firetray.Handler.showHidePopupMenuItems =
+ firetray.PopupMenu.showHideWindowItems.bind(firetray.PopupMenu);
diff --git a/src/modules/freebsd/FiretrayStatusIcon.jsm b/src/modules/freebsd/FiretrayStatusIcon.jsm
new file mode 100644
index 0000000..642d478
--- /dev/null
+++ b/src/modules/freebsd/FiretrayStatusIcon.jsm
@@ -0,0 +1,219 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
+Cu.import("resource://firetray/ctypes/linux/gio.jsm");
+Cu.import("resource://firetray/ctypes/linux/glib.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/libc.jsm");
+Cu.import("resource://firetray/ctypes/linux/x11.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([gdk, gio, glib, gobject]);
+
+let log = firetray.Logging.getLogger("firetray.StatusIcon");
+
+if ("undefined" == typeof(firetray.Handler))
+ log.error("This module MUST be imported from/after FiretrayHandler !");
+
+
+firetray.StatusIcon = {
+ initialized: false,
+ callbacks: {}, // pointers to JS functions. MUST LIVE DURING ALL THE EXECUTION
+ prefAppIconNames: null,
+ prefNewMailIconNames: null,
+ defaultAppIconName: null,
+ defaultNewMailIconName: null,
+ canAppIndicator: null,
+
+ init: function() {
+ this.defineIconNames();
+
+ // PopupMenu g_connect's some Handler functions. As these are overridden is
+ // StatusIcon implementations, PopupMenu must be initialized *after*
+ // implemenations are imported.
+ Cu.import("resource://firetray/ctypes/linux/appindicator.jsm");
+ this.canAppIndicator =
+ (appind3.available() && this.dbusNotificationWatcherReady());
+ log.info("canAppIndicator="+this.canAppIndicator);
+ /* We can't reliably detect if xembed tray icons are supported, because, for
+ instance, Unity/compiz falsely claims to have support for it through
+ _NET_SYSTEM_TRAY_Sn (compiz). So we end up using the desktop id as a
+ criteria for enabling appindicator. */
+ let desktop = this.getDesktop();
+ log.info("desktop="+JSON.stringify(desktop));
+
+ if (firetray.Utils.prefService.getBoolPref('with_appindicator') &&
+ this.canAppIndicator &&
+ (desktop.name === 'unity' ||
+ (desktop.name === 'kde' && desktop.ver > 4))) {
+ Cu.import("resource://firetray/linux/FiretrayAppIndicator.jsm");
+ /* FIXME: Ubuntu14.04/Unity: successfully closing appind3 crashes FF/TB
+ during exit, in Ubuntu's unity-menubar.patch's code.
+ https://bugs.launchpad.net/ubuntu/+source/firefox/+bug/1393256 */
+ // firetray.Handler.subscribeLibsForClosing([appind3]);
+ } else {
+ Cu.import("resource://firetray/linux/FiretrayGtkStatusIcon.jsm");
+ }
+
+ Cu.import("resource://firetray/linux/FiretrayPopupMenu.jsm");
+ if (!firetray.PopupMenu.init())
+ return false;
+
+ if (!firetray.StatusIcon.initImpl())
+ return false;
+
+ this.initialized = true;
+ return true;
+ },
+
+ shutdown: function() {
+ log.debug("Disabling StatusIcon");
+ firetray.StatusIcon.shutdownImpl();
+ firetray.PopupMenu.shutdown();
+ this.initialized = false;
+ },
+
+ defineIconNames: function() {
+ this.prefAppIconNames = (function() {
+ if (firetray.Handler.inMailApp) {
+ return "app_mail_icon_names";
+ } else if (firetray.Handler.inBrowserApp) {
+ return "app_browser_icon_names";
+ } else {
+ return "app_default_icon_names";
+ }
+ })();
+ this.defaultAppIconName = firetray.Handler.appName.toLowerCase();
+
+ this.prefNewMailIconNames = "new_mail_icon_names";
+ this.defaultNewMailIconName = "mail-unread";
+ },
+
+ getAppIconNames: function() {
+ let appIconNames = firetray.Utils.getArrayPref(
+ firetray.StatusIcon.prefAppIconNames);
+ appIconNames.push(firetray.StatusIcon.defaultAppIconName);
+ return appIconNames;
+ },
+ getNewMailIconNames: function() {
+ let newMailIconNames = firetray.Utils.getArrayPref(
+ firetray.StatusIcon.prefNewMailIconNames);
+ newMailIconNames.push(firetray.StatusIcon.defaultNewMailIconName);
+ return newMailIconNames;
+ },
+
+ loadImageCustom: function() { }, // done in setIconImageCustom
+
+ getDesktop: function() {
+ let env = Cc["@mozilla.org/process/environment;1"]
+ .createInstance(Ci.nsIEnvironment);
+ let XDG_CURRENT_DESKTOP = env.get("XDG_CURRENT_DESKTOP").toLowerCase();
+ let DESKTOP_SESSION = env.get("DESKTOP_SESSION").toLowerCase();
+
+ let desktop = {name:'unknown', ver:null};
+ if (XDG_CURRENT_DESKTOP === 'unity' || DESKTOP_SESSION === 'ubuntu') {
+ desktop.name = 'unity';
+ }
+ else if (XDG_CURRENT_DESKTOP === 'kde') { // DESKTOP_SESSION kde-plasma, plasme, kf5, ...
+ desktop.name = 'kde';
+ let KDE_SESSION_VERSION = env.get("KDE_SESSION_VERSION");
+ if (KDE_SESSION_VERSION) desktop.ver = parseInt(KDE_SESSION_VERSION, 10);
+ }
+ else if (DESKTOP_SESSION) {
+ desktop.name = DESKTOP_SESSION;
+ }
+ else if (XDG_CURRENT_DESKTOP) {
+ desktop.name = XDG_CURRENT_DESKTOP;
+ }
+
+ return desktop;
+ },
+
+ dbusNotificationWatcherReady: function() {
+ let watcherReady = false;
+
+ function error(e) {
+ if (!e.isNull()) {
+ log.error(e.contents.message);
+ glib.g_error_free(e);
+ }
+ }
+
+ let conn = new gio.GDBusConnection.ptr;
+ let err = new glib.GError.ptr(null);
+ conn = gio.g_bus_get_sync(gio.G_BUS_TYPE_SESSION, null, err.address());
+ if (error(err)) return watcherReady;
+
+ if (!conn.isNull()) {
+ let flags = gio.G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+ gio.G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ gio.G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS;
+
+ let proxy = gio.g_dbus_proxy_new_for_bus_sync(
+ gio.G_BUS_TYPE_SESSION,
+ flags,
+ null, /* GDBusInterfaceInfo */
+ appind3.NOTIFICATION_WATCHER_DBUS_ADDR,
+ appind3.NOTIFICATION_WATCHER_DBUS_OBJ,
+ appind3.NOTIFICATION_WATCHER_DBUS_IFACE,
+ null, /* GCancellable */
+ err.address());
+ if (error(err)) return watcherReady;
+
+ if (!proxy.isNull()) {
+ let owner = gio.g_dbus_proxy_get_name_owner(proxy);
+ if (!owner.isNull()) {
+ watcherReady = true;
+ }
+ gobject.g_object_unref(proxy);
+ }
+
+ gobject.g_object_unref(conn);
+ }
+
+ return watcherReady;
+ },
+
+ onScroll: function(direction) {
+ if (!firetray.Utils.prefService.getBoolPref("scroll_hides"))
+ return false;
+
+ let scroll_mode = firetray.Utils.prefService.getCharPref("scroll_mode");
+ switch(direction) {
+ case gdk.GDK_SCROLL_UP:
+ log.debug("SCROLL UP");
+ if (scroll_mode === "down_hides")
+ firetray.Handler.showAllWindows();
+ else if (scroll_mode === "up_hides")
+ firetray.Handler.hideAllWindows();
+ break;
+ case gdk.GDK_SCROLL_DOWN:
+ log.debug("SCROLL DOWN");
+ if (scroll_mode === "down_hides")
+ firetray.Handler.hideAllWindows();
+ else if (scroll_mode === "up_hides")
+ firetray.Handler.showAllWindows();
+ break;
+ default:
+ log.error("SCROLL UNKNOWN");
+ }
+
+ return true;
+ }
+
+}; // firetray.StatusIcon
+
+
+firetray.Handler.setIconTooltipDefault = function() {
+ if (!this.appName)
+ throw "application name not initialized";
+ this.setIconTooltip(this.appName);
+};
+
+firetray.Handler.setIconTooltip = function(toolTipStr) { };
diff --git a/src/modules/freebsd/FiretrayWindow.jsm b/src/modules/freebsd/FiretrayWindow.jsm
new file mode 100644
index 0000000..1a53252
--- /dev/null
+++ b/src/modules/freebsd/FiretrayWindow.jsm
@@ -0,0 +1,761 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* GdkWindow and GtkWindow are totally different things. A GtkWindow is a
+ "standalone" window. A GdkWindow is just a region on the screen that can
+ capture events and has certain attributes (such as a cursor, and a coordinate
+ system). Basically a GdkWindow is an X window, in the Xlib sense, and
+ GtkWindow is a widget used for a particular UI effect.
+ (http://mail.gnome.org/archives/gtk-app-devel-list/1999-January/msg00138.html) */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/ctypesMap.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+Cu.import("resource://firetray/ctypes/linux/libc.jsm");
+Cu.import("resource://firetray/ctypes/linux/x11.jsm");
+Cu.import("resource://firetray/FiretrayWindow.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([gobject, gdk, gtk, libc, x11, glib]);
+
+let log = firetray.Logging.getLogger("firetray.Window");
+
+if ("undefined" == typeof(firetray.Handler))
+ log.error("This module MUST be imported from/after FiretrayHandler !");
+
+const Services2 = {};
+XPCOMUtils.defineLazyServiceGetter(
+ Services2,
+ "uuid",
+ "@mozilla.org/uuid-generator;1",
+ "nsIUUIDGenerator"
+);
+
+const FIRETRAY_XWINDOW_HIDDEN = 1 << 0; // when minimized also
+const FIRETRAY_XWINDOW_MAXIMIZED = 1 << 1;
+
+/**
+ * custum type used to pass data in to and out of findGtkWindowByTitleCb
+ */
+var _find_data_t = ctypes.StructType("_find_data_t", [
+ { inTitle: ctypes.char.ptr },
+ { outWindow: gtk.GtkWindow.ptr }
+]);
+
+// NOTE: storing ctypes pointers into a JS object doesn't work: pointers are
+// "evolving" after a while (maybe due to back and forth conversion). So we
+// need to store them into a real ctypes array !
+firetray.Handler.gtkWindows = new ctypesMap(gtk.GtkWindow.ptr);
+firetray.Handler.gdkWindows = new ctypesMap(gdk.GdkWindow.ptr);
+firetray.Handler.gtkPopupMenuWindowItems = new ctypesMap(gtk.GtkImageMenuItem.ptr);
+
+
+firetray.Window = new FiretrayWindow();
+firetray.Window.signals = {'focus-in': {callback: {}, handler: {}}};
+
+firetray.Window.init = function() {
+ let gtkVersionCheck = gtk.gtk_check_version(
+ gtk.FIRETRAY_REQUIRED_GTK_MAJOR_VERSION,
+ gtk.FIRETRAY_REQUIRED_GTK_MINOR_VERSION,
+ gtk.FIRETRAY_REQUIRED_GTK_MICRO_VERSION
+ );
+ if (!gtkVersionCheck.isNull())
+ log.error("gtk_check_version="+gtkVersionCheck.readString());
+
+ if (firetray.Handler.isChatEnabled()) {
+ Cu.import("resource://firetray/linux/FiretrayChat.jsm");
+ Cu.import("resource://firetray/linux/FiretrayChatStatusIcon.jsm");
+ }
+
+ this.initialized = true;
+};
+
+firetray.Window.shutdown = function() {
+ this.initialized = false;
+};
+
+/**
+ * Iterate over all Gtk toplevel windows to find a window. We rely on
+ * Service.wm to watch windows correctly: we should find only one window.
+ *
+ * @author Nils Maier (stolen from MiniTrayR), himself inspired by Windows docs
+ * @param window nsIDOMWindow from Services.wm
+ * @return a gtk.GtkWindow.ptr
+ */
+firetray.Window.getGtkWindowFromChromeWindow = function(window) {
+ let baseWindow = window
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIBaseWindow);
+
+ // Tag the base window
+ let oldTitle = baseWindow.title;
+ log.debug("oldTitle="+oldTitle);
+ baseWindow.title = Services2.uuid.generateUUID().toString();
+
+ try {
+ // Search the window by the *temporary* title
+ let widgets = gtk.gtk_window_list_toplevels();
+ let that = this;
+ let findGtkWindowByTitleCb = gobject.GFunc_t(that._findGtkWindowByTitle); // void return, no sentinel
+ var userData = new _find_data_t(
+ ctypes.char.array()(baseWindow.title),
+ null
+ ).address();
+ log.debug("userData="+userData);
+ gobject.g_list_foreach(widgets, findGtkWindowByTitleCb, userData);
+ gobject.g_list_free(widgets);
+
+ if (userData.contents.outWindow.isNull())
+ throw new Error("Window not found!");
+
+ log.debug("found window: "+userData.contents.outWindow);
+ } catch (x) {
+ log.error(x);
+ } finally {
+ // Restore
+ baseWindow.title = oldTitle;
+ }
+
+ return userData.contents.outWindow;
+};
+
+/**
+ * compares a GtkWindow's title with a string passed in userData
+ * @param gtkWidget: GtkWidget from gtk_window_list_toplevels()
+ * @param userData: _find_data_t
+ */
+firetray.Window._findGtkWindowByTitle = function(gtkWidget, userData) {
+ let data = ctypes.cast(userData, _find_data_t.ptr);
+ let inTitle = data.contents.inTitle;
+
+ let gtkWin = ctypes.cast(gtkWidget, gtk.GtkWindow.ptr);
+ let winTitle = gtk.gtk_window_get_title(gtkWin);
+
+ if (!winTitle.isNull()) {
+ log.debug(inTitle+" = "+winTitle);
+ if (libc.strcmp(inTitle, winTitle) == 0)
+ data.contents.outWindow = gtkWin;
+ }
+};
+
+firetray.Window.getGdkWindowFromGtkWindow = function(gtkWin) {
+ try {
+ let gtkWid = ctypes.cast(gtkWin, gtk.GtkWidget.ptr);
+ return gtk.gtk_widget_get_window(gtkWid);
+ } catch (x) {
+ log.error(x);
+ }
+ return null;
+};
+
+firetray.Window.getXIDFromGdkWindow = function(gdkWin) {
+ return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
+};
+
+firetray.Window.getXIDFromGtkWidget = function(gtkWid) {
+ let gdkWin = gtk.gtk_widget_get_window(gtkWid);
+ return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
+};
+
+firetray.Window.addrPointedByInHex = function(ptr) {
+ return "0x"+ctypes.cast(ptr, ctypes.uintptr_t.ptr).contents.toString(16);
+};
+
+firetray.Window.getGdkWindowFromNativeHandle = function(nativeHandle) {
+ let gdkw = new gdk.GdkWindow.ptr(ctypes.UInt64(nativeHandle)); // a new pointer to the GdkWindow
+ gdkw = gdk.gdk_window_get_toplevel(gdkw);
+ log.debug("gdkw="+gdkw+" *gdkw="+this.addrPointedByInHex(gdkw));
+ return gdkw;
+};
+
+firetray.Window.getGtkWindowFromGdkWindow = function(gdkWin) {
+ let gptr = new gobject.gpointer;
+ gdk.gdk_window_get_user_data(gdkWin, gptr.address());
+ log.debug("gptr="+gptr+" *gptr="+this.addrPointedByInHex(gptr));
+ let gtkw = ctypes.cast(gptr, gtk.GtkWindow.ptr);
+ log.debug("gtkw="+gtkw+" *gtkw="+this.addrPointedByInHex(gtkw));
+ return gtkw;
+};
+
+/* consider using getRegisteredWinIdFromChromeWindow() if you only need the XID */
+firetray.Window.getWindowsFromChromeWindow = function(win) {
+ let baseWin = firetray.Handler.getWindowInterface(win, "nsIBaseWindow");
+ let nativeHandle = baseWin.nativeHandle; // Moz' private pointer to the GdkWindow
+ log.debug("nativeHandle="+nativeHandle);
+ let gtkWin, gdkWin;
+ if (nativeHandle) { // Gecko 17+
+ gdkWin = firetray.Window.getGdkWindowFromNativeHandle(nativeHandle);
+ gtkWin = firetray.Window.getGtkWindowFromGdkWindow(gdkWin);
+ } else {
+ gtkWin = firetray.Window.getGtkWindowFromChromeWindow(win);
+ gdkWin = firetray.Window.getGdkWindowFromGtkWindow(gtkWin);
+ }
+ let xid = firetray.Window.getXIDFromGdkWindow(gdkWin);
+ log.debug("XID="+xid);
+ return [baseWin, gtkWin, gdkWin, xid];
+};
+
+firetray.Window.unregisterWindowByXID = function(xid) {
+ if (!firetray.Handler.windows.hasOwnProperty(xid)) {
+ log.error("can't unregister unknown window "+xid);
+ return false;
+ }
+
+ firetray.Window.detachOnFocusInCallback(xid);
+ if (firetray.Handler.isChatEnabled() && firetray.Chat.initialized) {
+ firetray.Chat.detachSelectListeners(firetray.Handler.windows[xid].chromeWin);
+ }
+
+ if (!delete firetray.Handler.windows[xid])
+ throw new DeleteError();
+ firetray.Handler.gtkWindows.remove(xid);
+ firetray.Handler.gdkWindows.remove(xid);
+
+ firetray.PopupMenu.removeWindowItem(xid);
+
+ log.debug("window "+xid+" unregistered");
+ return true;
+};
+
+firetray.Window.show = function(xid) {
+ log.debug("show xid="+xid);
+
+ // try to restore previous state. TODO: z-order respected ?
+ firetray.Window.restorePositionAndSize(xid);
+ firetray.Window.restoreStates(xid);
+
+ // better visual effect if visibility set after restorePosition, but some
+ // WMs like compiz seem not to honor position setting if window not visible
+ firetray.Window.setVisibility(xid, true);
+
+ // after show
+ firetray.Window.restoreDesktop(xid);
+ if (firetray.Utils.prefService.getBoolPref('show_activates'))
+ firetray.Window.activate(xid);
+
+ firetray.PopupMenu.hideWindowItemAndSeparatorMaybe(xid);
+ firetray.Handler.showHideIcon();
+};
+
+/* FIXME: hiding windows should also hide child windows, like message windows
+ in Thunderbird */
+firetray.Window.hide = function(xid) {
+ log.debug("hide");
+
+ firetray.Window.savePositionAndSize(xid);
+ firetray.Window.saveStates(xid);
+ firetray.Window.saveDesktop(xid);
+
+ firetray.Window.setVisibility(xid, false);
+
+ firetray.PopupMenu.showWindowItem(xid);
+ firetray.Handler.showHideIcon();
+};
+
+firetray.Window.startupHide = function(xid) {
+ log.debug('startupHide: '+xid);
+
+ // also it seems cleaner, baseWin.visibility=false removes the possibility
+ // to restore the app by calling it from the command line. Not sure why...
+ firetray.Window.setVisibility(xid, false);
+
+ firetray.PopupMenu.showWindowItem(xid);
+ firetray.Handler.showHideIcon();
+};
+
+firetray.Window.savePositionAndSize = function(xid) {
+ let gx = {}, gy = {}, gwidth = {}, gheight = {};
+ firetray.Handler.windows[xid].baseWin.getPositionAndSize(gx, gy, gwidth, gheight);
+ firetray.Handler.windows[xid].savedX = gx.value;
+ firetray.Handler.windows[xid].savedY = gy.value;
+ firetray.Handler.windows[xid].savedWidth = gwidth.value;
+ firetray.Handler.windows[xid].savedHeight = gheight.value;
+ log.debug("save: gx="+gx.value+", gy="+gy.value+", gwidth="+gwidth.value+", gheight="+gheight.value);
+};
+
+firetray.Window.restorePositionAndSize = function(xid) {
+ if ("undefined" === typeof(firetray.Handler.windows[xid].savedX))
+ return; // windows[xid].saved* may not be initialized
+
+ log.debug("restore: x="+firetray.Handler.windows[xid].savedX+", y="+firetray.Handler.windows[xid].savedY+", w="+firetray.Handler.windows[xid].savedWidth+", h="+firetray.Handler.windows[xid].savedHeight);
+ firetray.Handler.windows[xid].baseWin.setPositionAndSize(
+ firetray.Handler.windows[xid].savedX,
+ firetray.Handler.windows[xid].savedY,
+ firetray.Handler.windows[xid].savedWidth,
+ firetray.Handler.windows[xid].savedHeight,
+ false); // repaint
+
+ ['savedX', 'savedX', 'savedWidth', 'savedHeight'].forEach(function(element) {
+ delete firetray.Handler.windows[xid][element];
+ });
+};
+
+firetray.Window.saveStates = function(xid) {
+ let winStates = firetray.Window.getXWindowStates(x11.Window(xid));
+ firetray.Handler.windows[xid].savedStates = winStates;
+ log.debug("save: windowStates="+winStates);
+};
+
+// NOTE: fluxbox bug probably: if hidden and restored iconified, then
+// switching to desktop de-iconifies it ?!
+firetray.Window.restoreStates = function(xid) {
+ let winStates = firetray.Handler.windows[xid].savedStates;
+ log.debug("restored WindowStates: " + winStates);
+
+ if (winStates & FIRETRAY_XWINDOW_HIDDEN) {
+ firetray.Handler.windows[xid].chromeWin.minimize();
+ log.debug("restored minimized");
+ }
+
+ /* we expect the WM to actually show the window *not* minimized once
+ restored */
+ if (firetray.Utils.prefService.getBoolPref('hides_on_minimize'))
+ // help prevent getting iconify event following show()
+ firetray.Handler.windows[xid].chromeWin.restore(); // nsIDOMChromeWindow.idl
+
+ if (winStates & FIRETRAY_XWINDOW_MAXIMIZED) {
+ firetray.Handler.windows[xid].chromeWin.maximize();
+ log.debug("restored maximized");
+ }
+
+ delete firetray.Handler.windows[xid].savedStates;
+};
+
+firetray.Window.saveDesktop = function(xid) {
+ if (!firetray.Utils.prefService.getBoolPref('remember_desktop'))
+ return;
+
+ let winDesktop = firetray.Window.getXWindowDesktop(x11.Window(xid));
+ firetray.Handler.windows[xid].savedDesktop = winDesktop;
+ log.debug("save: windowDesktop="+winDesktop);
+};
+
+firetray.Window.restoreDesktop = function(xid) {
+ if (!firetray.Utils.prefService.getBoolPref('remember_desktop'))
+ return;
+
+ let desktopDest = firetray.Handler.windows[xid].savedDesktop;
+ if (desktopDest === null || "undefined" === typeof(desktopDest)) return;
+
+ let dataSize = 1;
+ let data = ctypes.long(dataSize);
+ data[0] = desktopDest;
+ this.xSendClientMessgeEvent(xid, x11.current.Atoms._NET_WM_DESKTOP, data, dataSize);
+
+ log.debug("restored to desktop: "+desktopDest);
+ delete firetray.Handler.windows[xid].savedDesktop;
+};
+
+firetray.Window.getVisibility = function(xid) {
+ let gtkWidget = ctypes.cast(firetray.Handler.gtkWindows.get(xid), gtk.GtkWidget.ptr);
+ // nsIBaseWin.visibility always true
+ return gtk.gtk_widget_get_visible(gtkWidget);
+};
+
+firetray.Window.setVisibility = function(xid, visibility) {
+ log.debug("setVisibility="+visibility);
+ let gtkWidget = ctypes.cast(firetray.Handler.gtkWindows.get(xid), gtk.GtkWidget.ptr);
+ if (visibility)
+ gtk.gtk_widget_show_all(gtkWidget);
+ else
+ gtk.gtk_widget_hide(gtkWidget);
+};
+
+firetray.Window.xSendClientMessgeEvent = function(xid, atom, data, dataSize) {
+ let xev = new x11.XClientMessageEvent;
+ xev.type = x11.ClientMessage;
+ xev.window = x11.Window(xid);
+ xev.message_type = atom;
+ xev.format = 32;
+ for (let i=0; i<dataSize; ++i)
+ xev.data[i] = data[i];
+
+ let rootWin = x11.XDefaultRootWindow(x11.current.Display);
+ let propagate = false;
+ let mask = ctypes.long(x11.SubstructureNotifyMask|x11.SubstructureRedirectMask);
+ // fortunately, it's OK not to cast xev. ctypes.cast to a void_t doesn't work (length pb)
+ let status = x11.XSendEvent(x11.current.Display, rootWin, propagate, mask, xev.address());
+ // always returns 1 (BadRequest as a coincidence)
+};
+
+/**
+ * raises window on top and give focus.
+ */
+firetray.Window.activate = function(xid) {
+ // broken in KDE ?
+ gtk.gtk_window_present(firetray.Handler.gtkWindows.get(xid));
+ log.debug("window raised");
+};
+
+firetray.Window.setUrgency = function(xid, urgent) {
+ log.debug("setUrgency: "+urgent);
+ gtk.gtk_window_set_urgency_hint(firetray.Handler.gtkWindows.get(xid), urgent);
+};
+
+/**
+ * YOU MUST x11.XFree() THE VARIABLE RETURNED BY THIS FUNCTION
+ * @param xwin: a x11.Window
+ * @param prop: a x11.Atom
+ */
+firetray.Window.getXWindowProperties = function(xwin, prop) {
+ // infos returned by XGetWindowProperty() - FIXME: should be freed ?
+ let actual_type = new x11.Atom;
+ let actual_format = new ctypes.int;
+ let nitems = new ctypes.unsigned_long;
+ let bytes_after = new ctypes.unsigned_long;
+ let prop_value = new ctypes.unsigned_char.ptr;
+
+ let bufSize = XATOMS_EWMH_WM_STATES.length*ctypes.unsigned_long.size;
+ let offset = 0;
+ let res = x11.XGetWindowProperty(
+ x11.current.Display, xwin, prop, offset, bufSize, 0, x11.AnyPropertyType,
+ actual_type.address(), actual_format.address(), nitems.address(),
+ bytes_after.address(), prop_value.address());
+ log.debug("XGetWindowProperty res="+res+", actual_type="+actual_type.value+", actual_format="+actual_format.value+", bytes_after="+bytes_after.value+", nitems="+nitems.value);
+
+ if (!firetray.js.strEquals(res, x11.Success)) {
+ log.error("XGetWindowProperty failed");
+ return [null, null];
+ }
+ if (firetray.js.strEquals(actual_type.value, x11.None)) {
+ log.debug("property not found");
+ return [null, null];
+ }
+
+ log.debug("prop_value="+prop_value+", size="+prop_value.constructor.size);
+ /* If the returned format is 32, the property data will be stored as an
+ array of longs (which in a 64-bit application will be 64-bit values
+ that are padded in the upper 4 bytes). [man XGetWindowProperty] */
+ if (actual_format.value !== 32) {
+ log.error("unsupported format: "+actual_format.value);
+ }
+ log.debug("format OK");
+ var props = ctypes.cast(prop_value, ctypes.unsigned_long.array(nitems.value).ptr);
+ log.debug("props="+props+", size="+props.constructor.size);
+
+ return [props, nitems];
+};
+
+/**
+ * check the state of a window by its EWMH window state. This is more
+ * accurate than the chromeWin.windowState or the GdkWindowState which are
+ * based on WM_STATE. For instance, WM_STATE becomes 'Iconic' on virtual
+ * desktop change...
+ */
+firetray.Window.getXWindowStates = function(xwin) {
+ let winStates = 0;
+
+ let [propsFound, nitems] =
+ firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_STATE);
+ log.debug("propsFound, nitems="+propsFound+", "+nitems);
+ if (!propsFound) return 0;
+
+ let maximizedHorz = maximizedVert = false;
+ for (let i=0, len=nitems.value; i<len; ++i) {
+ log.debug("i: "+propsFound.contents[i]);
+ let currentProp = propsFound.contents[i];
+ if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_HIDDEN']))
+ winStates |= FIRETRAY_XWINDOW_HIDDEN;
+ else if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_HORZ']))
+ maximizedHorz = true;
+ else if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_VERT']))
+ maximizedVert = true;
+ }
+
+ if (maximizedHorz && maximizedVert)
+ winStates |= FIRETRAY_XWINDOW_MAXIMIZED;
+
+ x11.XFree(propsFound);
+
+ return winStates;
+};
+
+firetray.Window.getXWindowDesktop = function(xwin) {
+ let desktop = null;
+
+ let [propsFound, nitems] =
+ firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_DESKTOP);
+ log.debug("DESKTOP propsFound, nitems="+propsFound+", "+nitems);
+ if (!propsFound) return null;
+
+ if (firetray.js.strEquals(nitems.value, 0))
+ log.warn("desktop number not found");
+ else if (firetray.js.strEquals(nitems.value, 1))
+ desktop = propsFound.contents[0];
+ else
+ throw new RangeError("more than one desktop found");
+
+ x11.XFree(propsFound);
+
+ return desktop;
+};
+
+firetray.Window.checkSubscribedEventMasks = function(xid) {
+ let xWindowAttributes = new x11.XWindowAttributes;
+ let status = x11.XGetWindowAttributes(x11.current.Display, xid, xWindowAttributes.address());
+ log.debug("xWindowAttributes: "+xWindowAttributes);
+ let xEventMask = xWindowAttributes.your_event_mask;
+ let xEventMaskNeeded = x11.VisibilityChangeMask|x11.StructureNotifyMask|
+ x11.FocusChangeMask|x11.PropertyChangeMask;
+ log.debug("xEventMask="+xEventMask+" xEventMaskNeeded="+xEventMaskNeeded);
+ if ((xEventMask & xEventMaskNeeded) !== xEventMaskNeeded) {
+ log.error("missing mandatory event-masks"); // change with gdk_window_set_events()
+ }
+};
+
+firetray.Window.filterWindow = function(xev, gdkEv, data) {
+ if (!xev)
+ return gdk.GDK_FILTER_CONTINUE;
+
+ let xany = ctypes.cast(xev, x11.XAnyEvent.ptr);
+ let xid = xany.contents.window;
+
+ switch (xany.contents.type) {
+
+ case x11.MapNotify:
+ log.debug("MapNotify");
+ let gdkWinStateOnMap = gdk.gdk_window_get_state(firetray.Handler.gdkWindows.get(xid));
+ log.debug("gdkWinState="+gdkWinStateOnMap+" for xid="+xid);
+ let win = firetray.Handler.windows[xid];
+ if (firetray.Handler.appStarted && !win.visible) {
+ // when app hidden at startup, then called from command line without
+ // any argument (not through FireTray that is)
+ log.warn("window not visible, correcting visibility");
+ log.debug("visibleWindowsCount="+firetray.Handler.visibleWindowsCount);
+ }
+ break;
+
+ case x11.UnmapNotify: // for catching 'iconify'
+ log.debug("UnmapNotify");
+
+ let winStates = firetray.Window.getXWindowStates(xid);
+ let isHidden = winStates & FIRETRAY_XWINDOW_HIDDEN;
+ log.debug("winStates="+winStates+", isHidden="+isHidden);
+ // NOTE: Gecko 8.0 provides the 'sizemodechange' event, which comes once
+ // the window is minimized. i.e. preventDefault() or returning false won't
+ // prevent the event.
+ if (isHidden) {
+ log.debug("GOT ICONIFIED");
+ firetray.Handler.onMinimize(xid);
+ }
+ break;
+
+ // default:
+ // log.debug("xany.type="+xany.contents.type);
+ // break;
+ }
+
+ return gdk.GDK_FILTER_CONTINUE;
+};
+
+firetray.Window.startupFilter = function(xev, gdkEv, data) {
+ if (!xev)
+ return gdk.GDK_FILTER_CONTINUE;
+
+ let xany = ctypes.cast(xev, x11.XAnyEvent.ptr);
+ let xid = xany.contents.window;
+
+ // MapRequest already taken by window manager. Not sure we could be notified
+ // *before* the window is actually mapped, in order to minimize it before
+ // it's shown.
+ if (xany.contents.type === x11.MapNotify) {
+ gdk.gdk_window_remove_filter(firetray.Handler.gdkWindows.get(xid),
+ firetray.Handler.windows[xid].startupFilterCb, null);
+ if (firetray.Utils.prefService.getBoolPref('start_hidden')) {
+ log.debug("start_hidden");
+ firetray.Window.startupHide(xid);
+ }
+ }
+
+ return gdk.GDK_FILTER_CONTINUE;
+};
+
+firetray.Window.showAllWindowsAndActivate = function() {
+ let visibilityRate = firetray.Handler.visibleWindowsCount/firetray.Handler.windowsCount;
+ log.debug("visibilityRate="+visibilityRate);
+ if (visibilityRate < 1)
+ firetray.Handler.showAllWindows();
+
+ for(var key in firetray.Handler.windows); // FIXME: this is not the proper way for finding the last registered window !
+ firetray.Window.activate(key);
+};
+
+firetray.Window.attachOnFocusInCallback = function(xid) {
+ log.debug("attachOnFocusInCallback xid="+xid);
+ let callback = gtk.GCallbackWidgetFocusEvent_t(
+ firetray.Window.onFocusIn, null, FIRETRAY_CB_SENTINEL);
+ this.signals['focus-in'].callback[xid] = callback;
+ let handlerId = gobject.g_signal_connect(
+ firetray.Handler.gtkWindows.get(xid), "focus-in-event", callback, null);
+ log.debug("focus-in handler="+handlerId);
+ this.signals['focus-in'].handler[xid] = handlerId;
+};
+
+firetray.Window.detachOnFocusInCallback = function(xid) {
+ log.debug("detachOnFocusInCallback xid="+xid);
+ let gtkWin = firetray.Handler.gtkWindows.get(xid);
+ gobject.g_signal_handler_disconnect(
+ gtkWin,
+ gobject.gulong(this.signals['focus-in'].handler[xid])
+ );
+ delete this.signals['focus-in'].callback[xid];
+ delete this.signals['focus-in'].handler[xid];
+};
+
+// NOTE: fluxbox issues a FocusIn event when switching workspace
+// by hotkey, which means 2 FocusIn events when switching to a moz app :(
+// (http://sourceforge.net/tracker/index.php?func=detail&aid=3190205&group_id=35398&atid=413960)
+firetray.Window.onFocusIn = function(widget, event, data) {
+ log.debug("onFocusIn");
+ let xid = firetray.Window.getXIDFromGtkWidget(widget);
+ log.debug("xid="+xid);
+
+ firetray.Window.setUrgency(xid, false);
+
+ if (firetray.Handler.isChatEnabled() && firetray.Chat.initialized) {
+ firetray.Chat.stopGetAttentionMaybe(xid);
+ }
+
+ let stopPropagation = false;
+ return stopPropagation;
+};
+
+
+///////////////////////// firetray.Handler overriding /////////////////////////
+
+/** debug facility */
+firetray.Handler.dumpWindows = function() {
+ log.debug(firetray.Handler.windowsCount);
+ for (let winId in firetray.Handler.windows) log.info(winId+"="+firetray.Handler.gtkWindows.get(winId));
+};
+
+firetray.Handler.registerWindow = function(win) {
+ log.debug("register window");
+
+ // register
+ let [baseWin, gtkWin, gdkWin, xid] = firetray.Window.getWindowsFromChromeWindow(win);
+ this.windows[xid] = {};
+ this.windows[xid].chromeWin = win;
+ this.windows[xid].baseWin = baseWin;
+ Object.defineProperties(this.windows[xid], {
+ "visible": { get: function(){return firetray.Window.getVisibility(xid);} }
+ });
+ firetray.Window.checkSubscribedEventMasks(xid);
+ try {
+ this.gtkWindows.insert(xid, gtkWin);
+ this.gdkWindows.insert(xid, gdkWin);
+ firetray.PopupMenu.addWindowItem(xid);
+ } catch (x) {
+ if (x.name === "RangeError") // instanceof not working :-(
+ win.alert(x+"\n\nYou seem to have more than "+FIRETRAY_WINDOW_COUNT_MAX
+ +" windows open. This breaks FireTray and most probably "
+ +firetray.Handler.appName+".");
+ }
+ log.debug("window "+xid+" registered");
+ // NOTE: shouldn't be necessary to gtk_widget_add_events(gtkWin, gdk.GDK_ALL_EVENTS_MASK);
+
+ try {
+ // NOTE: we could try to catch the "delete-event" here and block
+ // delete_event_cb (in gtk2/nsWindow.cpp), but we prefer to use the
+ // provided 'close' JS event
+
+ this.windows[xid].filterWindowCb = gdk.GdkFilterFunc_t(
+ firetray.Window.filterWindow, null, FIRETRAY_CB_SENTINEL);
+ gdk.gdk_window_add_filter(gdkWin, this.windows[xid].filterWindowCb, null);
+ if (!firetray.Handler.appStarted) {
+ this.windows[xid].startupFilterCb = gdk.GdkFilterFunc_t(
+ firetray.Window.startupFilter, null, FIRETRAY_CB_SENTINEL);
+ gdk.gdk_window_add_filter(gdkWin, this.windows[xid].startupFilterCb, null);
+ }
+
+ firetray.Window.attachOnFocusInCallback(xid);
+ if (firetray.Handler.isChatEnabled() && firetray.Chat.initialized) {
+ firetray.Chat.attachSelectListeners(win);
+ }
+
+ } catch (x) {
+ log.error(x);
+ firetray.Window.unregisterWindowByXID(xid);
+ return null;
+ }
+
+ log.debug("AFTER"); firetray.Handler.dumpWindows();
+ return xid;
+};
+
+firetray.Handler.unregisterWindow = function(win) {
+ log.debug("unregister window");
+ let xid = firetray.Window.getRegisteredWinIdFromChromeWindow(win);
+ return firetray.Window.unregisterWindowByXID(xid);
+};
+
+firetray.Handler.showWindow = firetray.Window.show;
+firetray.Handler.hideWindow = firetray.Window.hide;
+
+firetray.Handler.showAllWindowsAndActivate = firetray.Window.showAllWindowsAndActivate;
+firetray.Handler.activateLastWindowCb = function(gtkStatusIcon, gdkEvent, userData) {
+ log.debug("activateLastWindowCb");
+
+ let gdkEventButton = ctypes.cast(gdkEvent, gdk.GdkEventButton.ptr);
+ if (gdkEventButton.contents.button === 2 && gdkEventButton.contents.type === gdk.GDK_BUTTON_PRESS) {
+ log.debug("MIDDLE CLICK");
+
+ firetray.Window.showAllWindowsAndActivate();
+ }
+
+ let stopPropagation = false;
+ return stopPropagation;
+};
+
+/* NOTE: gtk_window_is_active() not reliable, and _NET_ACTIVE_WINDOW may not
+ always be set before 'focus-in-event' (gnome-shell/mutter 3.4.1). */
+firetray.Handler.getActiveWindow = function() {
+ let gdkActiveWin = gdk.gdk_screen_get_active_window(gdk.gdk_screen_get_default()); // inspects _NET_ACTIVE_WINDOW
+ log.debug("gdkActiveWin="+gdkActiveWin);
+ if (firetray.js.strEquals(gdkActiveWin, 'GdkWindow.ptr(ctypes.UInt64("0x0"))'))
+ return null;
+ let activeWin = firetray.Window.getXIDFromGdkWindow(gdkActiveWin);
+ log.debug("ACTIVE_WINDOW="+activeWin);
+ return activeWin;
+};
+
+firetray.Handler.windowGetAttention = function(winId) {
+ firetray.Window.setUrgency(winId, true);
+};
+
+
+/**
+ * init X11 Display and handled XAtoms.
+ * Needs to be defined and called outside x11.jsm because: 1. gdk already
+ * imports x11, 2. there is no means to get the default Display solely with
+ * Xlib without opening one... :-(
+ */
+x11.init = function() {
+ if (!firetray.js.isEmpty(this.current))
+ return true; // init only once
+
+ this.current = {};
+ try {
+ let gdkDisplay = gdk.gdk_display_get_default();
+ this.current.Display = gdk.gdk_x11_display_get_xdisplay(gdkDisplay);
+ this.current.Atoms = {};
+ XATOMS.forEach(function(atomName, index, array) {
+ this.current.Atoms[atomName] = x11.XInternAtom(this.current.Display, atomName, 0);
+ log.debug("x11.current.Atoms."+atomName+"="+this.current.Atoms[atomName]);
+ }, this);
+ return true;
+ } catch (x) {
+ log.error(x);
+ return false;
+ }
+};
+x11.init();
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/firetray.git
More information about the Pkg-mozext-commits
mailing list