restore window to its previous virtual desktop

This commit is contained in:
foudfou 2012-01-09 22:13:27 +01:00
parent 4c3a1cfc89
commit 5e4eaea614
5 changed files with 175 additions and 100 deletions

View File

@ -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);
}

View File

@ -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;
};
*/

View File

@ -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))

View File

@ -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
View 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);
}