summaryrefslogtreecommitdiff
path: root/src/mcompositemanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mcompositemanager.cpp')
-rw-r--r--src/mcompositemanager.cpp2463
1 files changed, 2463 insertions, 0 deletions
diff --git a/src/mcompositemanager.cpp b/src/mcompositemanager.cpp
new file mode 100644
index 0000000..1bd6649
--- /dev/null
+++ b/src/mcompositemanager.cpp
@@ -0,0 +1,2463 @@
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui@nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui@nokia.com.
+**
+** This library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation
+** and appearing in the file LICENSE.LGPL included in the packaging
+** of this file.
+**
+****************************************************************************/
+
+#include "mtexturepixmapitem.h"
+#include "mtexturepixmapitem_p.h"
+#include "mcompositemanager.h"
+#include "mcompositemanager_p.h"
+#include "mcompositescene.h"
+#include "msimplewindowframe.h"
+#include "mdecoratorframe.h"
+#include <mrmiserver.h>
+
+#include <QX11Info>
+#include <QByteArray>
+#include <QVector>
+
+#include <X11/Xutil.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/Xatom.h>
+#include <X11/Xmd.h>
+
+#ifdef GLES2_VERSION
+#include <mce/dbus-names.h>
+#include <mce/mode-names.h>
+#endif
+
+#define TRANSLUCENT 0xe0000000
+#define OPAQUE 0xffffffff
+
+/*
+ The reason why we have to look at the entire redirected buffers is that we
+ never know if a window is physically visible or not when compositing mode is
+ toggled e.g. What if a window is partially translucent and how do we know
+ that the window beneath it can be physically seen by the user and the window
+ beneath that window and so on?
+
+ But this is mitigated anyway by the fact that the items representing those
+ buffers know whether they are redirected or not and will not switch to
+ another state if they are already in that state. So the overhead of freeing
+ and allocating EGL resources for the entire buffers is low.
+ */
+
+// own log for catching bugs
+#define _log(txt, args... ) { FILE *out; out = fopen("/tmp/mcompositor.log", "a"); if(out) { fprintf(out, "" txt, ##args ); fclose(out); } }
+
+class MCompAtoms
+{
+public:
+
+ // note that this enum is ordered and presents
+ // the depth ordering of different window types
+ enum Type {
+ DESKTOP = 0,
+ NORMAL,
+ FRAMELESS,
+ DOCK,
+ INPUT,
+ ABOVE,
+ NOTIFICATION,
+ DECORATOR,
+ UNKNOWN
+ };
+
+ enum Atoms {
+ // window manager
+ WM_PROTOCOLS,
+ WM_DELETE_WINDOW,
+ WM_TAKE_FOCUS,
+
+ // window types
+ _NET_SUPPORTED,
+ _NET_SUPPORTING_WM_CHECK,
+ _NET_WM_NAME,
+ _NET_WM_WINDOW_TYPE,
+ _NET_WM_WINDOW_TYPE_DESKTOP,
+ _NET_WM_WINDOW_TYPE_NORMAL,
+ _NET_WM_WINDOW_TYPE_DOCK,
+ _NET_WM_WINDOW_TYPE_INPUT,
+ _NET_WM_WINDOW_TYPE_NOTIFICATION,
+ _NET_WM_WINDOW_TYPE_DIALOG,
+ _NET_WM_STATE_ABOVE,
+ _NET_WM_STATE_SKIP_TASKBAR,
+ _NET_WM_STATE_FULLSCREEN,
+ _KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
+
+ // window properties
+ _NET_WM_WINDOW_OPACITY,
+ _NET_WM_STATE,
+ _NET_WM_ICON_GEOMETRY,
+ WM_STATE,
+
+ // misc
+ _NET_WM_PID,
+ _NET_WM_PING,
+
+ // root messages
+ _NET_ACTIVE_WINDOW,
+ _NET_CLOSE_WINDOW,
+ _NET_CLIENT_LIST,
+ _NET_CLIENT_LIST_STACKING,
+ WM_CHANGE_STATE,
+
+ // DUI-specific
+ _MEEGOTOUCH_DECORATOR_WINDOW,
+ _DUI_STATUSBAR_OVERLAY,
+ _DUI_GLOBAL_ALPHA,
+
+#ifdef WINDOW_DEBUG
+ _DUI_WM_INFO,
+ _DUI_WM_WINDOW_ZVALUE,
+ _DUI_WM_WINDOW_COMPOSITED_VISIBLE,
+ _DUI_WM_WINDOW_COMPOSITED_INVISIBLE,
+ _DUI_WM_WINDOW_DIRECT_VISIBLE,
+ _DUI_WM_WINDOW_DIRECT_INVISIBLE,
+#endif
+
+ ATOMS_TOTAL
+ };
+ static MCompAtoms *instance();
+ Type windowType(Window w);
+ bool isDecorator(Window w);
+ bool statusBarOverlayed(Window w);
+ int getPid(Window w);
+ long getWmState(Window w);
+ Atom getState(Window w);
+ QRectF iconGeometry(Window w);
+ QVector<Atom> netWmStates(Window w);
+ unsigned int get_opacity_prop(Display *dpy, Window w, unsigned int def);
+ double get_opacity_percent(Display *dpy, Window w, double def);
+ int globalAlphaFromWindow(Window w);
+
+ Atom getAtom(const unsigned int name);
+ Atom getType(Window w);
+
+ static Atom atoms[ATOMS_TOTAL];
+
+private:
+ explicit MCompAtoms();
+ static MCompAtoms *d;
+
+ int intValueProperty(Window w, Atom property);
+ Atom getAtom(Window w, Atoms atomtype);
+
+ Display *dpy;
+};
+
+#define ATOM(t) MCompAtoms::instance()->getAtom(MCompAtoms::t)
+Atom MCompAtoms::atoms[MCompAtoms::ATOMS_TOTAL];
+Window MCompositeManagerPrivate::stack[TOTAL_LAYERS];
+MCompAtoms *MCompAtoms::d = 0;
+static bool hasDock = false;
+static QRect availScreenRect = QRect();
+
+// temporary launch indicator. will get replaced later
+static QGraphicsTextItem *launchIndicator = 0;
+
+MCompAtoms *MCompAtoms::instance()
+{
+ if (!d)
+ d = new MCompAtoms();
+ return d;
+}
+
+MCompAtoms::MCompAtoms()
+{
+ const char *atom_names[] = {
+ "WM_PROTOCOLS",
+ "WM_DELETE_WINDOW",
+ "WM_TAKE_FOCUS",
+
+ "_NET_SUPPORTED",
+ "_NET_SUPPORTING_WM_CHECK",
+ "_NET_WM_NAME",
+ "_NET_WM_WINDOW_TYPE",
+ "_NET_WM_WINDOW_TYPE_DESKTOP",
+ "_NET_WM_WINDOW_TYPE_NORMAL",
+ "_NET_WM_WINDOW_TYPE_DOCK",
+ "_NET_WM_WINDOW_TYPE_INPUT",
+ "_NET_WM_WINDOW_TYPE_NOTIFICATION",
+ "_NET_WM_WINDOW_TYPE_DIALOG",
+
+ // window states
+ "_NET_WM_STATE_ABOVE",
+ "_NET_WM_STATE_SKIP_TASKBAR",
+ "_NET_WM_STATE_FULLSCREEN",
+ // uses the KDE standard for frameless windows
+ "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE",
+
+ "_NET_WM_WINDOW_OPACITY",
+ "_NET_WM_STATE",
+ "_NET_WM_ICON_GEOMETRY",
+ "WM_STATE",
+
+ // misc
+ "_NET_WM_PID",
+ "_NET_WM_PING",
+
+ // root messages
+ "_NET_ACTIVE_WINDOW",
+ "_NET_CLOSE_WINDOW",
+ "_NET_CLIENT_LIST",
+ "_NET_CLIENT_LIST_STACKING",
+ "WM_CHANGE_STATE",
+
+ "_MEEGOTOUCH_DECORATOR_WINDOW",
+ // TODO: remove this when statusbar in-scene approach is done
+ "_DUI_STATUSBAR_OVERLAY",
+ "_DUI_GLOBAL_ALPHA",
+
+#ifdef WINDOW_DEBUG
+ // custom properties for CITA
+ "_DUI_WM_INFO",
+ "_DUI_WM_WINDOW_ZVALUE",
+ "_DUI_WM_WINDOW_COMPOSITED_VISIBLE",
+ "_DUI_WM_WINDOW_COMPOSITED_INVISIBLE",
+ "_DUI_WM_WINDOW_DIRECT_VISIBLE",
+ "_DUI_WM_WINDOW_DIRECT_INVISIBLE",
+#endif
+ };
+
+ Q_ASSERT(sizeof(atom_names) == ATOMS_TOTAL);
+
+ dpy = QX11Info::display();
+
+ if (!XInternAtoms(dpy, (char **)atom_names, ATOMS_TOTAL, False, atoms))
+ qCritical("XInternAtoms failed");
+
+ XChangeProperty(dpy, QX11Info::appRootWindow(), atoms[_NET_SUPPORTED],
+ XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms,
+ ATOMS_TOTAL);
+}
+
+/* FIXME Workaround for bug NB#161282 */
+static bool is_desktop_window(Window w, Atom type = 0)
+{
+ Atom a;
+ if (!type)
+ a = MCompAtoms::instance()->getType(w);
+ else
+ a = type;
+ if (a == ATOM(_NET_WM_WINDOW_TYPE_DESKTOP))
+ return true;
+ XTextProperty textp;
+ if (!XGetWMName(QX11Info::display(), w, &textp))
+ return false;
+ if (strcmp((const char *)textp.value, "duihome") == 0
+ && a == ATOM(_NET_WM_WINDOW_TYPE_NORMAL)) {
+ return true;
+ }
+ return false;
+}
+
+/* FIXME: workaround for bug NB#161629 */
+static bool is_desktop_dock(Window w, Atom type = 0)
+{
+ Atom a;
+ if (!type)
+ a = MCompAtoms::instance()->getType(w);
+ else
+ a = type;
+ if (a != ATOM(_NET_WM_WINDOW_TYPE_DOCK))
+ return false;
+ /* // WMName of the dock is unstable, match all docks...
+ XTextProperty textp;
+ if (!XGetWMName(QX11Info::display(), w, &textp))
+ return false;
+ if (strcmp((const char *)textp.value, "duihome") == 0) {
+ return true;
+ }
+ */
+ return true;
+}
+
+MCompAtoms::Type MCompAtoms::windowType(Window w)
+{
+ // freedesktop.org window type
+ Atom a = getType(w);
+ if (is_desktop_window(w, a))
+ return DESKTOP;
+ else if (a == atoms[_NET_WM_WINDOW_TYPE_NORMAL])
+ return NORMAL;
+ else if (a == atoms[_NET_WM_WINDOW_TYPE_DOCK])
+ return DOCK;
+ else if (a == atoms[_NET_WM_WINDOW_TYPE_INPUT])
+ return INPUT;
+ else if (a == atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION])
+ return NOTIFICATION;
+ else if (a == atoms[_KDE_NET_WM_WINDOW_TYPE_OVERRIDE])
+ return FRAMELESS;
+
+ // fix this later. this value always gets returned for transient_for
+ // windows such as menus and dialogs which shouldn't be the case
+ return UNKNOWN;
+}
+
+bool MCompAtoms::isDecorator(Window w)
+{
+ return (intValueProperty(w, atoms[_MEEGOTOUCH_DECORATOR_WINDOW]) == 1);
+}
+
+// Remove this when statusbar in-scene approach is done
+bool MCompAtoms::statusBarOverlayed(Window w)
+{
+ return (intValueProperty(w, atoms[_DUI_STATUSBAR_OVERLAY]) == 1);
+}
+
+int MCompAtoms::getPid(Window w)
+{
+ return intValueProperty(w, atoms[_NET_WM_PID]);
+}
+
+Atom MCompAtoms::getState(Window w)
+{
+ return getAtom(w, _NET_WM_STATE);
+}
+
+QRectF MCompAtoms::iconGeometry(Window w)
+{
+ Atom actual;
+ int format;
+ unsigned long n, left;
+ unsigned char *data;
+ int result = XGetWindowProperty(QX11Info::display(), w, atoms[_NET_WM_ICON_GEOMETRY], 0L, 4L, False,
+ XA_CARDINAL, &actual, &format,
+ &n, &left, &data);
+ if (result == Success && data != NULL) {
+ unsigned long *geom = (unsigned long *) data;
+ QRectF r(geom[0], geom[1], geom[2], geom[3]);
+ XFree((void *) data);
+ return r;
+
+ }
+ return QRectF(); // empty
+}
+
+QVector<Atom> MCompAtoms::netWmStates(Window w)
+{
+ QVector<Atom> ret;
+
+ Atom actual;
+ int format;
+ unsigned long n, left;
+ unsigned char *data;
+ int result = XGetWindowProperty(QX11Info::display(), w, atoms[_NET_WM_STATE], 0, 0,
+ False, XA_ATOM, &actual, &format,
+ &n, &left, &data);
+ if (result == Success && actual == XA_ATOM && format == 32) {
+ ret.resize(left / 4);
+ XFree((void *) data);
+
+ if (XGetWindowProperty(QX11Info::display(), w, atoms[_NET_WM_STATE], 0,
+ ret.size(), False, XA_ATOM, &actual, &format,
+ &n, &left, &data) != Success) {
+ ret.clear();
+ } else if (n != (ulong)ret.size())
+ ret.resize(n);
+
+ if (!ret.isEmpty())
+ memcpy(ret.data(), data, ret.size() * sizeof(Atom));
+
+ XFree((void *) data);
+ }
+
+ return ret;
+}
+
+long MCompAtoms::getWmState(Window w)
+{
+ Atom actual;
+ int format;
+ unsigned long n, left;
+ unsigned char *data = 0, state = WithdrawnState;
+ int result = XGetWindowProperty(QX11Info::display(), w,
+ atoms[WM_STATE], 0L, 2L, False,
+ atoms[WM_STATE], &actual, &format,
+ &n, &left, &data);
+ if (result == Success && data != NULL) {
+ state = *data;
+ if (data)
+ XFree((void *)data);
+ }
+
+ return state;
+}
+
+unsigned int MCompAtoms::get_opacity_prop(Display *dpy, Window w, unsigned int def)
+{
+ Q_UNUSED(dpy);
+ Atom actual;
+ int format;
+ unsigned long n, left;
+
+ unsigned char *data;
+ int result = XGetWindowProperty(QX11Info::display(), w, atoms[_NET_WM_WINDOW_OPACITY], 0L, 1L, False,
+ XA_CARDINAL, &actual, &format,
+ &n, &left, &data);
+ if (result == Success && data != NULL) {
+ unsigned int i;
+ memcpy(&i, data, sizeof(unsigned int));
+ XFree((void *) data);
+ return i;
+ }
+ return def;
+}
+
+double MCompAtoms::get_opacity_percent(Display *dpy, Window w, double def)
+{
+ unsigned int opacity = get_opacity_prop(dpy, w,
+ (unsigned int)(OPAQUE * def));
+ return opacity * 1.0 / OPAQUE;
+}
+
+int MCompAtoms::globalAlphaFromWindow(Window w)
+{
+ Atom actual;
+ int format;
+ unsigned long n, left;
+
+ unsigned char *data;
+ int result = XGetWindowProperty(QX11Info::display(), w, atoms[_DUI_GLOBAL_ALPHA], 0L, 1L, False,
+ XA_CARDINAL, &actual, &format,
+ &n, &left, &data);
+ if (result == Success && data != NULL) {
+ unsigned int i;
+ memcpy(&i, data, sizeof(unsigned int));
+ XFree((void *) data);
+ double opacity = i * 1.0 / OPAQUE;
+ return (opacity * 255);
+ }
+
+ return 255;
+}
+
+Atom MCompAtoms::getAtom(const unsigned int name)
+{
+ return atoms[name];
+}
+
+int MCompAtoms::intValueProperty(Window w, Atom property)
+{
+ Atom actual;
+ int format;
+ unsigned long n, left;
+ unsigned char *data;
+
+ int result = XGetWindowProperty(QX11Info::display(), w, property, 0L, 1L, False,
+ XA_CARDINAL, &actual, &format,
+ &n, &left, &data);
+ if (result == Success && data != None) {
+ int p = *((unsigned long *)data);
+ XFree((void *)data);
+ return p;
+ }
+
+ return 0;
+}
+
+Atom MCompAtoms::getType(Window w)
+{
+ Atom t = getAtom(w, _NET_WM_WINDOW_TYPE);
+ if (t)
+ return t;
+ return atoms[_NET_WM_WINDOW_TYPE_NORMAL];
+}
+
+Atom MCompAtoms::getAtom(Window w, Atoms atomtype)
+{
+ Atom actual;
+ int format;
+ unsigned long n, left;
+ unsigned char *data;
+
+ int result = XGetWindowProperty(QX11Info::display(), w, atoms[atomtype], 0L, 1L, False,
+ XA_ATOM, &actual, &format,
+ &n, &left, &data);
+ if (result == Success && data != None) {
+ Atom a;
+ memcpy(&a, data, sizeof(Atom));
+ XFree((void *) data);
+ return a;
+ }
+ return 0;
+}
+
+static Window transient_for(Window window)
+{
+ Window transient_for = 0;
+ XGetTransientForHint(QX11Info::display(), window, &transient_for);
+ return transient_for;
+}
+
+static void skiptaskbar_wm_state(int toggle, Window window)
+{
+ Atom skip = ATOM(_NET_WM_STATE_SKIP_TASKBAR);
+ MCompAtoms *atom = MCompAtoms::instance();
+ QVector<Atom> states = atom->netWmStates(window);
+ bool update_root = false;
+
+ switch (toggle) {
+ case 0: {
+ int i = states.indexOf(skip);
+ if (i != -1) {
+ states.remove(i);
+ XChangeProperty(QX11Info::display(), window,
+ ATOM(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) states.data(), states.size());
+ update_root = true;
+ }
+ } break;
+ case 1: {
+ states.append(skip);
+ if (!states.isEmpty()) {
+ XChangeProperty(QX11Info::display(), window,
+ ATOM(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) states.data(), states.size());
+ update_root = true;
+ }
+ } break;
+ default: break;
+ }
+
+ if (update_root) {
+ XPropertyEvent p;
+ p.send_event = True;
+ p.display = QX11Info::display();
+ p.type = PropertyNotify;
+ p.window = RootWindow(QX11Info::display(), 0);
+ p.atom = ATOM(_NET_CLIENT_LIST);
+ p.state = PropertyNewValue;
+ p.time = CurrentTime;
+ XSendEvent(QX11Info::display(), p.window, False, PropertyChangeMask,
+ (XEvent *)&p);
+ }
+}
+
+static bool need_geometry_modify(Window window)
+{
+ MCompAtoms *atom = MCompAtoms::instance();
+
+ if ((atom->getState(window) == ATOM(_NET_WM_STATE_FULLSCREEN)) ||
+ (atom->statusBarOverlayed(window)))
+ return false;
+
+ return true;
+}
+
+static void fullscreen_wm_state(MCompositeManagerPrivate *priv,
+ int toggle, Window window)
+{
+ Atom fullscreen = ATOM(_NET_WM_STATE_FULLSCREEN);
+ Display *dpy = QX11Info::display();
+ MCompAtoms *atom = MCompAtoms::instance();
+ QVector<Atom> states = atom->netWmStates(window);
+ int i = states.indexOf(fullscreen);
+
+ switch (toggle) {
+ case 0: {
+ if (i != -1) {
+ states.remove(i);
+ XChangeProperty(QX11Info::display(), window,
+ ATOM(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) states.data(), states.size());
+ }
+
+ MCompositeWindow *win = MCompositeWindow::compositeWindow(window);
+ if (win && need_geometry_modify(window) && !availScreenRect.isEmpty()) {
+ QRect r = availScreenRect;
+ XMoveResizeWindow(dpy, window, r.x(), r.y(), r.width(), r.height());
+ }
+
+ priv->checkStacking(false);
+ } break;
+ case 1: {
+ if (i != -1 || states.isEmpty()) {
+ states.append(fullscreen);
+ XChangeProperty(QX11Info::display(), window,
+ ATOM(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) states.data(), states.size());
+ }
+
+ int xres = ScreenOfDisplay(dpy, DefaultScreen(dpy))->width;
+ int yres = ScreenOfDisplay(dpy, DefaultScreen(dpy))->height;
+ XMoveResizeWindow(dpy, window, 0, 0, xres, yres);
+ /* FIXME: is raising a fullscreen window necessary? We could
+ * have several fullscreen applications open at the same time. */
+ priv->activateWindow(window, CurrentTime);
+ } break;
+ default: break;
+ }
+}
+
+#ifdef GLES2_VERSION
+// This is a Harmattan hardware-specific feature to maniplute the graphics overlay
+static void toggle_global_alpha_blend(unsigned int state, int manager = 0)
+{
+ FILE *out;
+ char path[256];
+
+ snprintf(path, 256, "/sys/devices/platform/omapdss/manager%d/alpha_blending_enabled", manager);
+
+ out = fopen(path, "w");
+
+ if (out) {
+ fprintf(out, "%d", state);
+ fclose(out);
+ }
+}
+
+static void set_global_alpha(unsigned int plane, unsigned int level)
+{
+ FILE *out;
+ char path[256];
+
+ snprintf(path, 256, "/sys/devices/platform/omapdss/overlay%d/global_alpha", plane);
+
+ out = fopen(path, "w");
+
+ if (out) {
+ fprintf(out, "%d", level);
+ fclose(out);
+ }
+}
+#endif
+
+MCompositeManagerPrivate::MCompositeManagerPrivate(QObject *p)
+ : QObject(p),
+ glwidget(0),
+ damage_cache(0),
+ arranged(false),
+ compositing(true),
+ display_off(false)
+{
+ watch = new MCompositeScene(this);
+ atom = MCompAtoms::instance();
+
+#ifdef GLES2_VERSION
+ systembus_conn = new QDBusConnection(QDBusConnection::systemBus());
+ systembus_conn->connect(MCE_SERVICE, MCE_SIGNAL_PATH, MCE_SIGNAL_IF,
+ MCE_DISPLAY_SIG, this,
+ SLOT(mceDisplayStatusIndSignal(QString)));
+ if (!systembus_conn->isConnected())
+ qWarning("Failed to connect to the D-Bus system bus");
+
+ /* FIXME: Temporary workaround, current MCE does not seem to provide
+ * get_display_status interface */
+ QFile file("/sys/class/backlight/himalaya/brightness");
+ if (file.open(QIODevice::ReadOnly)) {
+ char buf[50];
+ qint64 len = file.readLine(buf, sizeof(buf));
+ buf[49] = '\0';
+ if (len != -1) {
+ int i = atoi(buf);
+ if (i == 0) display_off = true;
+ }
+ file.close();
+ }
+#endif
+}
+
+MCompositeManagerPrivate::~MCompositeManagerPrivate()
+{
+ delete watch;
+ delete atom;
+ watch = 0;
+ atom = 0;
+}
+
+Window MCompositeManagerPrivate::parentWindow(Window child)
+{
+ uint children = 0;
+ Window r, p, *kids = 0;
+
+ XQueryTree(QX11Info::display(), child, &r, &p, &kids, &children);
+ if (kids)
+ XFree(kids);
+ return p;
+}
+
+void MCompositeManagerPrivate::disableInput()
+{
+ watch->setupOverlay(xoverlay, QRect(0, 0, 0, 0), true);
+ watch->setupOverlay(localwin, QRect(0, 0, 0, 0), true);
+}
+
+void MCompositeManagerPrivate::enableInput()
+{
+ watch->setupOverlay(xoverlay, QRect(0, 0, 0, 0));
+ watch->setupOverlay(localwin, QRect(0, 0, 0, 0));
+
+ emit inputEnabled();
+}
+
+void MCompositeManagerPrivate::prepare()
+{
+ watch->prepareRoot();
+ Window w;
+ QString wm_name = "MCompositor";
+
+ w = XCreateSimpleWindow(QX11Info::display(),
+ RootWindow(QX11Info::display(), 0),
+ 0, 0, 1, 1, 0,
+ None, None);
+ XChangeProperty(QX11Info::display(), RootWindow(QX11Info::display(), 0),
+ ATOM(_NET_SUPPORTING_WM_CHECK), XA_WINDOW, 32,
+ PropModeReplace, (unsigned char *)&w, 1);
+ XChangeProperty(QX11Info::display(), w, ATOM(_NET_SUPPORTING_WM_CHECK),
+ XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
+ XChangeProperty(QX11Info::display(), w, ATOM(_NET_WM_NAME),
+ XInternAtom(QX11Info::display(), "UTF8_STRING", 0), 8,
+ PropModeReplace, (unsigned char *) wm_name.toUtf8().data(),
+ wm_name.size());
+
+
+ Xutf8SetWMProperties(QX11Info::display(), w, "MCompositor",
+ "MCompositor", NULL, 0, NULL, NULL,
+ NULL);
+ Atom a = XInternAtom(QX11Info::display(), "_NET_WM_CM_S0", False);
+ XSetSelectionOwner(QX11Info::display(), a, w, 0);
+
+ xoverlay = XCompositeGetOverlayWindow(QX11Info::display(),
+ RootWindow(QX11Info::display(), 0));
+ XReparentWindow(QX11Info::display(), localwin, xoverlay, 0, 0);
+ enableInput();
+
+ XDamageQueryExtension(QX11Info::display(), &damage_event, &damage_error);
+}
+
+bool MCompositeManagerPrivate::needDecoration(Window window)
+{
+ MCompAtoms::Type t = atom->windowType(window);
+ return (t != MCompAtoms::FRAMELESS
+ && t != MCompAtoms::DESKTOP
+ && t != MCompAtoms::NOTIFICATION
+ && t != MCompAtoms::INPUT
+ && t != MCompAtoms::DOCK
+ && !transient_for(window));
+}
+
+void MCompositeManagerPrivate::damageEvent(XDamageNotifyEvent *e)
+{
+ if (display_off)
+ return;
+ XserverRegion r = XFixesCreateRegion(QX11Info::display(), 0, 0);
+ int num;
+ XDamageSubtract(QX11Info::display(), e->damage, None, r);
+
+ XRectangle *rects = 0;
+ rects = XFixesFetchRegion(QX11Info::display(), r, &num);
+ XFixesDestroyRegion(QX11Info::display(), r);
+
+ if (damage_cache && damage_cache->window() == e->drawable) {
+ if (rects) {
+ damage_cache->updateWindowPixmap(rects, num);
+ XFree(rects);
+ }
+ return;
+ }
+ MCompositeWindow *item = texturePixmapItem(e->drawable);
+ damage_cache = item;
+ if (item)
+ item->updateWindowPixmap(rects, num);
+
+ if (rects)
+ XFree(rects);
+}
+
+void MCompositeManagerPrivate::destroyEvent(XDestroyWindowEvent *e)
+{
+ bool already_unredirected = false;
+ MCompositeWindow *item = texturePixmapItem(e->window);
+ if (item) {
+ if (item->isDirectRendered())
+ already_unredirected = true;
+ scene()->removeItem(item);
+ delete item;
+ if (!removeWindow(e->window))
+ qWarning("destroyEvent(): Error removing window");
+ glwidget->update();
+ damage_cache = 0;
+ } else {
+ // We got a destroy event from a framed window
+ FrameData fd = framed_windows.value(e->window);
+ if (!fd.frame)
+ return;
+
+ XGrabServer(QX11Info::display());
+ XReparentWindow(QX11Info::display(), e->window,
+ RootWindow(QX11Info::display(), 0), 0, 0);
+ XRemoveFromSaveSet(QX11Info::display(), e->window);
+ framed_windows.remove(e->window);
+ XUngrabServer(QX11Info::display());
+ delete fd.frame;
+ }
+ if (!already_unredirected)
+ XCompositeUnredirectWindow(QX11Info::display(), e->window,
+ CompositeRedirectAutomatic);
+ XUngrabButton(QX11Info::display(), AnyButton, AnyModifier, e->window);
+ XSync(QX11Info::display(), False);
+}
+
+/*
+ This doesn't really do anything for now. It will be used to get
+ the NETWM hints in the future like window opacity and stuff
+*/
+void MCompositeManagerPrivate::propertyEvent(XPropertyEvent *e)
+{
+ Q_UNUSED(e);
+ /*
+ Atom opacityAtom = ATOM(_NET_WM_WINDOW_OPACITY);
+ if(e->atom == opacityAtom)
+ ;
+ */
+}
+
+Window MCompositeManagerPrivate::getLastVisibleParent(MCompositeWindow *cw)
+{
+ Window last = 0, parent;
+ while (cw && (parent = cw->transientFor())) {
+ cw = windows.value(parent);
+ if (cw && cw->isMapped())
+ last = parent;
+ else // no-good parent, bail out
+ break;
+ }
+ return last;
+}
+
+bool MCompositeManagerPrivate::isAppWindow(MCompositeWindow *cw)
+{
+ if (cw && !getLastVisibleParent(cw) &&
+ (cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_NORMAL) ||
+ cw->windowTypeAtom() == ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
+ && !cw->isDecorator())
+ return true;
+ return false;
+}
+
+Window MCompositeManagerPrivate::getTopmostApp(int *index_in_stacking_list)
+{
+ Window topmost_app = 0;
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ Window w = stacking_list.at(i);
+ if (w == stack[DESKTOP_LAYER])
+ /* desktop is above all applications */
+ break;
+ MCompositeWindow *cw = windows.value(w);
+ if (cw && cw->isMapped() && isAppWindow(cw)) {
+ topmost_app = w;
+ if (index_in_stacking_list)
+ *index_in_stacking_list = i;
+ break;
+ }
+ }
+ return topmost_app;
+}
+
+// TODO: merge this with disableCompositing() so that in the end we have
+// stacking order sensitive logic
+bool MCompositeManagerPrivate::possiblyUnredirectTopmostWindow()
+{
+ bool ret = false;
+ Window top = 0;
+ int win_i = -1;
+ MCompositeWindow *cw = 0;
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ Window w = stacking_list.at(i);
+ if (!(cw = windows.value(w)))
+ continue;
+ if (w == stack[DESKTOP_LAYER]) {
+ top = w;
+ win_i = i;
+ break;
+ }
+ if (cw->isMapped() &&
+ (cw->hasAlpha() || cw->needDecoration() || cw->isDecorator()))
+ // this window prevents direct rendering
+ return false;
+ if (cw->isMapped() && isAppWindow(cw)) {
+ top = w;
+ win_i = i;
+ break;
+ }
+ }
+ if (top && cw) {
+ // unredirect the chosen window and any docks above it
+ ((MTexturePixmapItem *)cw)->enableDirectFbRendering();
+ setWindowDebugProperties(top);
+ // make sure unobscured event is sent when compositing again
+ cw->setWindowObscured(true, true);
+ for (int i = win_i + 1; i < stacking_list.size(); ++i) {
+ Window w = stacking_list.at(i);
+ if ((cw = windows.value(w)) && cw->isMapped() &&
+ cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DOCK)) {
+ ((MTexturePixmapItem *)cw)->enableDirectFbRendering();
+ setWindowDebugProperties(w);
+ cw->setWindowObscured(true, true);
+ }
+ }
+ if (compositing) {
+ scene()->views()[0]->setUpdatesEnabled(false);
+ XUnmapWindow(QX11Info::display(), xoverlay);
+ compositing = false;
+ }
+ ret = true;
+ }
+ return ret;
+}
+
+void MCompositeManagerPrivate::unmapEvent(XUnmapEvent *e)
+{
+ if (e->window == xoverlay)
+ return;
+
+ Window topmost_win = 0;
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ Window w = stacking_list.at(i);
+ MCompositeWindow *cw = windows.value(w);
+ if (cw && cw->isMapped() && !cw->isDecorator() &&
+ cw->windowTypeAtom() != ATOM(_NET_WM_WINDOW_TYPE_DOCK)) {
+ topmost_win = w;
+ break;
+ }
+ }
+
+ MCompositeWindow *item = texturePixmapItem(e->window);
+ if (item) {
+ item->setIsMapped(false);
+ setWindowState(e->window, IconicState);
+ if (item->isVisible()) {
+ item->setVisible(false);
+ item->clearTexture();
+ glwidget->update();
+ }
+ if (MDecoratorFrame::instance()->managedWindow() == e->window)
+ MDecoratorFrame::instance()->lower();
+ } else {
+ // We got an unmap event from a framed window
+ FrameData fd = framed_windows.value(e->window);
+ if (!fd.frame)
+ return;
+ // make sure we reparent first before deleting the window
+ XGrabServer(QX11Info::display());
+ XReparentWindow(QX11Info::display(), e->window,
+ RootWindow(QX11Info::display(), 0), 0, 0);
+ setWindowState(e->window, IconicState);
+ XRemoveFromSaveSet(QX11Info::display(), e->window);
+ framed_windows.remove(e->window);
+ XUngrabServer(QX11Info::display());
+ delete fd.frame;
+ }
+ updateWinList();
+
+ for (int i = 0; i < TOTAL_LAYERS; ++i)
+ if (stack[i] == e->window) stack[i] = 0;
+
+ if (topmost_win == e->window) {
+#ifdef GLES2_VERSION
+ toggle_global_alpha_blend(0);
+ set_global_alpha(0, 255);
+#endif
+
+ // activate next mapped window
+ // FIXME: this is flawed because the unmapped window is already
+ // moved below home in stacking_list at this point. Needs to be fixed
+ // so that the chained window case does not break.
+ for (int i = stacking_list.indexOf(e->window) - 1; i >= 0; --i) {
+ MCompositeWindow *cw = windows.value(stacking_list.at(i));
+ if (cw && cw->isMapped()) {
+ /* either lower window of the application (in chained window
+ * case), or duihome is activated */
+ activateWindow(stacking_list.at(i), CurrentTime, true);
+ break;
+ }
+ }
+ }
+}
+
+void MCompositeManagerPrivate::configureEvent(XConfigureEvent *e)
+{
+ if (e->window == xoverlay)
+ return;
+
+ MCompositeWindow *item = texturePixmapItem(e->window);
+ if (item) {
+ item->setPos(e->x, e->y);
+ item->resize(e->width, e->height);
+ if (e->override_redirect == True)
+ return;
+
+ Window above = e->above;
+ if (above != None) {
+ /* FIXME: this is flawed because it assumes the window is on top,
+ * which will break when we have one decorated window
+ * on top of this window */
+ if (item->needDecoration() && MDecoratorFrame::instance()->decoratorItem()) {
+ MDecoratorFrame::instance()->setManagedWindow(e->window);
+ MDecoratorFrame::instance()->decoratorItem()->setVisible(true);
+ MDecoratorFrame::instance()->raise();
+ MDecoratorFrame::instance()->decoratorItem()->setZValue(item->zValue() + 1);
+ item->update();
+ }
+ } else {
+ // FIXME: seems that this branch is never executed?
+ if (e->window == MDecoratorFrame::instance()->managedWindow())
+ MDecoratorFrame::instance()->lower();
+ item->setIconified(true);
+ // ensure ZValue is set only after the animation is done
+ item->requestZValue(0);
+
+ MCompositeWindow *desktop = texturePixmapItem(stack[DESKTOP_LAYER]);
+ if (desktop)
+#if (QT_VERSION >= 0x040600)
+ item->stackBefore(desktop);
+#endif
+ }
+ }
+}
+
+void MCompositeManagerPrivate::configureRequestEvent(XConfigureRequestEvent *e)
+{
+ if (e->parent != RootWindow(QX11Info::display(), 0))
+ return;
+
+ MCompAtoms::Type wtype = atom->windowType(e->window);
+ bool isInput = (wtype == MCompAtoms::INPUT);
+ // sandbox these windows. we own them
+ if (atom->isDecorator(e->window))
+ return;
+
+ /*qDebug() << __func__ << "CONFIGURE REQUEST FOR:" << e->window
+ << e->x << e->y << e->width << e->height << "above/mode:"
+ << e->above << e->detail;*/
+
+ // dock changed
+ if (hasDock && (wtype == MCompAtoms::DOCK)) {
+ dock_region = QRegion(e->x, e->y, e->width, e->height);
+ QRect r = (QRegion(QApplication::desktop()->screenGeometry()) - dock_region).boundingRect();
+ if (stack[DESKTOP_LAYER] && need_geometry_modify(stack[DESKTOP_LAYER]))
+ XMoveResizeWindow(QX11Info::display(), stack[DESKTOP_LAYER], r.x(), r.y(), r.width(), r.height());
+
+ if (stack[APPLICATION_LAYER]) {
+ if (need_geometry_modify(stack[APPLICATION_LAYER]))
+ XMoveResizeWindow(QX11Info::display(), stack[APPLICATION_LAYER],
+ r.x(), r.y(), r.width(), r.height());
+ positionWindow(stack[APPLICATION_LAYER], STACK_TOP);
+ }
+
+ if (stack[INPUT_LAYER] && need_geometry_modify(stack[INPUT_LAYER]))
+ XMoveResizeWindow(QX11Info::display(), stack[INPUT_LAYER], r.x(), r.y(), r.width(), r.height());
+ }
+
+ XWindowChanges wc;
+ wc.border_width = e->border_width;
+ wc.x = e->x;
+ wc.y = e->y;
+ wc.width = e->width;
+ wc.height = e->height;
+ wc.sibling = e->above;
+ wc.stack_mode = e->detail;
+
+ if ((e->detail == Above) && (e->above == None) && !isInput) {
+ XWindowAttributes a;
+ if (!XGetWindowAttributes(QX11Info::display(), e->window, &a)) {
+ qWarning("XGetWindowAttributes for 0x%lx failed", e->window);
+ return;
+ }
+ if ((a.map_state == IsViewable) && (wtype != MCompAtoms::DOCK)) {
+ setWindowState(e->window, NormalState);
+ setExposeDesktop(false);
+ stack[APPLICATION_LAYER] = e->window;
+
+ // selective compositing support
+ MCompositeWindow *i = texturePixmapItem(e->window);
+ if (i) {
+ // since we call disable compositing immediately
+ // we don't see the animated transition
+ if (!i->hasAlpha() && !i->needDecoration()) {
+ i->setIconified(false);
+ disableCompositing(FORCED);
+ } else if (MDecoratorFrame::instance()->managedWindow() == e->window)
+ enableCompositing();
+ i->restore();
+ }
+ }
+ } else if ((e->detail == Below) && (e->above == None) && !isInput)
+ setWindowState(e->window, IconicState);
+
+ /* modify stacking_list if stacking order should be changed */
+ int win_i = stacking_list.indexOf(e->window);
+ if (win_i >= 0 && e->detail == Above) {
+ if (e->above != None) {
+ int above_i = stacking_list.indexOf(e->above);
+ if (above_i >= 0) {
+ if (above_i > win_i)
+ stacking_list.move(win_i, above_i);
+ else
+ stacking_list.move(win_i, above_i + 1);
+ checkStacking(false);
+ }
+ } else {
+ Window parent = transient_for(e->window);
+ if (parent)
+ positionWindow(parent, STACK_TOP);
+ else
+ positionWindow(e->window, STACK_TOP);
+ }
+ } else if (win_i >= 0 && e->detail == Below) {
+ if (e->above != None) {
+ int above_i = stacking_list.indexOf(e->above);
+ if (above_i >= 0) {
+ if (above_i > win_i)
+ stacking_list.move(win_i, above_i - 1);
+ else
+ stacking_list.move(win_i, above_i);
+ checkStacking(false);
+ }
+ } else {
+ Window parent = transient_for(e->window);
+ if (parent)
+ positionWindow(parent, STACK_BOTTOM);
+ else
+ positionWindow(e->window, STACK_BOTTOM);
+ }
+ }
+
+ /* stacking is done in checkStacking(), based on stacking_list */
+ unsigned int value_mask = e->value_mask & ~(CWSibling | CWStackMode);
+ if (value_mask)
+ XConfigureWindow(QX11Info::display(), e->window, value_mask, &wc);
+}
+
+void MCompositeManagerPrivate::mapRequestEvent(XMapRequestEvent *e)
+{
+ XWindowAttributes a;
+ Display *dpy = QX11Info::display();
+ bool hasAlpha = false;
+
+ if (!XGetWindowAttributes(QX11Info::display(), e->window, &a))
+ return;
+ if (!hasDock) {
+ hasDock = (atom->windowType(e->window) == MCompAtoms::DOCK);
+ if (hasDock)
+ dock_region = QRegion(a.x, a.y, a.width, a.height);
+ }
+ int xres = ScreenOfDisplay(dpy, DefaultScreen(dpy))->width;
+ int yres = ScreenOfDisplay(dpy, DefaultScreen(dpy))->height;
+
+ if ((atom->windowType(e->window) == MCompAtoms::FRAMELESS
+ || atom->windowType(e->window) == MCompAtoms::DESKTOP
+ || atom->windowType(e->window) == MCompAtoms::INPUT)
+ && (atom->windowType(e->window) != MCompAtoms::DOCK)) {
+ if (hasDock) {
+ QRect r = (QRegion(QApplication::desktop()->screenGeometry()) - dock_region).boundingRect();
+ if (availScreenRect != r)
+ availScreenRect = r;
+ if (need_geometry_modify(e->window))
+ XMoveResizeWindow(dpy, e->window, r.x(), r.y(), r.width(), r.height());
+ } else if ((a.width != xres) && (a.height != yres)) {
+ XResizeWindow(dpy, e->window, xres, yres);
+ }
+ }
+
+ if (atom->isDecorator(e->window)) {
+ enableCompositing();
+ MDecoratorFrame::instance()->setDecoratorWindow(e->window);
+ return;
+ }
+
+ XRenderPictFormat *format = XRenderFindVisualFormat(QX11Info::display(),
+ a.visual);
+ if (format && (format->type == PictTypeDirect && format->direct.alphaMask)) {
+ enableCompositing();
+ hasAlpha = true;
+ }
+
+ if (needDecoration(e->window)) {
+ XSelectInput(dpy, e->window,
+ StructureNotifyMask | ColormapChangeMask |
+ PropertyChangeMask);
+ XAddToSaveSet(QX11Info::display(), e->window);
+
+ if (MDecoratorFrame::instance()->decoratorItem()) {
+ enableCompositing();
+ XMapWindow(QX11Info::display(), e->window);
+ // initially visualize decorator item so selective compositing
+ // checks won't disable compositing
+ MDecoratorFrame::instance()->decoratorItem()->setVisible(true);
+ } else {
+ MSimpleWindowFrame *frame = 0;
+ FrameData f = framed_windows.value(e->window);
+ frame = f.frame;
+ if (!frame) {
+ frame = new MSimpleWindowFrame(e->window);
+ Window trans;
+ XGetTransientForHint(QX11Info::display(), e->window, &trans);
+ if (trans)
+ frame->setDialogDecoration(true);
+
+ // TEST: a framed translucent window
+ if (hasAlpha)
+ frame->setAttribute(Qt::WA_TranslucentBackground);
+ QSize s = frame->suggestedWindowSize();
+ XResizeWindow(QX11Info::display(), e->window, s.width(), s.height());
+
+ XReparentWindow(QX11Info::display(), frame->winId(),
+ RootWindow(QX11Info::display(), 0), 0, 0);
+
+ // associate e->window with frame and its parent
+ FrameData fd;
+ fd.frame = frame;
+ fd.mapped = true;
+ fd.parentWindow = frame->winId();
+ framed_windows[e->window] = fd;
+
+ if (trans) {
+ FrameData f = framed_windows.value(trans);
+ if (f.frame) {
+ XSetTransientForHint(QX11Info::display(), frame->winId(),
+ f.frame->winId());
+ }
+ }
+ }
+
+ XReparentWindow(QX11Info::display(), e->window,
+ frame->windowArea(), 0, 0);
+ setWindowState(e->window, NormalState);
+ XMapWindow(QX11Info::display(), e->window);
+ frame->show();
+
+ XSync(QX11Info::display(), False);
+ }
+ } else {
+ setWindowState(e->window, NormalState);
+ XMapWindow(QX11Info::display(), e->window);
+ }
+}
+
+/* recursion is needed to handle transients that are transient for other
+ * transients */
+static void raise_transients(MCompositeManagerPrivate *priv,
+ Window w, int last_i)
+{
+ Window first_moved = 0;
+ for (int i = 0; i < last_i;) {
+ Window iw = priv->stacking_list.at(i);
+ if (iw == first_moved)
+ /* each window is only considered once */
+ break;
+ MCompositeWindow *cw = priv->windows.value(iw);
+ if (cw && cw->transientFor() == w) {
+ priv->stacking_list.move(i, last_i);
+ if (!first_moved) first_moved = iw;
+ raise_transients(priv, iw, last_i);
+ } else ++i;
+ }
+}
+
+#if 0 // disabled due to bugs in applications (e.g. widgetsgallery)
+static Bool
+timestamp_predicate(Display *display,
+ XEvent *xevent,
+ XPointer arg)
+{
+ Q_UNUSED(arg);
+ if (xevent->type == PropertyNotify &&
+ xevent->xproperty.window == RootWindow(display, 0) &&
+ xevent->xproperty.atom == ATOM(_NET_CLIENT_LIST))
+ return True;
+
+ return False;
+}
+
+static Time get_server_time()
+{
+ XEvent xevent;
+ long data = 0;
+
+ /* zero-length append to get timestamp in the PropertyNotify */
+ XChangeProperty(QX11Info::display(), RootWindow(QX11Info::display(), 0),
+ ATOM(_NET_CLIENT_LIST),
+ XA_WINDOW, 32, PropModeAppend,
+ (unsigned char *)&data, 0);
+
+ XIfEvent(QX11Info::display(), &xevent, timestamp_predicate, NULL);
+
+ return xevent.xproperty.time;
+}
+#endif
+
+/* NOTE: this assumes that stacking is correct */
+void MCompositeManagerPrivate::checkInputFocus(Time timestamp)
+{
+ Window w = None;
+
+ /* find topmost window wanting the input focus */
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ Window iw = stacking_list.at(i);
+ MCompositeWindow *cw = windows.value(iw);
+ if (!cw || !cw->isMapped() || !cw->wantsFocus())
+ continue;
+ /* workaround for NB#161629 */
+ if (is_desktop_dock(iw))
+ continue;
+ if (isSelfManagedFocus(iw)) {
+ w = iw;
+ break;
+ }
+ /* FIXME: do this based on geometry to cope with TYPE_NORMAL dialogs */
+ /* don't focus a window that is obscured (assumes that NORMAL
+ * and DESKTOP cover the whole screen) */
+ if (cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_NORMAL) ||
+ cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DESKTOP))
+ break;
+ }
+
+ if (prev_focus == w)
+ return;
+ prev_focus = w;
+
+#if 0 // disabled due to bugs in applications (e.g. widgetsgallery)
+ MCompositeWindow *cw = windows.value(w);
+ if (cw && cw->supportedProtocols().indexOf(ATOM(WM_TAKE_FOCUS)) != -1) {
+ /* CurrentTime for WM_TAKE_FOCUS brings trouble
+ * (a lesson learned from Fremantle) */
+ if (timestamp == CurrentTime)
+ timestamp = get_server_time();
+
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = w;
+ ev.xclient.message_type = ATOM(WM_PROTOCOLS);
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = ATOM(WM_TAKE_FOCUS);
+ ev.xclient.data.l[1] = timestamp;
+
+ XSendEvent(QX11Info::display(), w, False, NoEventMask, &ev);
+ } else
+#endif
+ XSetInputFocus(QX11Info::display(), w, RevertToPointerRoot, timestamp);
+}
+
+/* Go through stacking_list and verify that it is in order.
+ * If it isn't, reorder it and call XRestackWindows.
+ * NOTE: stacking_list needs to be reversed before giving it to
+ * XRestackWindows.*/
+void MCompositeManagerPrivate::checkStacking(bool force_visibility_check,
+ Time timestamp)
+{
+ static QList<Window> prev_list;
+ Window active_app = 0, duihome = stack[DESKTOP_LAYER], first_moved;
+ int last_i = stacking_list.size() - 1;
+ bool desktop_up = false;
+ int app_i = -1;
+ MDecoratorFrame *deco = MDecoratorFrame::instance();
+
+ active_app = getTopmostApp(&app_i);
+ if (!active_app || app_i < 0)
+ desktop_up = true;
+
+ /* raise active app with its transients, or duihome if
+ * there is no active application */
+ if (!desktop_up && active_app && app_i >= 0) {
+ if (duihome) {
+ /* stack duihome right below the application */
+ stacking_list.move(stacking_list.indexOf(duihome), last_i);
+ app_i = stacking_list.indexOf(active_app);
+ }
+ /* raise application windows belonging to the same group */
+ MCompositeWindow *cw = windows.value(active_app);
+ XID group;
+ if (cw && (group = cw->windowGroup())) {
+ for (int i = 0; i < app_i; ) {
+ cw = windows.value(stacking_list.at(i));
+ if (isAppWindow(cw) && cw->windowGroup() == group) {
+ stacking_list.move(i, last_i);
+ /* active_app was moved, update the index */
+ app_i = stacking_list.indexOf(active_app);
+ /* TODO: transients */
+ } else ++i;
+ }
+ }
+ stacking_list.move(app_i, last_i);
+ /* raise decorator above the application */
+ if (deco->decoratorItem() && deco->managedWindow() == active_app) {
+ Window deco_w = deco->decoratorItem()->window();
+ int deco_i = stacking_list.indexOf(deco_w);
+ if (deco_i >= 0) {
+ stacking_list.move(deco_i, last_i);
+ if (!compositing)
+ // decor requires compositing
+ enableCompositing(true);
+ MCompositeWindow *cw = windows.value(deco_w);
+ cw->updateWindowPixmap();
+ cw->setVisible(true);
+ }
+ }
+ /* raise transients recursively */
+ raise_transients(this, active_app, last_i);
+ } else if (duihome) {
+ //qDebug() << "raising home window" << duihome;
+ stacking_list.move(stacking_list.indexOf(duihome), last_i);
+ }
+
+ /* raise docks if either the desktop is up or the application is
+ * non-fullscreen */
+ if (desktop_up || !active_app || app_i < 0 ||
+ !(atom->getState(active_app) == ATOM(_NET_WM_STATE_FULLSCREEN))) {
+ first_moved = 0;
+ for (int i = 0; i < last_i;) {
+ Window w = stacking_list.at(i);
+ if (w == first_moved) break;
+ MCompositeWindow *cw = windows.value(w);
+ if (cw && cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DOCK)) {
+ stacking_list.move(i, last_i);
+ if (!first_moved) first_moved = w;
+ /* possibly reposition the decorator so that it does not
+ * go below the dock window */
+ if (active_app && deco->decoratorItem() &&
+ deco->managedWindow() == active_app) {
+ int h = (int)cw->boundingRect().height();
+ XMoveWindow(QX11Info::display(),
+ deco->decoratorItem()->window(), 0, h);
+ }
+ /* raise transients recursively */
+ raise_transients(this, w, last_i);
+ } else ++i;
+ }
+ } else if (active_app && deco->decoratorItem() &&
+ deco->managedWindow() == active_app &&
+ atom->getState(active_app) == ATOM(_NET_WM_STATE_FULLSCREEN)) {
+ // no dock => decorator starts from (0,0)
+ XMoveWindow(QX11Info::display(), deco->decoratorItem()->window(), 0, 0);
+ }
+ /* raise all system-modal dialogs */
+ first_moved = 0;
+ for (int i = 0; i < last_i;) {
+ /* TODO: transients */
+ Window w = stacking_list.at(i);
+ if (w == first_moved) break;
+ MCompositeWindow *cw = windows.value(w);
+ if (cw && !cw->transientFor()
+ && cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DIALOG)) {
+ stacking_list.move(i, last_i);
+ if (!first_moved) first_moved = w;
+ } else ++i;
+ }
+ /* raise all keep-above flagged and input methods, at the same time
+ * preserving their mutual stacking order */
+ first_moved = 0;
+ for (int i = 0; i < last_i;) {
+ /* TODO: transients */
+ Window w = stacking_list.at(i);
+ if (w == first_moved) break;
+ MCompositeWindow *cw = windows.value(w);
+ if (cw && cw->isDecorator()) {
+ ++i;
+ continue;
+ }
+ if ((cw && cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_INPUT)) ||
+ atom->getState(w) == ATOM(_NET_WM_STATE_ABOVE)) {
+ stacking_list.move(i, last_i);
+ if (!first_moved) first_moved = w;
+ } else ++i;
+ }
+ /* raise all non-transient notifications (transient ones were already
+ * handled above) */
+ first_moved = 0;
+ for (int i = 0; i < last_i;) {
+ Window w = stacking_list.at(i);
+ if (w == first_moved) break;
+ MCompositeWindow *cw = windows.value(w);
+ if (cw && !cw->transientFor() &&
+ cw->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_NOTIFICATION)) {
+ stacking_list.move(i, last_i);
+ if (!first_moved) first_moved = w;
+ } else ++i;
+ }
+
+ bool order_changed = prev_list != stacking_list;
+ if (order_changed) {
+ /* fix Z-values */
+ for (int i = 0; i <= last_i; ++i) {
+ MCompositeWindow *witem = windows.value(stacking_list.at(i));
+ if (witem)
+ witem->requestZValue(i);
+ }
+
+ QList<Window> reverse;
+ for (int i = last_i; i >= 0; --i)
+ reverse.append(stacking_list.at(i));
+ //qDebug() << __func__ << "stack:" << reverse.toVector();
+
+ XRestackWindows(QX11Info::display(), reverse.toVector().data(),
+ reverse.size());
+ XChangeProperty(QX11Info::display(),
+ RootWindow(QX11Info::display(), 0),
+ ATOM(_NET_CLIENT_LIST_STACKING),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)stacking_list.toVector().data(),
+ stacking_list.size());
+ prev_list = QList<Window>(stacking_list);
+
+ checkInputFocus(timestamp);
+ XSync(QX11Info::display(), False);
+ }
+ if (order_changed || force_visibility_check) {
+ /* Send synthetic visibility events for composited windows */
+ int home_i = stacking_list.indexOf(duihome);
+ for (int i = 0; i <= last_i; ++i) {
+ MCompositeWindow *cw = windows.value(stacking_list.at(i));
+ if (!cw || cw->isDirectRendered()) continue;
+ if (duihome && i > home_i) {
+ cw->setWindowObscured(false);
+ cw->setVisible(true);
+ } else if (i == home_i && desktop_up) {
+ cw->setWindowObscured(false);
+ cw->setVisible(true);
+ } else if (!duihome) {
+ cw->setWindowObscured(false);
+ cw->setVisible(true);
+ } else {
+ cw->setWindowObscured(true);
+ cw->setVisible(false);
+ }
+ }
+ }
+}
+
+void MCompositeManagerPrivate::mapEvent(XMapEvent *e)
+{
+ Window win = e->window;
+ Window transient_for = 0;
+
+ if (win == xoverlay) {
+ enableRedirection();
+ return;
+ }
+
+ FrameData fd = framed_windows.value(win);
+ if (fd.frame) {
+ XWindowAttributes a;
+ if (!XGetWindowAttributes(QX11Info::display(), e->window, &a))
+ return;
+ XConfigureEvent c;
+ c.type = ConfigureNotify;
+ c.send_event = True;
+ c.event = e->window;
+ c.window = e->window;
+ c.x = 0;
+ c.y = 0;
+ c.width = a.width;
+ c.height = a.height;
+ c.border_width = 0;
+ c.above = stack[DESKTOP_LAYER];
+ c.override_redirect = 0;
+ XSendEvent(QX11Info::display(), c.event, true, StructureNotifyMask, (XEvent *)&c);
+ }
+
+ // simple stacking model fulfills the current DUI concept,
+ if (atom->windowType(e->window) == MCompAtoms::DESKTOP) {
+ stack[DESKTOP_LAYER] = e->window; // below topmost
+ } else if (atom->windowType(e->window) == MCompAtoms::INPUT) {
+ stack[INPUT_LAYER] = e->window; // topmost
+ } else if (atom->windowType(e->window) == MCompAtoms::DOCK) {
+ stack[DOCK_LAYER] = e->window; // topmost
+ } else {
+ if ((atom->windowType(e->window) == MCompAtoms::FRAMELESS ||
+ (atom->windowType(e->window) == MCompAtoms::NORMAL))
+ && !atom->isDecorator(e->window)
+ && (parentWindow(win) == RootWindow(QX11Info::display(), 0))
+ && (e->event == QX11Info::appRootWindow())) {
+ hideLaunchIndicator();
+ stack[APPLICATION_LAYER] = e->window; // between
+
+ setExposeDesktop(false);
+ }
+ }
+
+#ifdef GLES2_VERSION
+ // TODO: this should probably be done on the focus level. Rewrite this
+ // once new stacking code from Kimmo is done
+ int g_alpha = atom->globalAlphaFromWindow(win);
+ if (g_alpha == 255)
+ toggle_global_alpha_blend(0);
+ else if (g_alpha < 255)
+ toggle_global_alpha_blend(1);
+ set_global_alpha(0, g_alpha);
+#endif
+
+ MCompositeWindow *item = texturePixmapItem(win);
+ if (item) item->setIsMapped(true);
+ // Compositing is assumed to be enabled at this point if a window
+ // has alpha channels
+ if (!compositing && (item && item->hasAlpha())) {
+ qWarning("mapEvent(): compositing not enabled!");
+ return;
+ }
+ if (item) {
+ item->setWindowTypeAtom(atom->getType(win));
+ item->saveBackingStore(true);
+ if (!display_off && !item->hasAlpha() && !item->needDecoration()) {
+ item->setVisible(true);
+ item->updateWindowPixmap();
+ disableCompositing();
+ } else if (!display_off) {
+ ((MTexturePixmapItem *)item)->enableRedirectedRendering();
+ item->delayShow(100);
+ }
+ /* do this after bindWindow() so that the window is in
+ * stacking_list */
+ activateWindow(win, CurrentTime, false);
+ return;
+ }
+
+ if (win == localwin || win == parentWindow(localwin))
+ return;
+
+ XGrabButton(QX11Info::display(), AnyButton, AnyModifier, win, True,
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
+ GrabModeSync, GrabModeSync, None, None);
+
+ // only composite top-level windows
+ if ((parentWindow(win) == RootWindow(QX11Info::display(), 0))
+ && (e->event == QX11Info::appRootWindow())) {
+ item = bindWindow(win);
+ if (transient_for)
+ item->setWindowType(MCompositeWindow::Transient);
+ else
+ item->setWindowType(MCompositeWindow::Normal);
+ if (!item->hasAlpha())
+ disableCompositing(FORCED);
+ else
+ item->delayShow(500);
+
+ // the current decorated window got mapped
+ if (e->window == MDecoratorFrame::instance()->managedWindow() &&
+ MDecoratorFrame::instance()->decoratorItem()) {
+ connect(item, SIGNAL(visualized(bool)),
+ MDecoratorFrame::instance(),
+ SLOT(visualizeDecorator(bool)));
+ MDecoratorFrame::instance()->decoratorItem()->setVisible(true);
+ MDecoratorFrame::instance()->raise();
+ MDecoratorFrame::instance()->decoratorItem()->setZValue(item->zValue() + 1);
+ stack[APPLICATION_LAYER] = e->window;
+ }
+ setWindowDebugProperties(win);
+ }
+ /* do this after bindWindow() so that the window is in stacking_list */
+ activateWindow(win, CurrentTime, false);
+}
+
+static bool should_be_pinged(MCompositeWindow *cw)
+{
+ if (cw && cw->supportedProtocols().indexOf(ATOM(_NET_WM_PING)) != -1
+ && cw->windowTypeAtom() != ATOM(_NET_WM_WINDOW_TYPE_DOCK)
+ && cw->iconifyState() == MCompositeWindow::NoIconifyState
+ && !cw->isDecorator()
+ && !is_desktop_window(cw->window()))
+ return true;
+ return false;
+}
+
+void MCompositeManagerPrivate::rootMessageEvent(XClientMessageEvent *event)
+{
+ MCompositeWindow *i = texturePixmapItem(event->window);
+ FrameData fd = framed_windows.value(event->window);
+
+ if (event->message_type == ATOM(_NET_ACTIVE_WINDOW)) {
+ // Visibility notification to desktop window. Ensure this is sent
+ // before transitions are started
+ if (event->window != stack[DESKTOP_LAYER])
+ setExposeDesktop(false);
+
+ Window raise = event->window;
+ MCompositeWindow *d_item = texturePixmapItem(stack[DESKTOP_LAYER]);
+ bool needComp = false;
+ if (d_item && d_item->isDirectRendered()) {
+ needComp = true;
+ // _NET_ACTIVE_WINDOW comes from duihome when tapping on thumbnail
+ // so display will be on soon if it's not already
+ enableCompositing(true, true);
+ }
+ if (i) {
+ i->setZValue(windows.size() + 1);
+ QRectF iconGeometry = atom->iconGeometry(raise);
+ i->setPos(iconGeometry.topLeft());
+ i->restore(iconGeometry, needComp);
+ if (!display_off && should_be_pinged(i))
+ i->startPing();
+ }
+ if (fd.frame)
+ setWindowState(fd.frame->managedWindow(), NormalState);
+ else
+ setWindowState(event->window, NormalState);
+ if (event->window == stack[DESKTOP_LAYER])
+ // raising home does not have a transition
+ activateWindow(event->window, CurrentTime, true);
+ else
+ activateWindow(event->window, CurrentTime, false);
+ } else if (event->message_type == ATOM(_NET_CLOSE_WINDOW)) {
+ Window close_window = event->window;
+
+ // send WM_DELETE_WINDOW message to actual window that needs to close
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = close_window;
+ ev.xclient.message_type = ATOM(WM_PROTOCOLS);
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = ATOM(WM_DELETE_WINDOW);
+ ev.xclient.data.l[1] = CurrentTime;
+
+ XSendEvent(QX11Info::display(), close_window, False, NoEventMask, &ev);
+ setExposeDesktop(true);
+ XSync(QX11Info::display(), False);
+
+ MCompositeWindow *check_hung = texturePixmapItem(close_window);
+ if (check_hung) {
+ if (check_hung->status() == MCompositeWindow::HUNG) {
+ // destroy at the server level
+ XKillClient(QX11Info::display(), close_window);
+ delete check_hung;
+ MDecoratorFrame::instance()->lower();
+ removeWindow(close_window);
+ return;
+ }
+ }
+ } else if (event->message_type == ATOM(WM_PROTOCOLS)) {
+ if (event->data.l[0] == (long) ATOM(_NET_WM_PING)) {
+ MCompositeWindow *ping_source = texturePixmapItem(event->data.l[2]);
+ if (ping_source) {
+ ping_source->receivedPing(event->data.l[1]);
+ Window managed = MDecoratorFrame::instance()->managedWindow();
+ if (ping_source->window() == managed && !ping_source->needDecoration()) {
+ MDecoratorFrame::instance()->lower();
+ MDecoratorFrame::instance()->setManagedWindow(0);
+ if(!ping_source->hasAlpha())
+ disableCompositing(FORCED);
+ }
+ }
+ }
+ } else if (event->message_type == ATOM(_NET_WM_STATE)) {
+ if (event->data.l[1] == (long) ATOM(_NET_WM_STATE_SKIP_TASKBAR))
+ skiptaskbar_wm_state(event->data.l[0], event->window);
+ else if (event->data.l[1] == (long) ATOM(_NET_WM_STATE_FULLSCREEN))
+ fullscreen_wm_state(this, event->data.l[0], event->window);
+ }
+}
+
+void MCompositeManagerPrivate::clientMessageEvent(XClientMessageEvent *event)
+{
+ // Handle iconify requests
+ if (event->message_type == ATOM(WM_CHANGE_STATE))
+ if (event->data.l[0] == IconicState && event->format == 32) {
+ setWindowState(event->window, IconicState);
+
+ MCompositeWindow *i = texturePixmapItem(event->window);
+ MCompositeWindow *d_item = texturePixmapItem(stack[DESKTOP_LAYER]);
+ if (d_item && i) {
+ d_item->setZValue(i->zValue() - 1);
+
+ Window lower = event->window;
+ setExposeDesktop(false);
+
+ bool needComp = false;
+ if (i->isDirectRendered()) {
+ enableCompositing();
+ needComp = true;
+ }
+ // Delayed transition is only available on platforms
+ // that have selective compositing. This is triggered
+ // when windows are rendered off-screen
+ i->iconify(atom->iconGeometry(lower), needComp);
+ if (i->needDecoration())
+ i->startTransition();
+ i->stopPing();
+ }
+ return;
+ }
+
+ // Handle root messages
+ rootMessageEvent(event);
+}
+
+void MCompositeManagerPrivate::iconifyOnLower(MCompositeWindow *window)
+{
+ if (window->iconifyState() != MCompositeWindow::TransitionIconifyState)
+ return;
+
+ // TODO: (work for more)
+ // Handle minimize request coming from a managed window itself,
+ // if there are any
+ FrameData fd = framed_windows.value(window->window());
+ if (fd.frame) {
+ setWindowState(fd.frame->managedWindow(), IconicState);
+ MCompositeWindow *i = texturePixmapItem(fd.frame->winId());
+ if (i)
+ i->iconify();
+ }
+
+ if (stack[DESKTOP_LAYER]) {
+ enableCompositing();
+ positionWindow(stack[DESKTOP_LAYER], STACK_TOP);
+ if (compositing)
+ possiblyUnredirectTopmostWindow();
+ }
+}
+
+void MCompositeManagerPrivate::raiseOnRestore(MCompositeWindow *window)
+{
+ stack[APPLICATION_LAYER] = window->window();
+ positionWindow(window->window(), STACK_TOP);
+
+ /* the animation is finished, compositing needs to be reconsidered */
+ possiblyUnredirectTopmostWindow();
+}
+
+void MCompositeManagerPrivate::setExposeDesktop(bool exposed)
+{
+ if (stack[DESKTOP_LAYER]) {
+ XVisibilityEvent desk_notify;
+ desk_notify.type = VisibilityNotify;
+ desk_notify.send_event = True;
+ desk_notify.window = stack[DESKTOP_LAYER];
+ desk_notify.state = exposed ? VisibilityUnobscured :
+ VisibilityFullyObscured;
+ XSendEvent(QX11Info::display(), stack[DESKTOP_LAYER], true,
+ VisibilityChangeMask, (XEvent *)&desk_notify);
+ }
+ if (stack[DOCK_LAYER]) {
+ XVisibilityEvent desk_notify;
+ desk_notify.type = VisibilityNotify;
+ desk_notify.send_event = True;
+ desk_notify.window = stack[DOCK_LAYER];
+ desk_notify.state = exposed ? VisibilityUnobscured :
+ VisibilityFullyObscured;
+ XSendEvent(QX11Info::display(), stack[DESKTOP_LAYER], true,
+ VisibilityChangeMask, (XEvent *)&desk_notify);
+ }
+}
+
+// Visibility notification to desktop window. Ensure this is called once
+// transitions are done
+void MCompositeManagerPrivate::exposeDesktop()
+{
+ setExposeDesktop(true);
+}
+
+bool MCompositeManagerPrivate::isSelfManagedFocus(Window w)
+{
+ /* FIXME: store these to the object */
+ XWindowAttributes attr;
+ if (!XGetWindowAttributes(QX11Info::display(), w, &attr))
+ return false;
+ if (attr.override_redirect || atom->windowType(w) == MCompAtoms::INPUT)
+ return false;
+ return true;
+}
+
+void MCompositeManagerPrivate::activateWindow(Window w, Time timestamp,
+ bool disableCompositing)
+{
+ if (MDecoratorFrame::instance()->managedWindow() == w)
+ MDecoratorFrame::instance()->activate();
+
+ if ((atom->windowType(w) != MCompAtoms::DESKTOP) &&
+ (atom->windowType(w) != MCompAtoms::DOCK)) {
+ stack[APPLICATION_LAYER] = w;
+ setExposeDesktop(false);
+ // if this is a transient window, raise the "parent" instead
+ MCompositeWindow *cw = windows.value(w);
+ Window last = getLastVisibleParent(cw);
+ if (last)
+ positionWindow(last, STACK_TOP);
+ else
+ positionWindow(w, STACK_TOP);
+ } else if (w == stack[DESKTOP_LAYER]) {
+ setExposeDesktop(true);
+ positionWindow(w, STACK_TOP);
+ } else
+ checkInputFocus(timestamp);
+
+ /* do this after possibly reordering the window stack */
+ if (disableCompositing)
+ possiblyUnredirectTopmostWindow();
+}
+
+#ifdef GLES2_VERSION
+void MCompositeManagerPrivate::mceDisplayStatusIndSignal(QString mode)
+{
+ if (mode == MCE_DISPLAY_OFF_STRING) {
+ disableCompositing(REALLY_FORCED);
+ display_off = true;
+ /* stop pinging to save some battery */
+ for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
+ it != windows.end(); ++it) {
+ MCompositeWindow *i = it.value();
+ i->stopPing();
+ }
+ } else if (mode == MCE_DISPLAY_ON_STRING) {
+ display_off = false;
+
+ if (!possiblyUnredirectTopmostWindow())
+ enableCompositing(false);
+ /* start pinging again */
+ for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
+ it != windows.end(); ++it) {
+ MCompositeWindow *i = it.value();
+ if (should_be_pinged(i))
+ i->startPing();
+ }
+ }
+}
+#endif
+
+void MCompositeManagerPrivate::setWindowState(Window w, int state)
+{
+ CARD32 d[2];
+ d[0] = state;
+ d[1] = None;
+ XChangeProperty(QX11Info::display(), w, ATOM(WM_STATE), ATOM(WM_STATE),
+ 32, PropModeReplace, (unsigned char *)d, 2);
+}
+
+void MCompositeManagerPrivate::setWindowDebugProperties(Window w)
+{
+#ifdef WINDOW_DEBUG
+ MCompositeWindow *i = texturePixmapItem(w);
+ if (!i)
+ return;
+
+ CARD32 d[1];
+ if (i->windowVisible())
+ d[0] = i->isDirectRendered() ?
+ ATOM(_DUI_WM_WINDOW_DIRECT_VISIBLE) : ATOM(_DUI_WM_WINDOW_COMPOSITED_VISIBLE);
+ else
+ d[0] = i->isDirectRendered() ?
+ ATOM(_DUI_WM_WINDOW_DIRECT_INVISIBLE) : ATOM(_DUI_WM_WINDOW_COMPOSITED_INVISIBLE);
+
+ XChangeProperty(QX11Info::display(), w, ATOM(_DUI_WM_INFO), XA_ATOM,
+ 32, PropModeReplace, (unsigned char *)d, 1);
+ long z = i->zValue();
+ XChangeProperty(QX11Info::display(), w, ATOM(_DUI_WM_WINDOW_ZVALUE), XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) &z, 1);
+
+#else
+ Q_UNUSED(w);
+#endif
+}
+
+bool MCompositeManagerPrivate::x11EventFilter(XEvent *event)
+{
+ static const int damage_ev = damage_event + XDamageNotify;
+
+ if (event->type == damage_ev) {
+ XDamageNotifyEvent *e = reinterpret_cast<XDamageNotifyEvent *>(event);
+ damageEvent(e);
+ return true;
+ }
+ switch (event->type) {
+
+ case DestroyNotify:
+ destroyEvent(&event->xdestroywindow); break;
+ case PropertyNotify:
+ propertyEvent(&event->xproperty); break;
+ case UnmapNotify:
+ unmapEvent(&event->xunmap); break;
+ case ConfigureNotify:
+ configureEvent(&event->xconfigure); break;
+ case ConfigureRequest:
+ configureRequestEvent(&event->xconfigurerequest); break;
+ case MapNotify:
+ mapEvent(&event->xmap); break;
+ case MapRequest:
+ mapRequestEvent(&event->xmaprequest); break;
+ case ClientMessage:
+ clientMessageEvent(&event->xclient); break;
+ case ButtonRelease:
+ case ButtonPress:
+ XAllowEvents(QX11Info::display(), ReplayPointer, event->xbutton.time);
+ activateWindow(event->xbutton.window, event->xbutton.time);
+ // Qt needs to handle this event for the window frame buttons
+ return false;
+ default:
+ return false;
+ }
+ return true;
+}
+
+QGraphicsScene *MCompositeManagerPrivate::scene()
+{
+ return watch;
+}
+
+void MCompositeManagerPrivate::redirectWindows()
+{
+ uint children = 0, i = 0;
+ Window r, p, *kids = 0;
+ XWindowAttributes attr;
+
+ XMapWindow(QX11Info::display(), xoverlay);
+ QDesktopWidget *desk = QApplication::desktop();
+
+ if (!XQueryTree(QX11Info::display(), desk->winId(),
+ &r, &p, &kids, &children)) {
+ qCritical("XQueryTree failed");
+ return;
+ }
+
+ for (i = 0; i < children; ++i) {
+ if (!XGetWindowAttributes(QX11Info::display(), kids[i], &attr))
+ continue;
+ if (attr.map_state == IsViewable &&
+ localwin != kids[i] &&
+ (attr.width > 1 && attr.height > 1)) {
+ bindWindow(kids[i]);
+ if (kids[i] == localwin || kids[i] == parentWindow(localwin))
+ continue;
+ XGrabButton(QX11Info::display(), AnyButton, AnyModifier, kids[i],
+ True,
+ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
+ GrabModeSync, GrabModeSync, None, None);
+ }
+ }
+ if (kids)
+ XFree(kids);
+ scene()->views()[0]->setUpdatesEnabled(true);
+ checkStacking(false);
+}
+
+bool MCompositeManagerPrivate::isRedirected(Window w)
+{
+ return (texturePixmapItem(w) != 0);
+}
+
+MCompositeWindow *MCompositeManagerPrivate::texturePixmapItem(Window w)
+{
+ return windows.value(w, 0);
+}
+
+bool MCompositeManagerPrivate::removeWindow(Window w)
+{
+ windows_as_mapped.removeAll(w);
+ if (windows.remove(w) == 0)
+ return false;
+
+ stacking_list.removeAll(w);
+
+ for (int i = 0; i < TOTAL_LAYERS; ++i)
+ if (stack[i] == w) stack[i] = 0;
+
+ updateWinList();
+ return true;
+}
+
+MCompositeWindow *MCompositeManagerPrivate::bindWindow(Window window)
+{
+ Display *display = QX11Info::display();
+ bool is_decorator = atom->isDecorator(window);
+
+ XSelectInput(display, window, StructureNotifyMask | PropertyChangeMask | ButtonPressMask);
+ XCompositeRedirectWindow(display, window, CompositeRedirectManual);
+
+ MCompAtoms::Type wtype = atom->windowType(window);
+ MTexturePixmapItem *item = new MTexturePixmapItem(window, glwidget);
+ item->setZValue(wtype);
+ item->saveState();
+ item->setIsMapped(true);
+ windows[window] = item;
+
+ if (!is_decorator && !item->isOverrideRedirect())
+ windows_as_mapped.append(window);
+
+ if (needDecoration(window)) {
+ item->setDecorated(true);
+ MDecoratorFrame::instance()->setManagedWindow(window);
+ }
+ item->setIsDecorator(is_decorator);
+
+ if (!display_off)
+ item->updateWindowPixmap();
+ stacking_list.append(window);
+ addItem(item);
+ item->setWindowTypeAtom(atom->getType(window));
+
+ if (wtype == MCompAtoms::INPUT) {
+ if (!display_off)
+ item->updateWindowPixmap();
+ return item;
+ } else if (wtype == MCompAtoms::DESKTOP) {
+ // just in case startup sequence changes
+ stack[DESKTOP_LAYER] = window;
+ connect(this, SIGNAL(inputEnabled()), item,
+ SLOT(setUnBlurred()));
+ return item;
+ }
+
+ item->manipulationEnabled(true);
+
+ // the decorator got mapped. this is here because the compositor
+ // could be restarted at any point
+ if (is_decorator && !MDecoratorFrame::instance()->decoratorItem()) {
+ // initially update the texture for this window because this
+ // will be hidden on first show
+ if (!display_off)
+ item->updateWindowPixmap();
+ item->setVisible(false);
+ MDecoratorFrame::instance()->setDecoratorItem(item);
+ } else
+ item->setVisible(true);
+
+ XWMHints *h = XGetWMHints(display, window);
+ if (h) {
+ if ((h->flags & StateHint) && (h->initial_state == IconicState)) {
+ setWindowState(window, IconicState);
+ }
+ XFree(h);
+ }
+
+ checkStacking(false);
+
+ if (!display_off && should_be_pinged(item))
+ item->startPing();
+
+ return item;
+}
+
+void MCompositeManagerPrivate::addItem(MCompositeWindow *item)
+{
+ watch->addItem(item);
+ updateWinList();
+ setWindowDebugProperties(item->window());
+ connect(item, SIGNAL(acceptingInput()), SLOT(enableInput()));
+
+ if (atom->windowType(item->window()) == MCompAtoms::DESKTOP)
+ return;
+
+ connect(item, SIGNAL(itemIconified(MCompositeWindow *)), SLOT(exposeDesktop()));
+ connect(this, SIGNAL(compositingEnabled()), item, SLOT(startTransition()));
+ connect(item, SIGNAL(itemRestored(MCompositeWindow *)), SLOT(raiseOnRestore(MCompositeWindow *)));
+ connect(item, SIGNAL(itemIconified(MCompositeWindow *)), SLOT(iconifyOnLower(MCompositeWindow *)));
+
+ // ping protocol
+ connect(item, SIGNAL(windowHung(MCompositeWindow *)),
+ SLOT(gotHungWindow(MCompositeWindow *)));
+
+ connect(item, SIGNAL(pingTriggered(MCompositeWindow *)),
+ SLOT(sendPing(MCompositeWindow *)));
+}
+
+void MCompositeManagerPrivate::updateWinList(bool stackingOnly)
+{
+ if (!stackingOnly) {
+ static QList<Window> prev_list;
+ /* windows_as_mapped may contain invisible windows, so we need to
+ * make a new list without them */
+ QList<Window> new_list;
+ for (int i = 0; i < windows_as_mapped.size(); ++i) {
+ Window w = windows_as_mapped.at(i);
+ MCompositeWindow *d = windows.value(w);
+ if (d->isMapped()) new_list.append(w);
+ }
+
+ if (new_list != prev_list) {
+ XChangeProperty(QX11Info::display(),
+ RootWindow(QX11Info::display(), 0),
+ ATOM(_NET_CLIENT_LIST),
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *)new_list.toVector().data(),
+ new_list.size());
+
+ prev_list = QList<Window>(new_list);
+ }
+ }
+ checkStacking(false);
+}
+
+/*!
+ Helper function to arrange arrange the order of the windows
+ in the _NET_CLIENT_LIST_STACKING
+*/
+void
+MCompositeManagerPrivate::positionWindow(Window w,
+ MCompositeManagerPrivate::StackPosition pos)
+{
+ int wp = stacking_list.indexOf(w);
+ if (wp == -1 || wp >= stacking_list.size())
+ return;
+
+ switch (pos) {
+ case STACK_BOTTOM: {
+ //qDebug() << __func__ << "to bottom:" << w;
+ stacking_list.move(wp, 0);
+ break;
+ }
+ case STACK_TOP: {
+ //qDebug() << __func__ << "to top:" << w;
+ stacking_list.move(wp, stacking_list.size() - 1);
+ break;
+ }
+ default:
+ break;
+
+ }
+ updateWinList(true);
+}
+
+void MCompositeManagerPrivate::enableCompositing(bool forced,
+ bool ignore_display_off)
+{
+ if ((!ignore_display_off && display_off) || (compositing && !forced))
+ return;
+
+ XWindowAttributes a;
+ if (!XGetWindowAttributes(QX11Info::display(), xoverlay, &a)) {
+ qCritical("XGetWindowAttributes for the overlay failed");
+ return;
+ }
+ if (a.map_state == IsUnmapped)
+ mapOverlayWindow();
+ else
+ enableRedirection();
+}
+
+void MCompositeManagerPrivate::mapOverlayWindow()
+{
+ // Render the previous contents of the framebuffer
+ // here exactly before compositing was enabled
+ // Ensure the changes are visualized immediately
+
+ QPainter m(glwidget);
+ m.drawPixmap(0, 0, QPixmap::grabWindow(QX11Info::appRootWindow()));
+ glwidget->update();
+ QCoreApplication::flush();
+
+ // Freeze painting of framebuffer as of this point
+ scene()->views()[0]->setUpdatesEnabled(false);
+ XMoveWindow(QX11Info::display(), localwin, -2, -2);
+ XMapWindow(QX11Info::display(), xoverlay);
+}
+
+void MCompositeManagerPrivate::enableRedirection()
+{
+ for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
+ it != windows.end(); ++it) {
+ MCompositeWindow *tp = it.value();
+ if (tp->windowVisible())
+ ((MTexturePixmapItem *)tp)->enableRedirectedRendering();
+ setWindowDebugProperties(it.key());
+ }
+ glwidget->update();
+ compositing = true;
+ /* send VisibilityNotifies */
+ checkStacking(true);
+
+ scene()->views()[0]->setUpdatesEnabled(true);
+
+ usleep(50000);
+ // At this point everything should be rendered off-screen
+ emit compositingEnabled();
+}
+
+void MCompositeManagerPrivate::disableCompositing(ForcingLevel forced)
+{
+ if (MCompositeWindow::isTransitioning() && forced != REALLY_FORCED)
+ return;
+ if (!compositing && forced == NO_FORCED)
+ return;
+
+ if (forced != REALLY_FORCED)
+ // we could still have existing decorator on-screen.
+ // ensure we don't accidentally disturb it
+ for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
+ it != windows.end(); ++it) {
+ MCompositeWindow *i = it.value();
+ if (i->isDecorator())
+ continue;
+ if (i->windowVisible() && (i->hasAlpha() || i->needDecoration()))
+ return;
+ }
+
+ scene()->views()[0]->setUpdatesEnabled(false);
+
+ for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
+ it != windows.end(); ++it) {
+ MCompositeWindow *tp = it.value();
+ // checks above fail. somehow decorator got in. stop it at this point
+ if (!tp->isDecorator() && !tp->isIconified() && !tp->hasAlpha())
+ ((MTexturePixmapItem *)tp)->enableDirectFbRendering();
+ setWindowDebugProperties(it.key());
+
+ /* Mark all windows obscured so that Unobscured events are sent when
+ * compositing is back on and we synthesize them. */
+ tp->setWindowObscured(true, true);
+ }
+
+ XSync(QX11Info::display(), False);
+ if (forced != REALLY_FORCED)
+ usleep(50000);
+
+ XUnmapWindow(QX11Info::display(), xoverlay);
+ XFlush(QX11Info::display());
+
+ if (MDecoratorFrame::instance()->decoratorItem())
+ MDecoratorFrame::instance()->lower();
+
+ compositing = false;
+}
+
+void MCompositeManagerPrivate::sendPing(MCompositeWindow *w)
+{
+ Window window = ((MCompositeWindow *) w)->window();
+ ulong t = QDateTime::currentDateTime().toTime_t();
+ w->setClientTimeStamp(t);
+
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.window = window;
+ ev.xclient.message_type = ATOM(WM_PROTOCOLS);
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = ATOM(_NET_WM_PING);
+ ev.xclient.data.l[1] = t;
+ ev.xclient.data.l[2] = window;
+
+ XSendEvent(QX11Info::display(), window, False, NoEventMask, &ev);
+ XSync(QX11Info::display(), False);
+}
+
+void MCompositeManagerPrivate::gotHungWindow(MCompositeWindow *w)
+{
+ if (!MDecoratorFrame::instance()->decoratorItem())
+ return;
+
+ enableCompositing(true);
+
+ // own the window so we could kill it if we want to.
+ MDecoratorFrame::instance()->setManagedWindow(w->window());
+ checkStacking(false);
+ MDecoratorFrame::instance()->raise();
+ w->updateWindowPixmap();
+}
+
+void MCompositeManagerPrivate::showLaunchIndicator(int timeout)
+{
+ if (!launchIndicator) {
+ launchIndicator = new QGraphicsTextItem("launching...");
+ scene()->addItem(launchIndicator);
+ launchIndicator->setPos((scene()->sceneRect().width() / 2) -
+ - (launchIndicator->boundingRect().width() / 2),
+ (scene()->sceneRect().height() / 2) -
+ (launchIndicator->boundingRect().width() / 2));
+ }
+ launchIndicator->show();
+ QTimer::singleShot(timeout * 1000, this, SLOT(hideLaunchIndicator()));
+}
+
+void MCompositeManagerPrivate::hideLaunchIndicator()
+{
+ if (launchIndicator)
+ launchIndicator->hide();
+}
+
+MCompositeManager::MCompositeManager(int &argc, char **argv)
+ : QApplication(argc, argv)
+{
+ if (QX11Info::isCompositingManagerRunning()) {
+ qCritical("Compositing manager already running.");
+ ::exit(0);
+ }
+
+ d = new MCompositeManagerPrivate(this);
+ MRmiServer *s = new MRmiServer(".mcompositor", this);
+ s->exportObject(this);
+}
+
+MCompositeManager::~MCompositeManager()
+{
+ delete d;
+ d = 0;
+}
+
+void MCompositeManager::setGLWidget(QGLWidget *glw)
+{
+ glw->setAttribute(Qt::WA_PaintOutsidePaintEvent);
+ d->glwidget = glw;
+}
+
+QGraphicsScene *MCompositeManager::scene()
+{
+ return d->scene();
+}
+
+void MCompositeManager::prepareEvents()
+{
+ d->prepare();
+}
+
+bool MCompositeManager::x11EventFilter(XEvent *event)
+{
+ return d->x11EventFilter(event);
+}
+
+void MCompositeManager::setSurfaceWindow(Qt::HANDLE window)
+{
+ d->localwin = window;
+}
+
+void MCompositeManager::redirectWindows()
+{
+ d->redirectWindows();
+}
+
+bool MCompositeManager::isRedirected(Qt::HANDLE w)
+{
+ return d->isRedirected(w);
+}
+
+void MCompositeManager::enableCompositing()
+{
+ d->enableCompositing();
+}
+
+void MCompositeManager::disableCompositing()
+{
+ d->disableCompositing();
+}
+
+void MCompositeManager::showLaunchIndicator(int timeout)
+{
+ d->showLaunchIndicator(timeout);
+}
+
+void MCompositeManager::hideLaunchIndicator()
+{
+ d->hideLaunchIndicator();
+}
+
+void MCompositeManager::topmostWindowsRaise()
+{
+ d->checkStacking(false);
+}