mirror of
https://github.com/moparisthebest/FireTray
synced 2024-12-21 21:48:47 -05:00
restore window to its previous virtual desktop
This commit is contained in:
parent
4c3a1cfc89
commit
5e4eaea614
@ -108,6 +108,10 @@ function gdk_defines(lib) {
|
||||
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.GdkWindow = ctypes.StructType("GdkWindow");
|
||||
this.GdkByteOrder = ctypes.int; // enum
|
||||
@ -176,6 +180,7 @@ function gdk_defines(lib) {
|
||||
{ "changed_mask": this.GdkWindowState },
|
||||
{ "new_window_state": this.GdkWindowState },
|
||||
]);
|
||||
this.GdkAtom = ctypes.StructType("GdkAtom");
|
||||
|
||||
this.GdkFilterFunc_t = ctypes.FunctionType(
|
||||
ctypes.default_abi, this.GdkFilterReturn,
|
||||
@ -225,6 +230,8 @@ function gdk_defines(lib) {
|
||||
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_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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,8 @@ const XATOMS_EWMH_WM_STATES = [
|
||||
"_NET_WM_STATE_FULLSCREEN", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_BELOW",
|
||||
"_NET_WM_STATE_DEMANDS_ATTENTION"
|
||||
];
|
||||
const XATOMS = XATOMS_ICCCM.concat(XATOMS_EWMH_WM_STATES).concat(XATOMS_EWMH_GENERAL);
|
||||
const XATOMS = XATOMS_ICCCM.concat(XATOMS_EWMH_WM_STATES)
|
||||
.concat(XATOMS_EWMH_GENERAL).concat(["CARDINAL"]);
|
||||
|
||||
|
||||
function x11_defines(lib) {
|
||||
@ -55,35 +56,43 @@ function x11_defines(lib) {
|
||||
}
|
||||
|
||||
// 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.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.PropertyDelete = 1;
|
||||
this.PropModeReplace = 0;
|
||||
this.PropModePrepend = 1;
|
||||
this.PropModeAppend = 2;
|
||||
// Event names
|
||||
this.DestroyNotify = 17;
|
||||
this.UnmapNotify = 18;
|
||||
this.MapNotify = 19;
|
||||
this.DestroyNotify = 17;
|
||||
this.UnmapNotify = 18;
|
||||
this.MapNotify = 19;
|
||||
this.PropertyNotify = 28;
|
||||
this.ClientMessage = 33;
|
||||
this.ClientMessage = 33;
|
||||
// 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 */
|
||||
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_ATOM = 4;
|
||||
this.XA_CARDINAL = 6;
|
||||
// Input Event Masks
|
||||
this.SubstructureNotifyMask = 1<<19;
|
||||
this.SubstructureRedirectMask = 1<<20;
|
||||
|
||||
this.Bool = ctypes.int;
|
||||
this.Status = ctypes.int;
|
||||
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;
|
||||
this.XEvent = ctypes.void_t; // union
|
||||
this.XAnyEvent = ctypes.StructType("XAnyEvent", [
|
||||
{ "type": ctypes.int },
|
||||
{ "serial": ctypes.unsigned_long },
|
||||
@ -116,6 +125,9 @@ function x11_defines(lib) {
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
if (!x11) {
|
||||
@ -149,67 +161,3 @@ typedef unsigned long Atom;
|
||||
typedef CARD32 Atom;
|
||||
# endif
|
||||
*/
|
||||
|
||||
/*
|
||||
XEvent {
|
||||
int type;
|
||||
XAnyEvent xany;
|
||||
XKeyEvent xkey;
|
||||
XButtonEvent xbutton;
|
||||
XMotionEvent xmotion;
|
||||
XCrossingEvent xcrossing;
|
||||
XFocusChangeEvent xfocus;
|
||||
XExposeEvent xexpose;
|
||||
XGraphicsExposeEvent xgraphicsexpose;
|
||||
XNoExposeEvent xnoexpose;
|
||||
XVisibilityEvent xvisibility;
|
||||
XCreateWindowEvent xcreatewindow;
|
||||
XDestroyWindowEvent xdestroywindow;
|
||||
XUnmapEvent xunmap;
|
||||
XMapEvent xmap;
|
||||
XMapRequestEvent xmaprequest;
|
||||
XReparentEvent xreparent;
|
||||
XConfigureEvent xconfigure;
|
||||
XGravityEvent xgravity;
|
||||
XResizeRequestEvent xresizerequest;
|
||||
XConfigureRequestEvent xconfigurerequest;
|
||||
XCirculateEvent xcirculate;
|
||||
XCirculateRequestEvent xcirculaterequest;
|
||||
XPropertyEvent xproperty;
|
||||
XSelectionClearEvent xselectionclear;
|
||||
XSelectionRequestEvent xselectionrequest;
|
||||
XSelectionEvent xselection;
|
||||
XColormapEvent xcolormap;
|
||||
XClientMessageEvent xclient;
|
||||
XMappingEvent xmapping;
|
||||
XErrorEvent xerror;
|
||||
XKeymapEvent xkeymap;
|
||||
XGenericEvent xgeneric;
|
||||
XGenericEventCookie xcookie;
|
||||
long pad[24];
|
||||
}
|
||||
|
||||
GdkEvent {
|
||||
GdkEventType type;
|
||||
GdkEventAny any;
|
||||
GdkEventExpose expose;
|
||||
GdkEventNoExpose no_expose;
|
||||
GdkEventVisibility visibility;
|
||||
GdkEventMotion motion;
|
||||
GdkEventButton button;
|
||||
GdkEventScroll scroll;
|
||||
GdkEventKey key;
|
||||
GdkEventCrossing crossing;
|
||||
GdkEventFocus focus_change;
|
||||
GdkEventConfigure configure;
|
||||
GdkEventProperty property;
|
||||
GdkEventSelection selection;
|
||||
GdkEventOwnerChange owner_change;
|
||||
GdkEventProximity proximity;
|
||||
GdkEventClient client;
|
||||
GdkEventDND dnd;
|
||||
GdkEventWindowState window_state;
|
||||
GdkEventSetting setting;
|
||||
GdkEventGrabBroken grab_broken;
|
||||
};
|
||||
*/
|
||||
|
@ -176,18 +176,20 @@ firetray.Window = {
|
||||
firetray.Handler.windows[xid].savedWidth,
|
||||
firetray.Handler.windows[xid].savedHeight,
|
||||
false); // repaint
|
||||
|
||||
['savedX', 'savedX', 'savedWidth', 'savedHeight'].forEach(function(element, index, array) {
|
||||
delete firetray.Handler.windows[xid][element];
|
||||
});
|
||||
},
|
||||
|
||||
saveWindowStates: function(xid) {
|
||||
// TODO: we may want to restore the window onto its original
|
||||
// desktop/monitor/etc.
|
||||
let winStates = firetray.Window.getXWindowStates(x11.Window(xid));
|
||||
firetray.Handler.windows[xid].savedWindowStates = winStates;
|
||||
firetray.Handler.windows[xid].savedStates = winStates;
|
||||
LOG("save: windowStates="+winStates);
|
||||
},
|
||||
|
||||
restoreWindowStates: function(xid) {
|
||||
let winStates = firetray.Handler.windows[xid].savedWindowStates;
|
||||
let winStates = firetray.Handler.windows[xid].savedStates;
|
||||
LOG("restored WindowStates: " + winStates);
|
||||
if (winStates & FIRETRAY_XWINDOW_MAXIMIZED) {
|
||||
firetray.Handler.windows[xid].chromeWin.maximize();
|
||||
@ -196,6 +198,36 @@ firetray.Window = {
|
||||
if (!hides_on_minimize && (winStates & FIRETRAY_XWINDOW_HIDDEN)) {
|
||||
firetray.Handler.windows[xid].chromeWin.minimize();
|
||||
}
|
||||
|
||||
delete firetray.Handler.windows[xid].savedStates;
|
||||
},
|
||||
|
||||
saveWindowDesktop: function(xid) {
|
||||
let winDesktop = firetray.Window.getXWindowDesktop(x11.Window(xid));
|
||||
firetray.Handler.windows[xid].savedDesktop = winDesktop;
|
||||
LOG("save: windowDesktop="+winDesktop);
|
||||
},
|
||||
|
||||
restoreWindowDesktop: function(xid) {
|
||||
let desktopDest = firetray.Handler.windows[xid].savedDesktop;
|
||||
if (desktopDest === null) return;
|
||||
|
||||
let xev = new x11.XClientMessageEvent;
|
||||
xev.type = x11.ClientMessage;
|
||||
xev.window = x11.Window(xid);
|
||||
xev.message_type = x11.current.Atoms._NET_WM_DESKTOP;
|
||||
xev.format = 32;
|
||||
xev.data[0] = desktopDest;
|
||||
|
||||
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)
|
||||
|
||||
LOG("restored to desktop: "+desktopDest);
|
||||
delete firetray.Handler.windows[xid].savedDesktop;
|
||||
},
|
||||
|
||||
/* KEPT FOR LATER USE
|
||||
@ -213,12 +245,12 @@ firetray.Window = {
|
||||
*/
|
||||
|
||||
/**
|
||||
* YOU MUST x11.XFree() THE VARIABLE RETURN BY THIS FUNCTION
|
||||
* YOU MUST x11.XFree() THE VARIABLE RETURNED BY THIS FUNCTION
|
||||
* @param xwin: a x11.Window
|
||||
* @param prop: a x11.Atom
|
||||
*/
|
||||
getXWindowProperties: function(xwin, prop) {
|
||||
// infos returned by XGetWindowProperty()
|
||||
// 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;
|
||||
@ -229,7 +261,8 @@ firetray.Window = {
|
||||
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());
|
||||
actual_type.address(), actual_format.address(), nitems.address(),
|
||||
bytes_after.address(), prop_value.address());
|
||||
LOG("XGetWindowProperty res="+res+", actual_type="+actual_type.value+", actual_format="+actual_format.value+", bytes_after="+bytes_after.value+", nitems="+nitems.value);
|
||||
|
||||
if (!strEquals(res, x11.Success)) {
|
||||
@ -264,19 +297,20 @@ firetray.Window = {
|
||||
getXWindowStates: function(xwin) {
|
||||
let winStates = 0;
|
||||
|
||||
let [propsFound, nitems] = firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_STATE);
|
||||
let [propsFound, nitems] =
|
||||
firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_STATE);
|
||||
LOG("propsFound, nitems="+propsFound+", "+nitems);
|
||||
if (!propsFound) return 0;
|
||||
|
||||
let maximizedHorz = maximizedVert = false;
|
||||
for (let i=0, len=nitems.value; i<len; ++i) {
|
||||
LOG("i: "+propsFound.contents[i]);
|
||||
let foundProp = propsFound.contents[i].toString();
|
||||
if (strEquals(propsFound.contents[i], x11.current.Atoms['_NET_WM_STATE_HIDDEN']))
|
||||
let currentProp = propsFound.contents[i];
|
||||
if (strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_HIDDEN']))
|
||||
winStates |= FIRETRAY_XWINDOW_HIDDEN;
|
||||
else if (strEquals(propsFound.contents[i], x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_HORZ']))
|
||||
else if (strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_HORZ']))
|
||||
maximizedHorz = true;
|
||||
else if (strEquals(propsFound.contents[i], x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_VERT']))
|
||||
else if (strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_VERT']))
|
||||
maximizedVert = true;
|
||||
}
|
||||
|
||||
@ -288,6 +322,25 @@ firetray.Window = {
|
||||
return winStates;
|
||||
},
|
||||
|
||||
getXWindowDesktop: function(xwin) {
|
||||
let desktop = null;
|
||||
|
||||
let [propsFound, nitems] =
|
||||
firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_DESKTOP);
|
||||
LOG("DESKTOP propsFound, nitems="+propsFound+", "+nitems);
|
||||
|
||||
if (strEquals(nitems.value, 0))
|
||||
WARN("desktop number not found");
|
||||
else if (strEquals(nitems.value, 1))
|
||||
desktop = propsFound.contents[0];
|
||||
else
|
||||
throw new RangeError("more than one desktop found");
|
||||
|
||||
x11.XFree(propsFound);
|
||||
|
||||
return desktop;
|
||||
},
|
||||
|
||||
filterWindow: function(xev, gdkEv, data) {
|
||||
if (!xev)
|
||||
return gdk.GDK_FILTER_CONTINUE;
|
||||
@ -301,7 +354,7 @@ firetray.Window = {
|
||||
case x11.UnmapNotify:
|
||||
LOG("UnmapNotify");
|
||||
let winStates = firetray.Window.getXWindowStates(xwin);
|
||||
let isHidden = winStates & FIRETRAY_XWINDOW_HIDDEN;
|
||||
let isHidden = winStates & FIRETRAY_XWINDOW_HIDDEN;
|
||||
LOG("winStates="+winStates+", isHidden="+isHidden);
|
||||
if (isHidden) {
|
||||
let hides_on_minimize = firetray.Utils.prefService.getBoolPref('hides_on_minimize');
|
||||
@ -436,8 +489,10 @@ firetray.Handler.showSingleWindow = function(xid) {
|
||||
|
||||
// try to restore previous state. TODO: z-order respected ?
|
||||
firetray.Window.restoreWindowPositionAndSize(xid);
|
||||
firetray.Window.restoreWindowStates(xid); // no need to be saved
|
||||
firetray.Window.restoreWindowStates(xid);
|
||||
firetray.Handler.windows[xid].baseWin.visibility = true; // show
|
||||
firetray.Window.restoreWindowDesktop(xid); // after show
|
||||
// TODO: we need want to restore to the original monitor (screen)
|
||||
|
||||
firetray.Handler.windows[xid].visibility = true;
|
||||
firetray.Handler.visibleWindowsCount += 1;
|
||||
@ -450,6 +505,7 @@ firetray.Handler.hideSingleWindow = function(xid) {
|
||||
|
||||
firetray.Window.saveWindowPositionAndSize(xid);
|
||||
firetray.Window.saveWindowStates(xid);
|
||||
firetray.Window.saveWindowDesktop(xid);
|
||||
firetray.Handler.windows[xid].baseWin.visibility = false; // hide
|
||||
|
||||
firetray.Handler.windows[xid].visibility = false;
|
||||
@ -477,9 +533,9 @@ firetray.Handler.showHideAllWindows = function(gtkStatusIcon, userData) {
|
||||
|
||||
/**
|
||||
* init X11 Display and handled XAtoms.
|
||||
* Needs to be defined and called outside x11.jsm because: gdk already imports
|
||||
* x11, and there is no means to get the default Display solely with Xlib
|
||||
* without opening one... :-(
|
||||
* 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 (!isEmpty(this.current))
|
||||
|
@ -1,5 +1,6 @@
|
||||
includes := $(shell pkg-config --libs --cflags gtk+-2.0)
|
||||
executables := gtk_icon_example trayicon hide xtypes x11XGetWindowProp window_state_event
|
||||
executables := gtk_icon_example trayicon hide xtypes x11XGetWindowProp \
|
||||
window_state_event xev_desktop
|
||||
|
||||
.PHONY: all
|
||||
all: $(executables)
|
||||
@ -25,3 +26,6 @@ x11XGetWindowProp: x11XGetWindowProp.c
|
||||
|
||||
window_state_event: window_state_event.c
|
||||
gcc $(includes) -o window_state_event window_state_event.c
|
||||
|
||||
xev_desktop: xev_desktop.c
|
||||
gcc -lXm -lXt -lX11 -o xev_desktop xev_desktop.c
|
||||
|
60
testing/xev_desktop.c
Normal file
60
testing/xev_desktop.c
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* cc -o memo memo.c -lXm -lXt -lX11 -I/usr/X11R6/include/
|
||||
-L/usr/X11R6/lib/
|
||||
*/
|
||||
|
||||
|
||||
#include <Xm/Xm.h>
|
||||
#include <X11/X.h>
|
||||
#include <Xm/Label.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void main(int argc, char **argv)
|
||||
{
|
||||
Widget shell, msg;
|
||||
XtAppContext app;
|
||||
XmString xmstr;
|
||||
XClientMessageEvent xev;
|
||||
Display *display;
|
||||
|
||||
shell = XtAppInitialize(&app, "Memo", NULL, 0, &argc, argv, NULL,
|
||||
NULL, 0);
|
||||
|
||||
xmstr = XmStringCreateLtoR("move window test",
|
||||
XmFONTLIST_DEFAULT_TAG);
|
||||
|
||||
msg =
|
||||
XtVaCreateManagedWidget(
|
||||
"message", xmLabelWidgetClass, shell, XmNlabelString, xmstr,
|
||||
NULL);
|
||||
|
||||
XmStringFree(xmstr);
|
||||
|
||||
XtRealizeWidget(shell);
|
||||
|
||||
|
||||
/* Now move the window to a different area */
|
||||
display = XtDisplay(shell);
|
||||
|
||||
|
||||
xev.type = ClientMessage;
|
||||
xev.window = XtWindow(shell);
|
||||
xev.message_type = XInternAtom(display, "_NET_WM_DESKTOP", False);
|
||||
xev.format = 32;
|
||||
|
||||
/* Force into desktop 2 */
|
||||
xev.data.l[0] = 2;
|
||||
|
||||
|
||||
XSendEvent(
|
||||
display,
|
||||
RootWindowOfScreen(XtScreen(shell)),
|
||||
False,
|
||||
SubstructureNotifyMask|SubstructureRedirectMask,
|
||||
(XEvent *) &xev);
|
||||
|
||||
|
||||
XtAppMainLoop(app);
|
||||
}
|
Loading…
Reference in New Issue
Block a user