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

* pass current window to onLoad() and onQuit() in order to register/unregister

window properly
* "delete-event" callbacks use GdkEventWindowState (not GdkEvent)
* begin implementation of window position/size/state save/restore

NOTE: this revision is *very* unstable. I think I should drop the GTK
"delete-event" handling and revert back to onClose().
This commit is contained in:
foudfou 2011-12-22 17:19:59 +01:00
parent 79e043e9a4
commit 8ec4d995b8
7 changed files with 200 additions and 41 deletions

View File

@ -16,7 +16,7 @@ if ("undefined" == typeof(firetray)) {
firetray.Main = {
onLoad: function(e) {
onLoad: function(win) {
// initialization code
this.strings = document.getElementById("firetray-strings");
@ -32,7 +32,7 @@ firetray.Main = {
}
let init = firetray.Handler.initialized || firetray.Handler.init();
firetray.Handler.registerWindow(window);
firetray.Handler.registerWindow(win);
// update unread messages count
if (firetray.Handler.inMailApp && firetray.Messaging.initialized)
@ -50,12 +50,12 @@ firetray.Main = {
return true;
},
onQuit: function(e) {
onQuit: function(win) {
// Remove observer
let that = this;
firetray.Utils.prefService.removeObserver("", that);
firetray.Handler.unregisterWindow(window);
firetray.Handler.unregisterWindow(win);
/* NOTE: don't firetray.Handler.initialized=false here, otherwise after a
window close, a new window will create a new handler (and hence, a new
@ -64,8 +64,7 @@ firetray.Main = {
},
/* GTK TEST
// TODO: prevent preceding warning about closing multiple tabs
// (browser.tabs.warnOnClose)
// TODO: prevent preceding warning about closing multiple tabs (browser.tabs.warnOnClose)
onClose: function(event) {
LOG('Firetray CLOSE');
let hides_on_close = firetray.Utils.prefService.getBoolPref('hides_on_close');
@ -90,13 +89,14 @@ firetray.Main = {
// https://developer.mozilla.org/en/Extensions/Performance_best_practices_in_extensions
// https://developer.mozilla.org/en/XUL_School/JavaScript_Object_Management.html
// https://developer.mozilla.org/en/Extensions/Performance_best_practices_in_extensions#Removing_Event_Listeners
let thatWindow = window;
window.addEventListener(
'load', function (e) {
removeEventListener('load', arguments.callee, true);
firetray.Main.onLoad(); },
firetray.Main.onLoad(thatWindow); },
false);
window.addEventListener(
'unload', function (e) {
removeEventListener('unload', arguments.callee, true);
firetray.Main.onQuit(); },
firetray.Main.onQuit(thatWindow); },
false);

View File

@ -113,7 +113,7 @@ firetray.Handler = {
setText: function(text, color) {},
setTooltip: function(localizedMessage) {},
setTooltipDefault: function() {},
showHideToTray: function() {},
showHideAllWindows: function() {},
registerWindow: function(win) {},
unregisterWindow: function(win) {},

View File

@ -61,8 +61,53 @@ function gdk_defines(lib) {
this.GDK_FILTER_TRANSLATE = 1;
this.GDK_FILTER_REMOVE = 2;
this.GdkWindowState = ctypes.int; // enum
this.GDK_WINDOW_STATE_ICONIFIED = 2;
this.GDK_WINDOW_STATE_MAXIMIZED = 4;
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.GdkWindow = ctypes.StructType("GdkWindow");
this.GdkByteOrder = ctypes.int; // enum
@ -124,6 +169,13 @@ function gdk_defines(lib) {
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.GdkFilterFunc_t = ctypes.FunctionType(
ctypes.default_abi, this.GdkFilterReturn,
@ -134,7 +186,7 @@ function gdk_defines(lib) {
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", 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_pixbuf_new_from_file", this.GdkPixbuf.ptr, gobject.gchar.ptr, glib.GError.ptr.ptr);
@ -169,6 +221,11 @@ function gdk_defines(lib) {
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);
}
if (!gdk) {

View File

@ -89,6 +89,7 @@ function gobject_defines(lib) {
this.guint32 = ctypes.uint32_t;
this.guint16 = ctypes.uint16_t;
this.gint = ctypes.int;
this.gint8 = ctypes.int8_t;
this.gchar = ctypes.char;
this.guchar = ctypes.unsigned_char;
this.gboolean = this.gint;

View File

@ -63,6 +63,9 @@ function gtk_defines(lib) {
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;
lib.lazy_bind("gtk_status_icon_new", this.GtkStatusIcon.ptr);
lib.lazy_bind("gtk_status_icon_set_from_file", ctypes.void_t,
@ -110,6 +113,15 @@ function gtk_defines(lib) {
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_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", gobject.gint, this.GtkWindow.ptr);
lib.lazy_bind("gtk_window_stick", gobject.gint, this.GtkWindow.ptr);
lib.lazy_bind("gtk_window_maximize", gobject.gint, this.GtkWindow.ptr);
lib.lazy_bind("gtk_window_fullscreen", gobject.gint, this.GtkWindow.ptr);
}
if (!gtk) {

View File

@ -47,8 +47,8 @@ firetray.StatusIcon = {
firetray.Handler.setTooltipDefault();
LOG("showHideToTray: "+firetray.Handler.hasOwnProperty("showHideToTray"));
firetray_iconActivateCb = gtk.GCallbackStatusIconActivate_t(firetray.Handler.showHideToTray);
LOG("showHideAllWindows: "+firetray.Handler.hasOwnProperty("showHideAllWindows"));
firetray_iconActivateCb = gtk.GCallbackStatusIconActivate_t(firetray.Handler.showHideAllWindows);
let res = gobject.g_signal_connect(firetray.StatusIcon.trayIcon, "activate", firetray_iconActivateCb, null);
LOG("g_connect activate="+res);

View File

@ -48,11 +48,13 @@ firetray.Handler.registerWindow = function(win) {
// register
let [gtkWin, gdkWin, xid] = firetray.Window.getWindowsFromChromeWindow(win);
/* NOTE: it should not be necessary to gtk_widget_add_events(gtkWin,
gdk.GDK_ALL_EVENTS_MASK); */
this.windows[xid] = {}; // windows.hasOwnProperty(xid) is true, remove with: delete windows[xid]
this.windows[xid] = {};
this.windows[xid].win = win;
this.windows[xid].gtkWin = gtkWin;
this.windows[xid].gdkWin = gdkWin;
LOG("window "+xid+" registered");
/* NOTE: it should not be necessary to gtk_widget_add_events(gtkWin,
gdk.GDK_ALL_EVENTS_MASK); */
try {
@ -60,20 +62,21 @@ firetray.Handler.registerWindow = function(win) {
"delete-event" */
let deleteEventId = gobject.g_signal_lookup("delete-event", gtk.gtk_window_get_type());
LOG("deleteEventId="+deleteEventId);
let mozDeleteEventCb = gobject.g_signal_handler_find(gtkWin, gobject.G_SIGNAL_MATCH_ID, deleteEventId, 0, null, null, null);
LOG("mozDeleteEventCb="+mozDeleteEventCb);
gobject.g_signal_handler_block(gtkWin, mozDeleteEventCb); // not _disconnect !
this.windows[xid].mozDeleteEventCb = mozDeleteEventCb; // FIXME: cb should be unblocked
let mozDeleteEventCbId = gobject.g_signal_handler_find(gtkWin, gobject.G_SIGNAL_MATCH_ID, deleteEventId, 0, null, null, null);
LOG("mozDeleteEventCbId="+mozDeleteEventCbId);
gobject.g_signal_handler_block(gtkWin, mozDeleteEventCbId); // not _disconnect !
this.windows[xid].mozDeleteEventCbId = mozDeleteEventCbId;
this.windows[xid].windowDeleteCb = gtk.GCallbackGenericEvent_t(firetray.Window.windowDelete);
res = gobject.g_signal_connect(gtkWin, "delete-event", that.windows[xid].windowDeleteCb, null);
LOG("g_connect delete-event="+res);
// NOTE: it'd be nice to pass the xid to g_signal_connect...
this.windows[xid].windowDeleteCbId = gobject.g_signal_connect(gtkWin, "delete-event", that.windows[xid].windowDeleteCb, null);
LOG("g_connect delete-event="+this.windows[xid].windowDeleteCbId);
/* we'll catch minimize events with Gtk:
http://stackoverflow.com/questions/8018328/what-is-the-gtk-event-called-when-a-window-minimizes */
this.windows[xid].windowStateCb = gtk.GCallbackGenericEvent_t(firetray.Window.windowState);
res = gobject.g_signal_connect(gtkWin, "window-state-event", this.windows[xid].windowStateCb, null);
LOG("g_connect window-state-event="+res);
this.windows[xid].windowStateCb = gtk.GCallbackWindowStateEvent_t(firetray.Window.windowState);
this.windows[xid].windowStateCbId = gobject.g_signal_connect(gtkWin, "window-state-event", this.windows[xid].windowStateCb, null);
LOG("g_connect window-state-event="+this.windows[xid].windowStateCbId);
} catch (x) {
this._unregisterWindowByXID(xid);
@ -87,30 +90,55 @@ firetray.Handler.registerWindow = function(win) {
firetray.Handler.unregisterWindow = function(win) {
LOG("unregister window");
let [gtkWin, gdkWin, xid] = firetray.Window.getWindowsFromChromeWindow(win);
return this._unregisterWindowByXID(xid);
try {
let [gtkWin, gdkWin, xid] = firetray.Window.getWindowsFromChromeWindow(win);
// unblock Moz original delete-event handler
gobject.g_signal_handler_disconnect(gtkWin, this.windows[xid].windowDeleteCbId);
gobject.g_signal_handler_unblock(gtkWin, this.windows[xid].mozDeleteEventCbId);
return this._unregisterWindowByXID(xid);
} catch (x) {
ERROR(x);
}
return null;
};
firetray.Handler._unregisterWindowByXID = function(xid) {
try {
if (this.windows.hasOwnProperty(xid))
delete this.windows[xid];
else {
ERROR("can't unregister unknown window "+xid);
return false;
}
return true;
};
firetray.Handler.showHideToTray = function(gtkStatusIcon, userData) {
LOG("showHideToTray: "+userData);
for (let xid in firetray.Handler.windows) {
LOG(xid);
try {
gdk.gdk_window_show(firetray.Handler.windows[xid].gdkWin);
} catch (x) {
ERROR(x);
}
LOG("window "+xid+" unregistered");
return true;
};
firetray.Handler.showSingleWindow = function(xid) {
try {
// keep z-order - and try to restore previous state
LOG("gdkWin="+firetray.Handler.windows[xid].gdkWin);
gdk.gdk_window_show_unraised(firetray.Handler.windows[xid].gdkWin);
// need to restore *after* showing for correction
// firetray.Window._restoreWindowPositionSizeState(xid);
} catch (x) {
ERROR(x);
}
};
firetray.Handler.showHideAllWindows = function(gtkStatusIcon, userData) {
LOG("showHideAllWindows: "+userData);
// NOTE: showHideAllWindows being a callback, we need to use 'firetray.Handler'
// explicitely instead of 'this'
for (let xid in firetray.Handler.windows) {
LOG("show xid="+xid);
firetray.Handler.showSingleWindow(xid);
}
let stopPropagation = true;
@ -136,6 +164,7 @@ firetray.Window = {
// Tag the base window
let oldTitle = baseWindow.title;
LOG("oldTitle="+oldTitle);
baseWindow.title = Services2.uuid.generateUUID().toString();
try {
@ -205,6 +234,16 @@ firetray.Window = {
return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
},
getXIDFromGtkWidget: function(gtkWid) {
try {
let gdkWin = gtk.gtk_widget_get_window(gtkWid);
return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
} catch (x) {
ERROR(x);
}
return null;
},
getWindowsFromChromeWindow: function(win) {
let gtkWin = firetray.Window.getGtkWindowHandle(win);
LOG("gtkWin="+gtkWin);
@ -217,18 +256,68 @@ firetray.Window = {
windowDelete: function(gtkWidget, gdkEv, userData){
LOG("gtk_widget_hide: "+gtkWidget+", "+gdkEv+", "+userData);
try{
let xid = firetray.Window.getXIDFromGtkWidget(gtkWidget);
LOG("windowDelete XID="+xid);
try {
firetray.Window._saveWindowPositionSizeState(xid);
// hide window - NOTE: we don't use BaseWindow.visibility to have full
// control
let gdkWin = firetray.Window.getGdkWindowFromGtkWindow(gtkWidget);
gdk.gdk_window_hide(gdkWin);
} catch (x) {
ERROR(x);
}
let stopPropagation = true;
return stopPropagation;
},
windowState: function(gtkWidget, gdkEv, userData){
LOG("window-state-event");
_saveWindowPositionSizeState: function(xid) {
let gdkWin = firetray.Handler.windows[xid].gdkWin;
try {
let gx = new gobject.gint; let gy = new gobject.gint;
// gtk.gtk_window_get_position(gtkWin, gx.address(), gy.address());
gdk.gdk_window_get_position(gdkWin, gx.address(), gy.address());
let gwidth = new gobject.gint; let gheight = new gobject.gint;
// gtk.gtk_window_get_size(gtkWin, gwidth.address(), gheight.address());
gdk.gdk_drawable_get_size(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr), gwidth.address(), gheight.address());
let windowState = gdk.gdk_window_get_state(firetray.Handler.windows[xid].gdkWin);
LOG("gx="+gx+", gy="+gy+", gwidth="+gwidth+", gheight="+gheight+", windowState="+windowState);
firetray.Handler.windows[xid].savedX = gx;
firetray.Handler.windows[xid].savedY = gy;
firetray.Handler.windows[xid].savedWidth = gwidth;
firetray.Handler.windows[xid].savedHeight = gheight;
firetray.Handler.windows[xid].savedState = windowState;
} catch (x) {
ERROR(x);
}
},
_restoreWindowPositionSizeState: function(xid) {
let gdkWin = firetray.Handler.windows[xid].gdkWin;
if (!firetray.Handler.windows[xid].savedX)
return; // windows[xid].saved* may not be initialized
LOG("restore gdkWin: "+gdkWin+", x="+firetray.Handler.windows[xid].savedX+", y="+firetray.Handler.windows[xid].savedY+", w="+firetray.Handler.windows[xid].savedWidth+", h="+firetray.Handler.windows[xid].savedHeight);
try {
gdk.gdk_window_move_resize(gdkWin,
firetray.Handler.windows[xid].savedX,
firetray.Handler.windows[xid].savedY,
firetray.Handler.windows[xid].savedWidth,
firetray.Handler.windows[xid].savedHeight);
// firetray.Handler.windows[xid].savedState
} catch (x) {
ERROR(x);
}
},
windowState: function(gtkWidget, gdkEventState, userData){
// LOG("window-state-event");
// if(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED){
let stopPropagation = true;
return stopPropagation;
}