[meego-commits] 10763: Changes to Trunk:Testing/meegotouch-compositor
Fathi Boudra
no_reply at build.meego.com
Thu Dec 9 10:17:08 UTC 2010
Hi,
I have made the following changes to meegotouch-compositor in project Trunk:Testing. Please review and accept ASAP.
Thank You,
Fathi Boudra
[This message was auto-generated]
---
Request #10763:
submit: devel:qt-mtf/meegotouch-compositor(r49) -> Trunk:Testing/meegotouch-compositor
Message:
* Thu Dec 09 2010 Fathi Boudra <fathi.boudra at nokia.com> - 0.8.0
- Update to 0.8.0-2 (BMC#11090)
* Wed Nov 09 2010 Fathi Boudra <fathi.boudra at nokia.com> - 0.7.8
- Remove gl build requirement
* Wed Nov 09 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.8
- BMC#8957: - mcompositor 0.7.6 core dumps on netbook
-fixed and tested on netbook
* Tue Nov 03 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.8
- updated to release 0.7.8~1
- added pkgconfig(xrandr), build requires it
* Tue Nov 03 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.7
- updated to release 0.7.7-1
* Tue Oct 26 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.6
- updated to release 0.7.6-1
- removed obsolete plankton theme
* Tue Oct 19 2010 Miroslav Safr <mirsolav.safr at tieto.com> - 0.7.5
- updated to 0.7.5-1 (with build fix)
- updated initialize_EGL_library.patch
* Fri Oct 08 2010 Miroslav Safr <mirsolav.safr at tieto.com> - 0.7.3
- Update to 0.7.3
- removed integrated patches:
mcompositor-black-screen-gtk.patch
re-add_fallback_when_Image_extension_is_not_available.patch
- added %{_libdir}/libmcompositor.so* and includes
State: new 2010-12-09T02:17:06 boudra
Comment: None
changes files:
--------------
--- meegotouch-compositor.changes
+++ meegotouch-compositor.changes
@@ -0,0 +1,33 @@
+* Thu Dec 09 2010 Fathi Boudra <fathi.boudra at nokia.com> - 0.8.0
+- Update to 0.8.0-2 (BMC#11090)
+
+* Wed Nov 09 2010 Fathi Boudra <fathi.boudra at nokia.com> - 0.7.8
+- Remove gl build requirement
+
+* Wed Nov 09 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.8
+- BMC#8957: - mcompositor 0.7.6 core dumps on netbook
+ -fixed and tested on netbook
+
+* Tue Nov 03 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.8
+- updated to release 0.7.8~1
+- added pkgconfig(xrandr), build requires it
+
+* Tue Nov 03 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.7
+- updated to release 0.7.7-1
+
+* Tue Oct 26 2010 Miroslav Safr <miroslav.safr at tieto.com> - 0.7.6
+- updated to release 0.7.6-1
+- removed obsolete plankton theme
+
+* Tue Oct 19 2010 Miroslav Safr <mirsolav.safr at tieto.com> - 0.7.5
+- updated to 0.7.5-1 (with build fix)
+- updated initialize_EGL_library.patch
+
+* Fri Oct 08 2010 Miroslav Safr <mirsolav.safr at tieto.com> - 0.7.3
+- Update to 0.7.3
+- removed integrated patches:
+ mcompositor-black-screen-gtk.patch
+ re-add_fallback_when_Image_extension_is_not_available.patch
+- added %{_libdir}/libmcompositor.so* and includes
+
+
old:
----
mcompositor-black-screen-gtk.patch
meegotouch-compositor-0.5.8.tar.bz2
re-add_fallback_when_Image_extension_is_not_available.patch
new:
----
meegotouch-compositor-0.8.0.tar.gz
spec files:
-----------
--- meegotouch-compositor.spec
+++ meegotouch-compositor.spec
@@ -1,39 +1,37 @@
#
# Do NOT Edit the Auto-generated Part!
-# Generated by: spectacle version 0.20
+# Generated by: spectacle version 0.21
#
# >> macros
# << macros
Name: meegotouch-compositor
Summary: MeeGo UI Compositing Window Manager
-Version: 0.5.8
+Version: 0.8.0
Release: 1
Group: System/Desktop
License: LGPLv2.1
URL: http://meego.gitorious.org/meegotouch/meegotouch-compositor
-Source0: %{name}-%{version}.tar.bz2
+Source0: %{name}-%{version}.tar.gz
Source1: mdecorator.desktop
Source100: meegotouch-compositor.yaml
Patch0: add_Xext_lib_to_windowctl.patch
Patch1: fix_test_compile_issues.patch
Patch2: fix_build_on_ARM.patch
Patch3: initialize_EGL_library.patch
-Patch4: mcompositor-black-screen-gtk.patch
-Patch5: re-add_fallback_when_Image_extension_is_not_available.patch
Requires(post): /sbin/ldconfig
Requires(postun): /sbin/ldconfig
BuildRequires: pkgconfig(QtDBus)
BuildRequires: pkgconfig(QtNetwork)
BuildRequires: pkgconfig(QtOpenGL)
BuildRequires: pkgconfig(contextprovider-1.0)
-BuildRequires: pkgconfig(gl)
BuildRequires: pkgconfig(meegotouch)
BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(xcomposite)
BuildRequires: pkgconfig(xdamage)
BuildRequires: pkgconfig(xext)
BuildRequires: pkgconfig(xrender)
+BuildRequires: pkgconfig(xrandr)
Provides: duicompositor >= 0.3.9
Provides: mcompositor >= 0.4.6
Obsoletes: duicompositor < 0.3.9
@@ -67,10 +65,6 @@
%patch2 -p1
# initialize_EGL_library.patch
%patch3 -p1
-# mcompositor-black-screen-gtk.patch
-%patch4 -p1
-# re-add_fallback_when_Image_extension_is_not_available.patch
-%patch5 -p1
# >> setup
# << setup
@@ -89,10 +83,11 @@
# >> install pre
# << install pre
%qmake_install
+mkdir -p %{buildroot}%{_sysconfdir}/xdg/autostart
+cp -a %{SOURCE1} %{buildroot}%{_sysconfdir}/xdg/autostart
+
# >> install post
-mkdir -p %{buildroot}%{_sysconfdir}/xdg/autostart/
-cp %{SOURCE1} %{buildroot}%{_sysconfdir}/xdg/autostart/
# << install post
@@ -112,6 +107,7 @@
%{_bindir}/mcompositor
%{_bindir}/mdecorator
%{_libdir}/libdecorator.so.*
+%{_libdir}/libmcompositor.so.*
# << files
@@ -122,5 +118,14 @@
%{_bindir}/windowctl
%{_bindir}/windowstack
%{_libdir}/libdecorator.so
+%{_libdir}/libmcompositor.so
+%{_includedir}/meegotouch/mcompositor/mcompatoms_p.h
+%{_includedir}/meegotouch/mcompositor/mcompmgrextensionfactory.h
+%{_includedir}/meegotouch/mcompositor/mcompositemanager.h
+%{_includedir}/meegotouch/mcompositor/mcompositemanagerextension.h
+%{_includedir}/meegotouch/mcompositor/mcompositewindow.h
+%{_includedir}/meegotouch/mcompositor/mcompositewindowgroup.h
+%{_includedir}/meegotouch/mcompositor/mcompositewindowshadereffect.h
+%{_includedir}/meegotouch/mcompositor/mwindowpropertycache.h
# << files devel
other changes:
--------------
++++++ initialize_EGL_library.patch
--- initialize_EGL_library.patch
+++ initialize_EGL_library.patch
@@ -5,12 +5,9 @@
More correct, and needed on some platforms.
---
- src/mtexturepixmapitem_egl.cpp | 8 ++++++--
- 1 files changed, 6 insertions(+), 2 deletions(-)
-
--- a/src/mtexturepixmapitem_egl.cpp
+++ b/src/mtexturepixmapitem_egl.cpp
-@@ -85,8 +85,11 @@ class EglResourceManager
+@@ -86,8 +86,11 @@ class EglResourceManager
public:
EglResourceManager()
: has_tfp(false) {
@@ -23,11 +20,11 @@
QString exts = QLatin1String(eglQueryString(dpy, EGL_EXTENSIONS));
if ((exts.contains("EGL_KHR_image") &&
-@@ -97,6 +100,7 @@ public:
+@@ -98,6 +101,7 @@ public:
eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR");
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress("glEGLImageTargetTexture2DOES");
} else {
-+ qCritical("EGL version: %d.%d\n", maj, min);
- qCritical() << "EGL extensions:" << exts;
- qFatal("no EGL tfp support, aborting\n");
++ qDebug("EGL version: %d.%d\n", maj, min);
+ qDebug("No EGL tfp support.\n");
}
+ texman = new EglTextureManager();
++++++ meegotouch-compositor-0.5.8.tar.bz2 -> meegotouch-compositor-0.8.0.tar.gz
--- .git
+++ .git
-(directory)
--- .git/HEAD
+++ .git/HEAD
-4c90d59ca5d13bcdc1f62e4fb4d2d36fb6547a4a
--- .git/branches
+++ .git/branches
-(directory)
--- .git/config
+++ .git/config
-[core]
- repositoryformatversion = 0
- filemode = true
- bare = false
- logallrefupdates = true
-[remote "origin"]
- fetch = +refs/heads/*:refs/remotes/origin/*
- url = git://gitorious.org/meegotouch/meegotouch-compositor.git
-[branch "master"]
- remote = origin
- merge = refs/heads/master
--- .git/description
+++ .git/description
-Unnamed repository; edit this file 'description' to name the repository.
--- .git/hooks
+++ .git/hooks
-(directory)
--- .git/hooks/applypatch-msg.sample
+++ .git/hooks/applypatch-msg.sample
-#!/bin/sh
-#
-# An example hook script to check the commit log message taken by
-# applypatch from an e-mail message.
-#
-# The hook should exit with non-zero status after issuing an
-# appropriate message if it wants to stop the commit. The hook is
-# allowed to edit the commit message file.
-#
-# To enable this hook, rename this file to "applypatch-msg".
-
-. git-sh-setup
-test -x "$GIT_DIR/hooks/commit-msg" &&
- exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
-:
--- .git/hooks/commit-msg.sample
+++ .git/hooks/commit-msg.sample
-#!/bin/sh
-#
-# An example hook script to check the commit log message.
-# Called by "git commit" with one argument, the name of the file
-# that has the commit message. The hook should exit with non-zero
-# status after issuing an appropriate message if it wants to stop the
-# commit. The hook is allowed to edit the commit message file.
-#
-# To enable this hook, rename this file to "commit-msg".
-
-# Uncomment the below to add a Signed-off-by line to the message.
-# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
-# hook is more suited to it.
-#
-# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
-# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
-
-# This example catches duplicate Signed-off-by lines.
-
-test "" = "$(grep '^Signed-off-by: ' "$1" |
- sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
- echo >&2 Duplicate Signed-off-by lines.
- exit 1
-}
--- .git/hooks/post-commit.sample
+++ .git/hooks/post-commit.sample
-#!/bin/sh
-#
-# An example hook script that is called after a successful
-# commit is made.
-#
-# To enable this hook, rename this file to "post-commit".
-
-: Nothing
--- .git/hooks/post-receive.sample
+++ .git/hooks/post-receive.sample
-#!/bin/sh
-#
-# An example hook script for the "post-receive" event.
-#
-# The "post-receive" script is run after receive-pack has accepted a pack
-# and the repository has been updated. It is passed arguments in through
-# stdin in the form
-# <oldrev> <newrev> <refname>
-# For example:
-# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
-#
-# see contrib/hooks/ for a sample, or uncomment the next line and
-# rename the file to "post-receive".
-
-#. /usr/share/git-core/contrib/hooks/post-receive-email
--- .git/hooks/post-update.sample
+++ .git/hooks/post-update.sample
-#!/bin/sh
-#
-# An example hook script to prepare a packed repository for use over
-# dumb transports.
-#
-# To enable this hook, rename this file to "post-update".
-
-exec git update-server-info
--- .git/hooks/pre-applypatch.sample
+++ .git/hooks/pre-applypatch.sample
-#!/bin/sh
-#
-# An example hook script to verify what is about to be committed
-# by applypatch from an e-mail message.
-#
-# The hook should exit with non-zero status after issuing an
-# appropriate message if it wants to stop the commit.
-#
-# To enable this hook, rename this file to "pre-applypatch".
-
-. git-sh-setup
-test -x "$GIT_DIR/hooks/pre-commit" &&
- exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
-:
--- .git/hooks/pre-commit.sample
+++ .git/hooks/pre-commit.sample
-#!/bin/sh
-#
-# An example hook script to verify what is about to be committed.
-# Called by "git commit" with no arguments. The hook should
-# exit with non-zero status after issuing an appropriate message if
-# it wants to stop the commit.
-#
-# To enable this hook, rename this file to "pre-commit".
-
-if git rev-parse --verify HEAD >/dev/null 2>&1
-then
- against=HEAD
-else
- # Initial commit: diff against an empty tree object
- against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
-fi
-
-# If you want to allow non-ascii filenames set this variable to true.
-allownonascii=$(git config hooks.allownonascii)
-
-# Cross platform projects tend to avoid non-ascii filenames; prevent
-# them from being added to the repository. We exploit the fact that the
-# printable range starts at the space character and ends with tilde.
-if [ "$allownonascii" != "true" ] &&
- # Note that the use of brackets around a tr range is ok here, (it's
- # even required, for portability to Solaris 10's /usr/bin/tr), since
- # the square bracket bytes happen to fall in the designated range.
- test "$(git diff --cached --name-only --diff-filter=A -z $against |
- LC_ALL=C tr -d '[ -~]\0')"
-then
- echo "Error: Attempt to add a non-ascii file name."
- echo
- echo "This can cause problems if you want to work"
- echo "with people on other platforms."
- echo
- echo "To be portable it is advisable to rename the file ..."
- echo
- echo "If you know what you are doing you can disable this"
- echo "check using:"
- echo
- echo " git config hooks.allownonascii true"
- echo
- exit 1
-fi
-
-exec git diff-index --check --cached $against --
--- .git/hooks/pre-rebase.sample
+++ .git/hooks/pre-rebase.sample
-#!/bin/sh
-#
-# Copyright (c) 2006, 2008 Junio C Hamano
-#
-# The "pre-rebase" hook is run just before "git rebase" starts doing
-# its job, and can prevent the command from running by exiting with
-# non-zero status.
-#
-# The hook is called with the following parameters:
-#
-# $1 -- the upstream the series was forked from.
-# $2 -- the branch being rebased (or empty when rebasing the current branch).
-#
-# This sample shows how to prevent topic branches that are already
-# merged to 'next' branch from getting rebased, because allowing it
-# would result in rebasing already published history.
-
-publish=next
-basebranch="$1"
-if test "$#" = 2
-then
- topic="refs/heads/$2"
-else
- topic=`git symbolic-ref HEAD` ||
- exit 0 ;# we do not interrupt rebasing detached HEAD
-fi
-
-case "$topic" in
-refs/heads/??/*)
- ;;
-*)
- exit 0 ;# we do not interrupt others.
- ;;
-esac
-
-# Now we are dealing with a topic branch being rebased
-# on top of master. Is it OK to rebase it?
-
-# Does the topic really exist?
-git show-ref -q "$topic" || {
- echo >&2 "No such branch $topic"
- exit 1
-}
-
-# Is topic fully merged to master?
-not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
-if test -z "$not_in_master"
-then
- echo >&2 "$topic is fully merged to master; better remove it."
- exit 1 ;# we could allow it, but there is no point.
-fi
-
-# Is topic ever merged to next? If so you should not be rebasing it.
-only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
-only_next_2=`git rev-list ^master ${publish} | sort`
-if test "$only_next_1" = "$only_next_2"
-then
- not_in_topic=`git rev-list "^$topic" master`
- if test -z "$not_in_topic"
- then
- echo >&2 "$topic is already up-to-date with master"
- exit 1 ;# we could allow it, but there is no point.
- else
- exit 0
- fi
-else
- not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
- /usr/bin/perl -e '
- my $topic = $ARGV[0];
- my $msg = "* $topic has commits already merged to public branch:\n";
- my (%not_in_next) = map {
- /^([0-9a-f]+) /;
- ($1 => 1);
- } split(/\n/, $ARGV[1]);
- for my $elem (map {
- /^([0-9a-f]+) (.*)$/;
- [$1 => $2];
- } split(/\n/, $ARGV[2])) {
- if (!exists $not_in_next{$elem->[0]}) {
- if ($msg) {
- print STDERR $msg;
- undef $msg;
- }
- print STDERR " $elem->[1]\n";
- }
- }
- ' "$topic" "$not_in_next" "$not_in_master"
- exit 1
-fi
-
-exit 0
-
-################################################################
-
-This sample hook safeguards topic branches that have been
-published from being rewound.
-
-The workflow assumed here is:
-
- * Once a topic branch forks from "master", "master" is never
- merged into it again (either directly or indirectly).
-
- * Once a topic branch is fully cooked and merged into "master",
- it is deleted. If you need to build on top of it to correct
- earlier mistakes, a new topic branch is created by forking at
- the tip of the "master". This is not strictly necessary, but
- it makes it easier to keep your history simple.
-
- * Whenever you need to test or publish your changes to topic
- branches, merge them into "next" branch.
-
-The script, being an example, hardcodes the publish branch name
-to be "next", but it is trivial to make it configurable via
-$GIT_DIR/config mechanism.
-
-With this workflow, you would want to know:
-
-(1) ... if a topic branch has ever been merged to "next". Young
- topic branches can have stupid mistakes you would rather
- clean up before publishing, and things that have not been
- merged into other branches can be easily rebased without
- affecting other people. But once it is published, you would
- not want to rewind it.
-
-(2) ... if a topic branch has been fully merged to "master".
- Then you can delete it. More importantly, you should not
- build on top of it -- other people may already want to
- change things related to the topic as patches against your
- "master", so if you need further changes, it is better to
- fork the topic (perhaps with the same name) afresh from the
- tip of "master".
-
-Let's look at this example:
-
- o---o---o---o---o---o---o---o---o---o "next"
- / / / /
- / a---a---b A / /
- / / / /
- / / c---c---c---c B /
- / / / \ /
- / / / b---b C \ /
- / / / / \ /
- ---o---o---o---o---o---o---o---o---o---o---o "master"
-
-
-A, B and C are topic branches.
-
- * A has one fix since it was merged up to "next".
-
- * B has finished. It has been fully merged up to "master" and "next",
- and is ready to be deleted.
-
- * C has not merged to "next" at all.
-
-We would want to allow C to be rebased, refuse A, and encourage
-B to be deleted.
-
-To compute (1):
-
- git rev-list ^master ^topic next
- git rev-list ^master next
-
- if these match, topic has not merged in next at all.
-
-To compute (2):
-
- git rev-list master..topic
-
- if this is empty, it is fully merged to "master".
--- .git/hooks/prepare-commit-msg.sample
+++ .git/hooks/prepare-commit-msg.sample
-#!/bin/sh
-#
-# An example hook script to prepare the commit log message.
-# Called by "git commit" with the name of the file that has the
-# commit message, followed by the description of the commit
-# message's source. The hook's purpose is to edit the commit
-# message file. If the hook fails with a non-zero status,
-# the commit is aborted.
-#
-# To enable this hook, rename this file to "prepare-commit-msg".
-
-# This hook includes three examples. The first comments out the
-# "Conflicts:" part of a merge commit.
-#
-# The second includes the output of "git diff --name-status -r"
-# into the message, just before the "git status" output. It is
-# commented because it doesn't cope with --amend or with squashed
-# commits.
-#
-# The third example adds a Signed-off-by line to the message, that can
-# still be edited. This is rarely a good idea.
-
-case "$2,$3" in
- merge,)
- /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
-
-# ,|template,)
-# /usr/bin/perl -i.bak -pe '
-# print "\n" . `git diff --cached --name-status -r`
-# if /^#/ && $first++ == 0' "$1" ;;
-
- *) ;;
-esac
-
-# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
-# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
--- .git/hooks/update.sample
+++ .git/hooks/update.sample
-#!/bin/sh
-#
-# An example hook script to blocks unannotated tags from entering.
-# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
-#
-# To enable this hook, rename this file to "update".
-#
-# Config
-# ------
-# hooks.allowunannotated
-# This boolean sets whether unannotated tags will be allowed into the
-# repository. By default they won't be.
-# hooks.allowdeletetag
-# This boolean sets whether deleting tags will be allowed in the
-# repository. By default they won't be.
-# hooks.allowmodifytag
-# This boolean sets whether a tag may be modified after creation. By default
-# it won't be.
-# hooks.allowdeletebranch
-# This boolean sets whether deleting branches will be allowed in the
-# repository. By default they won't be.
-# hooks.denycreatebranch
-# This boolean sets whether remotely creating branches will be denied
-# in the repository. By default this is allowed.
-#
-
-# --- Command line
-refname="$1"
-oldrev="$2"
-newrev="$3"
-
-# --- Safety check
-if [ -z "$GIT_DIR" ]; then
- echo "Don't run this script from the command line." >&2
- echo " (if you want, you could supply GIT_DIR then run" >&2
- echo " $0 <ref> <oldrev> <newrev>)" >&2
- exit 1
-fi
-
-if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
- echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
- exit 1
-fi
-
-# --- Config
-allowunannotated=$(git config --bool hooks.allowunannotated)
-allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
-denycreatebranch=$(git config --bool hooks.denycreatebranch)
-allowdeletetag=$(git config --bool hooks.allowdeletetag)
-allowmodifytag=$(git config --bool hooks.allowmodifytag)
-
-# check for no description
-projectdesc=$(sed -e '1q' "$GIT_DIR/description")
-case "$projectdesc" in
-"Unnamed repository"* | "")
- echo "*** Project description file hasn't been set" >&2
- exit 1
- ;;
-esac
-
-# --- Check types
-# if $newrev is 0000...0000, it's a commit to delete a ref.
-zero="0000000000000000000000000000000000000000"
-if [ "$newrev" = "$zero" ]; then
- newrev_type=delete
-else
- newrev_type=$(git cat-file -t $newrev)
-fi
-
-case "$refname","$newrev_type" in
- refs/tags/*,commit)
- # un-annotated tag
- short_refname=${refname##refs/tags/}
- if [ "$allowunannotated" != "true" ]; then
- echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
- echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
- exit 1
- fi
- ;;
- refs/tags/*,delete)
- # delete tag
- if [ "$allowdeletetag" != "true" ]; then
- echo "*** Deleting a tag is not allowed in this repository" >&2
- exit 1
- fi
- ;;
- refs/tags/*,tag)
- # annotated tag
- if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
- then
- echo "*** Tag '$refname' already exists." >&2
- echo "*** Modifying a tag is not allowed in this repository." >&2
- exit 1
- fi
- ;;
- refs/heads/*,commit)
- # branch
- if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
- echo "*** Creating a branch is not allowed in this repository" >&2
- exit 1
- fi
- ;;
- refs/heads/*,delete)
- # delete branch
- if [ "$allowdeletebranch" != "true" ]; then
- echo "*** Deleting a branch is not allowed in this repository" >&2
- exit 1
- fi
- ;;
- refs/remotes/*,commit)
- # tracking branch
- ;;
- refs/remotes/*,delete)
- # delete tracking branch
- if [ "$allowdeletebranch" != "true" ]; then
- echo "*** Deleting a tracking branch is not allowed in this repository" >&2
- exit 1
- fi
- ;;
- *)
- # Anything else (is there anything else?)
- echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
- exit 1
- ;;
-esac
-
-# --- Finished
-exit 0
--- .git/info
+++ .git/info
-(directory)
--- .git/info/exclude
+++ .git/info/exclude
-# git ls-files --others --exclude-from=.git/info/exclude
-# Lines that start with '#' are comments.
-# For a project mostly in C, the following would be a good set of
-# exclude patterns (uncomment them if you want to use them):
-# *.[oa]
-# *~
--- .git/logs
+++ .git/logs
-(directory)
--- .git/logs/HEAD
+++ .git/logs/HEAD
-0000000000000000000000000000000000000000 485277ebf58de88f908f2a4103f3c5c2b79bd3de kaitlin <kaitlin at usagi.intel.com> 1286232856 -0700 clone: from git://gitorious.org/meegotouch/meegotouch-compositor.git
-485277ebf58de88f908f2a4103f3c5c2b79bd3de 679b0a07b8e6c81af549ea7191c75963f90b7262 kaitlin <kaitlin at usagi.intel.com> 1286232904 -0700 checkout: moving from master to 0.7.0-1
-679b0a07b8e6c81af549ea7191c75963f90b7262 e7bd46b423eeef5e2a2611175f2f8b29796358a7 kaitlin <kaitlin at usagi.intel.com> 1286233241 -0700 checkout: moving from 679b0a07b8e6c81af549ea7191c75963f90b7262 to 0.6.1-1
-e7bd46b423eeef5e2a2611175f2f8b29796358a7 d4f20c59ecb1bfa85f0c868886f8a96311449ab9 kaitlin <kaitlin at usagi.intel.com> 1286233593 -0700 checkout: moving from e7bd46b423eeef5e2a2611175f2f8b29796358a7 to 0.6.0-1
-d4f20c59ecb1bfa85f0c868886f8a96311449ab9 90748c32f931cfc208b5349bff11c9f8868dd60c kaitlin <kaitlin at usagi.intel.com> 1286233686 -0700 checkout: moving from d4f20c59ecb1bfa85f0c868886f8a96311449ab9 to 0.5.9-1
-90748c32f931cfc208b5349bff11c9f8868dd60c 4c90d59ca5d13bcdc1f62e4fb4d2d36fb6547a4a kaitlin <kaitlin at usagi.intel.com> 1286233995 -0700 checkout: moving from 90748c32f931cfc208b5349bff11c9f8868dd60c to 0.5.8-1
--- .git/logs/refs
+++ .git/logs/refs
-(directory)
--- .git/logs/refs/heads
+++ .git/logs/refs/heads
-(directory)
--- .git/logs/refs/heads/master
+++ .git/logs/refs/heads/master
-0000000000000000000000000000000000000000 485277ebf58de88f908f2a4103f3c5c2b79bd3de kaitlin <kaitlin at usagi.intel.com> 1286232856 -0700 clone: from git://gitorious.org/meegotouch/meegotouch-compositor.git
--- .git/objects
+++ .git/objects
-(directory)
--- .git/objects/info
+++ .git/objects/info
-(directory)
--- .git/objects/pack
+++ .git/objects/pack
-(directory)
--- .git/packed-refs
+++ .git/packed-refs
-# pack-refs with: peeled
-14848d7259baf2fd17ce80f09c5c86253bea1a75 refs/tags/0.7.2-1
-3291ca1b25e17119d15b447c301e0170a42ce085 refs/tags/0.7.1-1
-b6da94318069f4c47ff2401cbd34b43b9c134b40 refs/tags/0.7.0rc1
-679b0a07b8e6c81af549ea7191c75963f90b7262 refs/tags/0.7.0-1
-e7bd46b423eeef5e2a2611175f2f8b29796358a7 refs/tags/0.6.1-1
-d4f20c59ecb1bfa85f0c868886f8a96311449ab9 refs/tags/0.6.0-1
-90748c32f931cfc208b5349bff11c9f8868dd60c refs/tags/0.5.9-1
-4c90d59ca5d13bcdc1f62e4fb4d2d36fb6547a4a refs/tags/0.5.8-1
-9632704aa2826c67d40a8c84f9d791f07cfe8523 refs/tags/0.5.7-1
-ff59edb1ac3f71ad40858ceb8358c613a8be635b refs/tags/0.5.6-1
-a821801116d593a4d26d7975c0484430d5b9db54 refs/tags/0.5.5-1
-82bbc3fcb086af573133fd586386e40579c635bd refs/tags/0.5.4-1
-5055e17c3ab1d5db31ff9b31f974966e8aa5763b refs/tags/0.5.3-1
-a9b4d70fe35b79de08c33053cb77f5866bc9a697 refs/tags/0.5.2-1
-^0c672622dcbd266ddfbde539066190462f7cd4e5
-0d410771a07a53477494edda276b0745325a4e81 refs/tags/0.5.10-1
-fcf50456b2cd4393e2fbcfed244574f6ecfd818b refs/tags/0.5.1-1
-6a48a397238e5a876d0d21516b6fd96e488b5053 refs/tags/0.5.0rc1
-d352d354e9d2a03212a06d3063bbd84273f5cfb0 refs/tags/0.5.0-1
-a355209ccf898b312cb8144e00385f9830dba4b0 refs/tags/0.4.9-1
-f374b5a447b9e8e105087ff2df93ff062a45a142 refs/tags/0.4.8-1
-af381a9aa0c006f7fc7dd8285a7c66f6f60abe76 refs/tags/0.4.7-2
-ebab152ae135d97a59bf39e770587defd3543c59 refs/tags/0.4.6rc2
-3bbabcf124c9d8b459ca3cb7ca4145536ee00b6f refs/tags/0.4.6rc1
-c25e85ff48783a7a6c88e268bd8d85207829e323 refs/tags/0.4.6-1
-a678fd43629913d0c560e8972416994d7eccdfde refs/tags/0.4.5-1
-efc9cf8109dadff7c33e34439675593c5633b70f refs/tags/0.4.4-1
-8c9889176e50c312c93f10b9973363462140f19d refs/tags/0.4.3-1
-c9f781e93a8a2aa0f1c7c83749901b39ccb1e53d refs/tags/0.4.2-1
-b6f79410aa6479073cc95d00bbd5767417d1d353 refs/tags/0.4.10rc2
-ccc240d5f2319bfac94bd0226865b3a0f341db3d refs/tags/0.4.10rc1
-499cfd5896e17f26eb6f5aa835dd8d10a433c622 refs/tags/0.4.10-4
-ea10f32a47bd09120c7793c7109a9a2457794029 refs/tags/0.4.10-3
-d45828000f3fcad9495f8de48dbacd128725e044 refs/tags/0.4.10-2
-cdd3d26e6ac987a47e80d04d3b80225746838a40 refs/tags/0.4.10-1
-90c59d05a7899d025a1034aa1dbc07c1a1e80183 refs/tags/0.4.1-2
-a403a71e1bcb6494962a9dd21cc652d315a3f9d9 refs/tags/0.4.1-1
-d431904e0c085c0372f8a9d141d11ba500ac3d35 refs/tags/0.4.0-1
-e50192f4ec19a836b35555d9ca4b3f568e6093f8 refs/tags/0.3.9-1
-77f0b3891e4f8c261472bc73d6b1e8c5bed355aa refs/tags/0.3.8-1
-f2eda9878b9b7b56eb3fe706efeb3e490989dc9c refs/tags/0.3.7-1
-543666894aaa754950cb8644c0802871116c4bb3 refs/tags/0.3.6rc2
-b36080c6ee501e49f620054a878042e2dfb91f60 refs/tags/0.3.6rc1
-98de0ceaed24896b9069b384c9f59dbf93fd3f71 refs/tags/0.3.6-1
-98013b6bb838017364369893061869dc289af8c5 refs/tags/0.3.5-1
-77c2bb2e45bf5959a8380eca0b6bf0967a397552 refs/tags/0.3.4rc1
-77c2bb2e45bf5959a8380eca0b6bf0967a397552 refs/tags/0.3.4rc
-b04fb347bddfe081267abcb1c0dfdf3796de78c9 refs/tags/0.3.3rc1
-c2a0774e52a3107bf7d1c0bf013d74aa6dd41adb refs/tags/0.3.10-2
-0f7845c6f23f0a47f23a3aafd8382b6a4025670e refs/tags/0.3.10-1
-485277ebf58de88f908f2a4103f3c5c2b79bd3de refs/remotes/origin/master
--- .git/refs
+++ .git/refs
-(directory)
--- .git/refs/heads
+++ .git/refs/heads
-(directory)
--- .git/refs/heads/master
+++ .git/refs/heads/master
-485277ebf58de88f908f2a4103f3c5c2b79bd3de
--- .git/refs/remotes
+++ .git/refs/remotes
-(directory)
--- .git/refs/remotes/origin
+++ .git/refs/remotes/origin
-(directory)
--- .git/refs/remotes/origin/HEAD
+++ .git/refs/remotes/origin/HEAD
-ref: refs/remotes/origin/master
--- .git/refs/tags
+++ .git/refs/tags
-(directory)
--- .gitignore
+++ .gitignore
@@ -11,6 +11,7 @@
debian/duicompositor
debian/duicompositor-dbg
debian/duicompositor-tests
+debian/mcompositor-dev/
debian/mcompositor-dbg/
debian/mcompositor-functional-tests/
debian/mcompositor/
@@ -23,6 +24,8 @@
*-stamp
tests.xml
decorators/mdecorator/mdecorator
-src/mcompositor
+mcompositor/mcompositor
tests/windowctl/windowctl
tests/windowstack/windowstack
+tests/GLES2/test-gles2
+tests/focus-tracker/focus-tracker
--- debian/changelog
+++ debian/changelog
@@ -1,3 +1,151 @@
+mcompositor (0.8.0-2) unstable; urgency=low
+
+ * rebuild mcompositor-dev for all architectures
+
+ -- Adam Endrodi <ext-adam.endrodi at nokia.com> Wed, 08 Dec 2010 15:01:17 +0200
+
+mcompositor (0.8.0-1) unstable; urgency=low
+
+ * New: MCompositeWindowGroup to render several windows in one texture.
+ * Fixes: NB#206365 - Device freeze if Application Crash
+ * Remove a bunch of deprecated functions
+ * Fixes: NB#202713 - New implementation for fullscreen switching
+ * more fixes for NB#202713 + fix status menu stacking issue
+ * Limit xoverlay changes for NB#202713 to the EGL backend only
+ * Fixes: NB#202667 - Search application launches automatically
+ * fix transiency detection for the decorator
+ * add MCompositeManagerPrivate::changed_properties to signify when we should
+ check stacking/focus/etc
+ * Fixes: NB#206347 - Status indicator menu is opened on top of home, not the topmost
+ * Fixes: NB#207947 - System dialog opens in wrong orientation
+ * Fixes: NB#208395 - Lock ui is revealing another copy of it
+ * don't set_as_current_app !isMapped() windows
+ * delete bogus "compositing not enabled!" warning
+ * debugging enhancements for stacking
+ * make test13.py and test14.py pass
+ * try to make test20.py pass
+ * tighten package dependencies
+ * Fixes: NB#202389 - mcompositor is wasting time printing stuff to console
+ * exposee desktop in checkStacking()
+ * die if plugins are unloadable
+ * delete rootWindow::_NET_SUPPORTING_WM_CHECK on exit
+ * set right decoratorRect in case it's too large
+
+ -- Adam Endrodi <ext-adam.endrodi at nokia.com> Tue, 07 Dec 2010 13:54:46 +0200
+
+mcompositor (0.7.9-1) unstable; urgency=low
+
+ * Fixes: NB#205707 - System dialogs are not shown
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Wed, 17 Nov 2010 14:54:49 +0200
+
+mcompositor (0.7.8-1) unstable; urgency=low
+
+ * Fixes: NB#203276 - Show unmap animation on UnmapNotify, not on _NET_CLOSE_WINDOW
+ * Fixes: http://bugs.meego.com/show_bug.cgi?id=8957
+ * Fixes: NB#199378 - Only Input Method toolbar is visible after reopening Conversational View - Move all redirection of windows to MTexturePi
+ * Fixes: NB#199856 - Application catches tap from screen unblanking when Status Menu fails to open - allow setting NormalState when a window
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Thu, 28 Oct 2010 16:19:02 +0300
+
+mcompositor (0.7.7-1) unstable; urgency=low
+
+ * Fixes: NB#196487 - display doesn't become dimmed
+ * Various optimizations and fixes
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Thu, 28 Oct 2010 00:31:43 +0300
+
+mcompositor (0.7.6-1) unstable; urgency=low
+
+ * Fixes: NB#198346 - Not able to answer coming calls when device lock...
+ * Fixes: NB#187502 - Fn+Backspace works simultaneously as a task switcher...
+
+ -- Kimmo Hämäläinen <kimmo.hamalainen at nokia.com> Fri, 22 Oct 2010 10:31:05 +0300
+
+mcompositor (0.7.5-1) unstable; urgency=low
+
+ * Fixes: NB#189364 - Same properties are changed multiple times for all bg apps..
+ * Fixes: NB#189519 - compositor calls eglSwapBuffers without rendering anything
+ * Fixes: NB#196385 - Display is not updated correctly after screen blank on-off sequence, take two
+ * New interface: custom window animation handler
+ * New interface: stacking list
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Tue, 12 Oct 2010 14:14:43 +0300
+
+mcompositor (0.7.4-1) unstable; urgency=low
+
+ * Fixes: NB#196316 - Ugly flicker during startup-animation
+ * New: Changes: testing binaries into own package
+ * New: Restored texture from pixmap functionality software rendering
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Tue, 05 Oct 2010 16:46:41 +0300
+
+mcompositor (0.7.3-1) unstable; urgency=low
+
+ * Fixes: NB#196194 - MCOMPOSITOR titlebar misalignment causes Toast to be unusable
+ * Fixes: NB#195550 - Status Menu opens in background of application
+ * Fixes: NB#187120 - Flicker when opening the dialer page
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Thu, 30 Sep 2010 03:29:48 +0300
+
+mcompositor (0.7.2-1) unstable; urgency=low
+
+ * Fixes: NB#194203 - MCompositor slows down application start from the grid by 0,8s
+ * Fixes: NB#182860 - Pin query appears...,
+ * Fixes: NB#192454 - Devicelock UI flickers...
+ * Fixes: NB#188373 Global alpha being reset when running omapxvsink
+ * New: Improvements and texture getter for shader effects API
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Fri, 24 Sep 2010 12:58:13 +0300
+
+mcompositor (0.7.1-1) unstable; urgency=low
+
+ * Fixes: NB#193821 - leaking pixmap references
+ * Fixes: NB#188336 - setVideoGlobalAlpha does not work
+ * Fixes: NB#193948 - application state is reported incorrectly to tdriver
+ * Fixes: NB#186732 - Reaction times for switching from Home Screen to fullscreen are above target for many applications
+ * New development package
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Fri, 17 Sep 2010 12:37:43 +0300
+
+mcompositor (0.7.0-1) unstable; urgency=low
+
+ * Fixes: NB#191286 - mcompositor is eating CPU from the background in basic panning use cases
+ * Fixes: NB#189756 - COREWEB: /usr/bin/mcompositor 'MWindowPropertyCache::isDecorator MCompositeManagerPrivate::compareWindows MCompositeManagerPrivate::roughSort MCompositeManagerPrivate::bindWindow'
+ * New: Modularized compositing framework with support for extensions and custom window effects using shaders
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Wed, 15 Sep 2010 00:11:28 +0300
+
+mcompositor (0.6.1-1) unstable; urgency=low
+
+ * Fixes: NB#180786 - QWidget::show() does not fire subsequent X11 window map requests
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Tue, 14 Sep 2010 13:29:10 +0300
+
+mcompositor (0.6.0-1) unstable; urgency=low
+
+ * Fixes: NB#186402 - The application is getting minimized on clicking close button
+ * Improvements: beginAnimation() and endAnimation() API
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Thu, 09 Sep 2010 23:42:14 +0300
+
+mcompositor (0.5.10-1) unstable; urgency=low
+
+ * Fixes: NB#184226 - PIN code dialog uses initial_state==IconicState when it shows itself
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Wed, 08 Sep 2010 17:06:40 +0300
+
+mcompositor (0.5.9-1) unstable; urgency=low
+
+ * Fixes: NB#184773 - Application dialog gets stucked when left metakey + backspace buttons are pressed
+ * Fixes: NB#181749 - Window animations feel 'cheap' compared to Fremantle
+ * Fixes: NB#183538 - Blue screen displays for ~2 secs while booting the device
+ * Fixes: NB#180628 - visibility notification coming late
+ * Fixes: NB#186827 - mcompositor causes device freezing
+ * Fixes: NB#186832 - MCompositor crashes at app termination
+
+ -- Abdiel Janulgue <abj at codefuassasin.research.nokia.com> Wed, 18 Aug 2010 19:43:31 +0300
+
mcompositor (0.5.8-1) unstable; urgency=low
* Fixes: NB#185979 - Managing separate child window causing mcompositor to crash
--- debian/control
+++ debian/control
@@ -2,7 +2,7 @@
Section: x11
Priority: extra
Maintainer: Abdiel Janulgue <abdiel.janulgue at nokia.com>
-Build-Depends: debhelper (>= 5), libqt4-dev, libmeegotouch-dev, libgles2-sgx-img-dev [arm armel], opengles-sgx-img-common-dev [arm armel], libgl-dev [i386], libgl1 [i386], libqt4-opengl-dev, libxrender-dev, libxcomposite-dev, libxdamage-dev, libxtst-dev, libxi-dev, mce-dev [arm armel], libcontextsubscriber-dev, pkg-config, aegis-builder (>= 1.4), libxml2-utils, test-definition, libx11-xcb-dev, libxcb-render0-dev, libxext-dev, libxcb-shape0-dev
+Build-Depends: debhelper (>= 5), libqt4-dev, libmeegotouch-dev, libgles2-sgx-img-dev [arm armel], opengles-sgx-img-common-dev [arm armel], libgl-dev [i386], libgl1 [i386], libqt4-opengl-dev, libxrender-dev, libxcomposite-dev, libxdamage-dev, libxtst-dev, libxi-dev, mce-dev [arm armel], libcontextsubscriber-dev, pkg-config, aegis-builder (>= 1.4), libxml2-utils, test-definition, libx11-xcb-dev, libxcb-render0-dev, libxext-dev, libxcb-shape0-dev, libxrandr-dev
Standards-Version: 3.7.2
Package: mcompositor
@@ -19,9 +19,22 @@
Description: MeeGo Touch UI Compositing Window Manager debug symbols
MeeGo Touch UI Compositor debug symbols
+Package: mcompositor-dev
+Architecture: any
+Depends: mcompositor (=${Source-Version})
+Description: MeeGo Touch UI Compositing Window Manager header files
+ MeeGo Touch UI Compositor header files for plugins.
+
+Package: mcompositor-utils
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: commandline tools for testing mcompositor
+ .
+
+
Package: mcompositor-functional-tests
Architecture: any
-Depends: libmeegotouchcore0, ci-testing, meego-env-dimming, meego-env-behave, python, contextkit-utils, ${shlibs:Depends}
+Depends: libmeegotouchcore0, ci-testing, meego-env-dimming, meego-env-behave, python, contextkit-utils, aegis-dss-tools [arm armel], x11-utils, mcompositor-utils, ${shlibs:Depends}
XB-Maemo-CI-Packages: mcompositor
XB-Maemo-CI-Stage: fast
Description: mcompositor functional testcases
--- debian/mcompositor-dev.install
+++ debian/mcompositor-dev.install
+usr/include
--- debian/mcompositor-functional-tests.install
+++ debian/mcompositor-functional-tests.install
@@ -1,5 +1,3 @@
usr/share/mcompositor-functional-tests
usr/share/meegotouch/testscripts
-usr/bin/windowctl
-usr/bin/windowstack
-usr/bin/focus-tracker
+usr/bin/mcompositor-test-init.py
--- debian/mcompositor-utils.install
+++ debian/mcompositor-utils.install
+usr/bin/windowctl
+usr/bin/windowstack
+usr/bin/focus-tracker
--- debian/mcompositor.install
+++ debian/mcompositor.install
@@ -1,3 +1,4 @@
usr/bin/mcompositor
usr/bin/mdecorator
usr/lib/libdecorator.so*
+usr/lib/libmcompositor.so*
--- debian/rules
+++ debian/rules
@@ -120,7 +120,7 @@
dh_fixperms
# dh_perl
# dh_python
- dh_makeshlibs
+ dh_makeshlibs -V
dh_installdeb
dh_shlibdeps
dh_gencontrol
--- decorators/mdecorator/mdecoratorwindow.cpp
+++ decorators/mdecorator/mdecoratorwindow.cpp
@@ -73,10 +73,10 @@
virtual void activateEvent() {
}
- virtual void setAutoRotation(bool mode) {
- decorwindow->setOrientationAngleLocked(!mode);
- if (!mode)
- decorwindow->setOrientationAngle(M::Angle0);
+ virtual void setAutoRotation(bool mode)
+ {
+ Q_UNUSED(mode)
+ // we follow the orientation of the topmost app
}
virtual void setOnlyStatusbar(bool mode)
@@ -106,16 +106,11 @@
MDecoratorWindow::MDecoratorWindow(QWidget *parent)
: MWindow(parent)
{
- XSelectInput(QX11Info::display(), winId(), PropertyChangeMask);
onlyStatusbarAtom = XInternAtom(QX11Info::display(),
"_MDECORATOR_ONLY_STATUSBAR", False);
managedWindowAtom = XInternAtom(QX11Info::display(),
"_MDECORATOR_MANAGED_WINDOW", False);
- // default setting is not to rotate automatically
- setOrientationAngle(M::Angle0);
- setOrientationAngleLocked(true);
-
homeButtonPanel = new MHomeButtonPanel();
escapeButtonPanel = new MEscapeButtonPanel();
navigationBar = new MNavigationBar();
@@ -142,6 +137,7 @@
setMDecoratorWindowProperty();
setInputRegion();
+ setProperty("followsCurrentApplicationWindowOrientation", true);
}
void MDecoratorWindow::setWindowTitle(const QString& title)
@@ -250,21 +246,33 @@
{
static XRectangle prev_rect = {0, 0, 0, 0};
QRegion region;
- QRect r = statusBar->boundingRect().toRect();
- region += r;
+ QRect r_tmp(statusBar->geometry().toRect());
+ region += statusBar->mapToScene(r_tmp).boundingRect().toRect();
if (!only_statusbar) {
- QRect r2 = navigationBar->boundingRect().toRect();
- QRegion tmp(0, r.height(), r2.width(), r2.height());
- region += tmp;
- r2 = homeButtonPanel->boundingRect().toRect();
- tmp = QRegion(0, r.height(), r2.width(), r2.height());
- region += tmp;
- r2 = escapeButtonPanel->boundingRect().toRect();
- tmp = QRegion(0, r.height(), r2.width(), r2.height());
- region += tmp;
+ r_tmp = QRect(navigationBar->geometry().toRect());
+ region += navigationBar->mapToScene(r_tmp).boundingRect().toRect();
+ r_tmp = QRect(homeButtonPanel->geometry().toRect());
+ region += homeButtonPanel->mapToScene(r_tmp).boundingRect().toRect();
+ r_tmp = QRect(escapeButtonPanel->geometry().toRect());
+ region += escapeButtonPanel->mapToScene(r_tmp).boundingRect().toRect();
}
- decoratorRect = region.boundingRect();
+ const QRect fs(QApplication::desktop()->screenGeometry());
+ decoratorRect = region.boundingRect();
+ // crop it to fullscreen to work around a weird issue
+ if (decoratorRect.width() > fs.width())
+ decoratorRect.setWidth(fs.width());
+ if (decoratorRect.height() > fs.height())
+ decoratorRect.setHeight(fs.height());
+
+ if (!only_statusbar && decoratorRect.width() > fs.width() / 2
+ && decoratorRect.height() > fs.height() / 2) {
+ // decorator is so big that it is probably in more than one part
+ // (which is not yet supported)
+ setOnlyStatusbar(true);
+ r_tmp = statusBar->geometry().toRect();
+ region = decoratorRect = statusBar->mapToScene(r_tmp).boundingRect().toRect();
+ }
XRectangle rect = itemRectToScreenRect(decoratorRect);
if (memcmp(&prev_rect, &rect, sizeof(XRectangle))) {
Display *dpy = QX11Info::display();
--- mcompositor
+++ mcompositor
+(directory)
--- mcompositor.pro
+++ mcompositor.pro
@@ -3,6 +3,7 @@
SUBDIRS = \
decorators \
src \
+ mcompositor\
tests \
src.depends=decorators
--- mcompositor/main.cpp
+++ mcompositor/main.cpp
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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 <QtGui>
+#include <QGLWidget>
+#include "mcompositescene.h"
+#include "mcompositemanager.h"
+
+int main(int argc, char *argv[])
+{
+ // We don't need meego graphics system
+ setenv("QT_GRAPHICSSYSTEM", "raster", 1);
+
+#ifdef WINDOW_DEBUG
+ // React to context-commander's fake events; required by test20.py
+ // to be able to fake a phone call.
+ setenv("CONTEXT_COMMANDING", "1", 1);
+#endif
+
+ // Don't load any Qt plugins
+ QCoreApplication::setLibraryPaths(QStringList());
+ MCompositeManager app(argc, argv);
+
+ QGraphicsScene *scene = app.scene();
+ QGraphicsView view(scene);
+
+ view.setProperty("NoMStyle", true);
+ view.setUpdatesEnabled(false);
+ view.setAutoFillBackground(false);
+ view.setBackgroundBrush(Qt::NoBrush);
+ view.setForegroundBrush(Qt::NoBrush);
+ view.setFrameShadow(QFrame::Plain);
+
+ view.setWindowFlags(Qt::X11BypassWindowManagerHint);
+ view.setAttribute(Qt::WA_NoSystemBackground);
+#if QT_VERSION >= 0x040600
+ view.move(-2, -2);
+ view.setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
+ view.setOptimizationFlags(QGraphicsView::IndirectPainting);
+#endif
+ app.setSurfaceWindow(view.winId());
+
+ view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ view.setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+ view.setMinimumSize(QApplication::desktop()->width() + 2,
+ QApplication::desktop()->height() + 2);
+ view.setMaximumSize(QApplication::desktop()->width() + 2,
+ QApplication::desktop()->height() + 2);
+
+ QGLFormat fmt;
+ fmt.setSamples(0);
+ fmt.setSampleBuffers(false);
+
+ QGLWidget *w = new QGLWidget(fmt);
+ w->setAttribute(Qt::WA_PaintOutsidePaintEvent);
+#ifndef GLES2_VERSION
+ QPalette p = w->palette();
+ p.setColor(QPalette::Background, QColor(Qt::black));
+ w->setPalette(p);
+ w->update();
+#endif
+
+ w->setAutoFillBackground(false);
+ w->setMinimumSize(QApplication::desktop()->width(),
+ QApplication::desktop()->height());
+ w->setMaximumSize(QApplication::desktop()->width(),
+ QApplication::desktop()->height());
+ app.setGLWidget(w);
+ view.setViewport(w);
+ w->makeCurrent();
+ view.show();
+
+ app.prepareEvents();
+ app.redirectWindows();
+ app.loadPlugins();
+
+ return app.exec();
+}
--- mcompositor/mcompositor.pro
+++ mcompositor/mcompositor.pro
+include(../meegotouch_config.pri)
+
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += ../src
+
+LIBS += ../src/libmcompositor.so ../decorators/libdecorator/libdecorator.so
+
+target.path += $$M_INSTALL_BIN
+INSTALLS += target
+
+# Input
+SOURCES += main.cpp
+
+contains(DEFINES, WINDOW_DEBUG_ALOT) {
+ HEADERS += xserverpinger.h
+ SOURCES += xserverpinger.cpp
+}
+
+QT = core gui opengl
--- mcompositor/xserverpinger.cpp
+++ mcompositor/xserverpinger.cpp
+// Instances of XServerPinger send some simple X requests periodically
+// and warn no reply arrives until the next ping.
+#include "xserverpinger.h"
+
+#include <QCoreApplication>
+#include <QSocketNotifier>
+#include <QtDebug>
+
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcbext.h>
+
+#include <signal.h>
+#include <sys/prctl.h>
+#include <sys/signalfd.h>
+
+// Ping X in @pingInterval miliseconds.
+XServerPinger::XServerPinger(int pingInterval)
+{
+ Display *dpy;
+ sigset_t sigs;
+
+ // Open a separate connection to X.
+ dpy = XOpenDisplay(NULL);
+ xcb = XGetXCBConnection(dpy);
+ connect(new QSocketNotifier(ConnectionNumber(dpy), QSocketNotifier::Read),
+ SIGNAL(activated(int)), SLOT(xInput(int)));
+
+ // XGetInputFocus() is our ping request.
+ request = xcb_get_input_focus(xcb);
+ xcb_flush(xcb);
+
+ timer = new QTimer();
+ connect(timer, SIGNAL(timeout()), SLOT(tick()));
+ timer->start(pingInterval);
+
+ // die() if we get SIGINT or SIGTERM.
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGINT);
+ sigaddset(&sigs, SIGTERM);
+ connect(new QSocketNotifier(signalfd(-1, &sigs, 0),
+ QSocketNotifier::Read),
+ SIGNAL(activated(int)), SLOT(die(int)));
+ sigprocmask(SIG_BLOCK, &sigs, NULL);
+}
+
+void XServerPinger::xInput(int)
+{
+ void *reply;
+ xcb_generic_error_t *error;
+ xcb_generic_event_t *event;
+
+ // X has sent us something, read it.
+ if ((event = xcb_poll_for_event(xcb)) != NULL)
+ // Some event, ignore it
+ free(event);
+
+ if (request.sequence
+ && xcb_poll_for_reply(xcb, request.sequence, &reply, &error)) {
+ if (reply) {
+ free(reply);
+ request.sequence = 0;
+ } else if (error)
+ // Ignore
+ free(error);
+ }
+}
+
+void XServerPinger::tick()
+{
+ if (!request.sequence) {
+ // Last ping was successful, keep pinging.
+ request = xcb_get_input_focus(xcb);
+ xcb_flush(xcb);
+ } else
+ // Ping timed out
+ qWarning("X is on holidays");
+}
+
+void XServerPinger::die(int)
+{
+ const char *wmcheck = "_NET_SUPPORTING_WM_CHECK";
+
+ xcb_delete_property(xcb,
+ xcb_setup_roots_iterator(xcb_get_setup(xcb)).data->root,
+ xcb_intern_atom_reply(xcb,
+ xcb_intern_atom(xcb,
+ False,
+ strlen(wmcheck),
+ wmcheck),
+ NULL)->atom);
+ xcb_flush(xcb);
+ QCoreApplication::quit();
+}
+
+// Start XServerPinger in a separate process if it's not running yet.
+static void altmain() __attribute__((constructor));
+static void altmain()
+{
+ // Don't run again if the parent restarted.
+ if (getenv("XSERVERPINGER"))
+ return;
+ putenv("XSERVERPINGER=1");
+
+ // Start a new process and let our parent (the real mcompositor) go.
+ if (fork())
+ return;
+
+ // Die with the parent.
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+ int meh = 0;
+ QCoreApplication app(meh, 0);
+
+ XServerPinger pinger(2500);
+ app.exec();
+ exit(0);
+}
--- mcompositor/xserverpinger.h
+++ mcompositor/xserverpinger.h
+#ifndef XSERVERPINGER_H
+#define XSERVERPINGER_H
+
+#include <QObject>
+#include <QTimer>
+
+#include <xcb/xcb.h>
+
+class XServerPinger: public QObject
+{
+ Q_OBJECT
+
+public:
+ XServerPinger(int pingInterval);
+protected:
+ xcb_connection_t *xcb;
+ xcb_get_input_focus_cookie_t request;
+ QTimer *timer;
+
+public slots:
+ void xInput(int);
+ void tick();
+ void die(int);
+};
+
+#endif // ! XSERVERPINGER_H
--- meegotouch_config.pri
+++ meegotouch_config.pri
@@ -19,5 +19,19 @@
}
}
+contains(QT_CONFIG, opengles2) {
+ DEFINES += GLES2_VERSION
+} else {
+ # Qt wasn't built with EGL/GLES2 support but EGL is present
+ # ensure we still use the EGL back-end
+ exists($$QMAKE_INCDIR_OPENGL/EGL) {
+ DEFINES += GLES2_VERSION
+ }
+ # Otherwise use GLX backend
+ else {
+ DEFINES += DESKTOP_VERSION
+ }
+}
+
# Compositor components only
-VERSION = 0.5.5
+VERSION = 0.7.9
--- src/main.cpp
+++ src/main.cpp
-/***************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (directui at nokia.com)
-**
-** This file is part of mcompositor.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at directui at 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 <QtGui>
-#include <QGLWidget>
-#include "mcompositescene.h"
-#include "mcompositemanager.h"
-
-int main(int argc, char *argv[])
-{
- // We don't need meego graphics system
- setenv("QT_GRAPHICSSYSTEM", "raster", 1);
-
- // Don't load any Qt plugins
- QCoreApplication::setLibraryPaths(QStringList());
- MCompositeManager app(argc, argv);
-
- QGraphicsScene *scene = app.scene();
- QGraphicsView view(scene);
-
- view.setProperty("NoMStyle", true);
- view.setUpdatesEnabled(false);
- view.setAutoFillBackground(false);
- view.setBackgroundBrush(Qt::NoBrush);
- view.setForegroundBrush(Qt::NoBrush);
- view.setFrameShadow(QFrame::Plain);
-
- view.setWindowFlags(Qt::X11BypassWindowManagerHint);
- view.setAttribute(Qt::WA_NoSystemBackground);
-#if QT_VERSION >= 0x040600
- view.move(-2, -2);
- view.setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
- view.setOptimizationFlags(QGraphicsView::IndirectPainting);
-#endif
- app.setSurfaceWindow(view.winId());
-
- view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- view.setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
- view.setMinimumSize(QApplication::desktop()->width() + 2,
- QApplication::desktop()->height() + 2);
- view.setMaximumSize(QApplication::desktop()->width() + 2,
- QApplication::desktop()->height() + 2);
-
- QGLFormat fmt;
- fmt.setSamples(0);
- fmt.setSampleBuffers(false);
-
- QGLWidget *w = new QGLWidget(fmt);
- w->setAttribute(Qt::WA_PaintOutsidePaintEvent);
- QPalette p = w->palette();
- p.setColor(QPalette::Background, QColor(Qt::black));
- w->setPalette(p);
- w->update();
-
- w->setAutoFillBackground(false);
- w->setMinimumSize(QApplication::desktop()->width(),
- QApplication::desktop()->height());
- w->setMaximumSize(QApplication::desktop()->width(),
- QApplication::desktop()->height());
- app.setGLWidget(w);
- view.setViewport(w);
- w->makeCurrent();
- view.show();
-
- app.prepareEvents();
- app.redirectWindows();
-
- return app.exec();
-}
--- src/mcompatoms_p.h
+++ src/mcompatoms_p.h
@@ -20,8 +20,6 @@
#ifndef MCOMPATOMS_P_H
#define MCOMPATOMS_P_H
-#include <QRectF>
-
class MCompAtoms
{
public:
@@ -44,6 +42,7 @@
};
enum Atoms {
+ // The following atoms are added to the _NET_SUPPORTED list.
// window manager
WM_PROTOCOLS,
WM_DELETE_WINDOW,
@@ -73,6 +72,7 @@
_NET_WM_WINDOW_OPACITY,
_NET_WM_STATE,
_NET_WM_ICON_GEOMETRY,
+ _NET_WM_USER_TIME_WINDOW,
WM_STATE,
// misc
@@ -94,6 +94,11 @@
_MEEGO_STACKING_LAYER,
_MEEGOTOUCH_DECORATOR_BUTTONS,
_MEEGOTOUCH_CURRENT_APP_WINDOW,
+ _MEEGOTOUCH_ALWAYS_MAPPED,
+ _MEEGOTOUCH_DESKTOP_VIEW,
+ _MEEGOTOUCH_CANNOT_MINIMIZE,
+ _MEEGOTOUCH_MSTATUSBAR_GEOMETRY,
+ _MEEGOTOUCH_CUSTOM_REGION,
#ifdef WINDOW_DEBUG
_M_WM_INFO,
@@ -104,6 +109,16 @@
_M_WM_WINDOW_DIRECT_INVISIBLE,
#endif
+ // The rest of the atoms are not added to _NET_SUPPORTED.
+ END_OF_NET_SUPPORTED,
+
+ // RROutput properties
+ RROUTPUT_CTYPE = END_OF_NET_SUPPORTED,
+ RROUTPUT_PANEL,
+ RROUTPUT_ALPHA_MODE,
+ RROUTPUT_GRAPHICS_ALPHA,
+ RROUTPUT_VIDEO_ALPHA,
+
ATOMS_TOTAL
};
static MCompAtoms *instance();
--- src/mcompmgrextensionfactory.h
+++ src/mcompmgrextensionfactory.h
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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.
+**
+****************************************************************************/
+
+#ifndef MCOMPMGREXTENSIONFACTORY_H
+#define MCOMPMGREXTENSIONFACTORY_H
+
+#include <QtPlugin>
+
+class MCompmgrExtensionFactory
+{
+ public:
+ virtual ~MCompmgrExtensionFactory() {}
+
+ virtual MCompositeManagerExtension* create() = 0;
+ virtual QString extensionName() = 0;
+};
+
+Q_DECLARE_INTERFACE(MCompmgrExtensionFactory,
+ "com.nokia.mCompositor.MCompmgrExtensionFactory/1.0");
+
+#endif //MCOMPMGREXTENSIONFACTORY_H
--- src/mcompositemanager.cpp
+++ src/mcompositemanager.cpp
@@ -25,24 +25,33 @@
#include "msimplewindowframe.h"
#include "mdecoratorframe.h"
#include "mdevicestate.h"
+#include "mcompositemanagerextension.h"
+#include "mcompmgrextensionfactory.h"
#include <mrmiserver.h>
#include <QX11Info>
#include <QByteArray>
#include <QVector>
+#include <QtPlugin>
#include <X11/Xutil.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/shape.h>
+#include <X11/extensions/Xrandr.h>
#include <X11/Xatom.h>
#include <X11/Xmd.h>
#include <X11/XKBlib.h>
+#include <X11/Xproto.h>
#include "mcompatoms_p.h"
-#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
#define TRANSLUCENT 0xe0000000
#define OPAQUE 0xffffffff
@@ -83,6 +92,32 @@
#ifdef WINDOW_DEBUG
static QTime overhead_measure;
+// this can be toggled with SIGUSR1
+static bool debug_mode = false;
+#endif
+
+// Enable to see the decisions of the stacker.
+#if 0
+# define STACKING_DEBUGGING
+# define STACKING(fmt, args...) \
+ qDebug("line:%u: " fmt, __LINE__ ,##args)
+# define STACKING_MOVE(from, to) \
+ STACKING("moving %d (0x%lx) -> %d in stack", \
+ from, \
+ 0 <= from && from < stacking_list.size() \
+ ? stacking_list[from] : 0, \
+ to)
+#else
+# define STACKING(...) /* NOP */
+# define STACKING_MOVE(...) /* NOP */
+#endif
+
+// Enable to see what and why getTopmostApp() chooses
+// as a toplevel window.
+#if 0
+# define GTA(...) qDebug("getTopmostApp: " __VA_ARGS__)
+#else
+# define GTA(...) /* NOP */
#endif
MCompAtoms *MCompAtoms::instance()
@@ -94,7 +129,7 @@
MCompAtoms::MCompAtoms()
{
- const char *atom_names[] = {
+ static const char *atom_names[] = {
"WM_PROTOCOLS",
"WM_DELETE_WINDOW",
"WM_TAKE_FOCUS",
@@ -124,6 +159,7 @@
"_NET_WM_WINDOW_OPACITY",
"_NET_WM_STATE",
"_NET_WM_ICON_GEOMETRY",
+ "_NET_WM_USER_TIME_WINDOW",
"WM_STATE",
// misc
@@ -145,6 +181,11 @@
"_MEEGO_STACKING_LAYER",
"_MEEGOTOUCH_DECORATOR_BUTTONS",
"_MEEGOTOUCH_CURRENT_APP_WINDOW",
+ "_MEEGOTOUCH_ALWAYS_MAPPED",
+ "_MEEGOTOUCH_DESKTOP_VIEW",
+ "_MEEGOTOUCH_CANNOT_MINIMIZE",
+ "_MEEGOTOUCH_MSTATUSBAR_GEOMETRY",
+ "_MEEGOTOUCH_CUSTOM_REGION",
#ifdef WINDOW_DEBUG
// custom properties for CITA
@@ -155,6 +196,14 @@
"_M_WM_WINDOW_DIRECT_VISIBLE",
"_M_WM_WINDOW_DIRECT_INVISIBLE",
#endif
+
+ // Add atoms you don't want to be in rootWindow::_NET_SUPPORTED below.
+ // RROutput properties
+ RR_PROPERTY_CONNECTOR_TYPE,
+ "Panel",
+ "AlphaMode",
+ "GraphicsAlpha",
+ "VideoAlpha",
};
Q_ASSERT((sizeof(atom_names) / sizeof(atom_names[0])) == ATOMS_TOTAL);
@@ -166,7 +215,7 @@
XChangeProperty(dpy, QX11Info::appRootWindow(), atoms[_NET_SUPPORTED],
XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms,
- ATOMS_TOTAL);
+ END_OF_NET_SUPPORTED);
}
MCompAtoms::Type MCompAtoms::windowType(Window w)
@@ -333,49 +382,6 @@
return 0;
}
-class MapRequesterPrivate: public QObject
-{
- Q_OBJECT
-public:
- static MapRequesterPrivate* instance(QObject* parent = 0)
- {
- if (!d)
- d = new MapRequesterPrivate(parent);
- return d;
- }
-
- void requestMap(Window window)
- {
- if (!((MCompositeManager *) qApp)->isCompositing()
- // if something is already queueing, add to the queue, otherwise
- // the mapping order goes wrong
- || !map_requests.isEmpty())
- map_requests.push_back(window);
- else
- XMapWindow(QX11Info::display(), window);
- }
-
-public slots:
- void grantMapRequests()
- {
- while (!map_requests.isEmpty()) {
- // first come first served
- Window w = map_requests.takeFirst();
- XMapWindow(QX11Info::display(), w);
- }
- }
-
-private:
- QList<Window> map_requests;
- explicit MapRequesterPrivate(QObject* parent = 0)
- :QObject(parent)
- {}
-
- static MapRequesterPrivate *d;
-};
-
-MapRequesterPrivate* MapRequesterPrivate::d = 0;
-
static Window transient_for(Window window)
{
Window transient_for = 0;
@@ -489,7 +495,8 @@
QRect r = availScreenRect;
XMoveResizeWindow(dpy, window, r.x(), r.y(), r.width(), r.height());
}
- priv->dirtyStacking(false);
+ if (win && win->propertyCache()->isMapped())
+ priv->dirtyStacking(false);
} break;
case 1: /* add */ {
if (i == -1) {
@@ -513,7 +520,8 @@
MDecoratorFrame::instance()->lower();
MDecoratorFrame::instance()->setManagedWindow(0);
}
- priv->dirtyStacking(false);
+ if (win && win->propertyCache()->isMapped())
+ priv->dirtyStacking(false);
} break;
case 2: /* toggle */ {
if (i == -1)
@@ -525,48 +533,131 @@
}
}
-#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];
+/* Finds, caches and returns the primary (Panel) RROutput.
+ * Returns None if it cannot be found or it doesn't support
+ * alpha blending. */
+static RROutput find_primary_output()
+{
+ static bool been_here = false;
+ static RROutput primary = None;
+ int i;
+ Display *dpy;
+ int major, minor;
+ bool has_alpha_mode;
+ XRRScreenResources *scres;
+
+ /* Initialize only once. */
+ if (been_here != None)
+ return primary;
+ been_here = true;
- snprintf(path, 256, "/sys/devices/platform/omapdss/manager%d/alpha_blending_enabled", manager);
+ /* Check RandR, who knows what kind of X server we ride. */
+ dpy = QX11Info::display();
+ if (!XRRQueryVersion(dpy, &major, &minor)
+ || !(major > 1 || (major == 1 && minor >= 3)))
+ return None;
+
+ /* Enumerate all the outputs X knows about and find the one
+ * whose connector type is "Panel". */
+ if (!(scres = XRRGetScreenResources(dpy, DefaultRootWindow(dpy))))
+ return None;
+
+ has_alpha_mode = false;
+ for (i = 0, primary = None; i < scres->noutput && primary == None; i++) {
+ Atom t;
+ int fmt;
+ unsigned char *contype;
+ unsigned long nitems, rem;
+
+ if (XRRGetOutputProperty(dpy, scres->outputs[i], ATOM(RROUTPUT_CTYPE),
+ 0, 1, False, False, AnyPropertyType, &t,
+ &fmt, &nitems, &rem, &contype) == Success) {
+ if (t == XA_ATOM && fmt == 32 && nitems == 1
+ && *(Atom *)contype == ATOM(RROUTPUT_PANEL)) {
+ unsigned char *alpha_mode;
+
+ /* Does the primary output support alpha blending? */
+ primary = scres->outputs[i];
+ if (XRRGetOutputProperty(dpy, primary,
+ ATOM(RROUTPUT_ALPHA_MODE), 0, 1, False, False,
+ AnyPropertyType, &t, &fmt, &nitems, &rem,
+ &alpha_mode) == Success) {
+ has_alpha_mode = t == XA_INTEGER && fmt == 32
+ && nitems == 1;
+ XFree(alpha_mode);
+ }
+ }
+ XFree(contype);
+ }
+ }
+ XRRFreeScreenResources(scres);
- out = fopen(path, "w");
+ /* If the primary output doesn't support alpha blending, don't bother. */
+ if (!has_alpha_mode)
+ primary = None;
- if (out) {
- fprintf(out, "%d", state);
- fclose(out);
- }
+ return primary;
}
-static void set_global_alpha(unsigned int plane, unsigned int level)
+/* Set GraphicsAlpha and/or VideoAlpha of the primary output
+ * and enable/disable alpha blending if necessary. */
+static void set_global_alpha(int new_gralpha, int new_vidalpha)
{
- FILE *out;
- char path[256];
+ static int blending = -1, gralpha = -1, vidalpha = -1;
+ RROutput output;
+ Display *dpy;
+ int blend;
- snprintf(path, 256, "/sys/devices/platform/omapdss/overlay%d/global_alpha", plane);
+ Q_ASSERT(-1 <= new_gralpha && new_gralpha <= 255);
+ Q_ASSERT(-1 <= new_vidalpha && new_vidalpha <= 255);
+ if ((output = find_primary_output()) == None)
+ return;
+ dpy = QX11Info::display();
- out = fopen(path, "w");
+ /* Only set changed properties. */
+ if (new_gralpha < 0)
+ new_gralpha = gralpha;
+ if (new_vidalpha < 0)
+ new_vidalpha = vidalpha;
+ if (new_gralpha < 0 && new_vidalpha < 0)
+ /* There must have been an error getting the properties. */
+ return;
- if (out) {
- fprintf(out, "%d", level);
- fclose(out);
- }
+ blend = new_gralpha < 255 || new_vidalpha < 255;
+ if (blend != blending && !blend)
+ /* Disable blending first. */
+ XRRChangeOutputProperty(dpy, output, ATOM(RROUTPUT_ALPHA_MODE),
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *)&blend, 1);
+
+ if (new_gralpha >= 0 && new_gralpha != gralpha) {
+ /* Change or reset graphics alpha. */
+ XRRChangeOutputProperty(dpy, output, ATOM(RROUTPUT_GRAPHICS_ALPHA),
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *)&new_gralpha, 1);
+ gralpha = new_gralpha;
+ }
+ if (new_vidalpha >= 0 && new_vidalpha != vidalpha) {
+ /* Change or reset video alpha. */
+ XRRChangeOutputProperty(dpy, output, ATOM(RROUTPUT_VIDEO_ALPHA),
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *)&new_vidalpha, 1);
+ vidalpha = new_vidalpha;
+ }
+
+ if (blend != blending && blend)
+ /* Enable blending last. */
+ XRRChangeOutputProperty(dpy, output, ATOM(RROUTPUT_ALPHA_MODE),
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *)&blend, 1);
+ blending = blend;
}
-static void set_alpha_onplane(int plane, int value)
+/* Turn off global alpha blending on both planes. */
+static void reset_global_alpha()
{
- if (value == 255)
- toggle_global_alpha_blend(0, plane);
- else if (value < 255)
- toggle_global_alpha_blend(1, plane);
-
- set_global_alpha(plane, value);
+ set_global_alpha(255, 255);
}
-#endif
static Bool map_predicate(Display *display, XEvent *xevent, XPointer arg)
{
@@ -620,7 +711,10 @@
buttoned_win(0),
glwidget(0),
compositing(true),
- stacking_timeout_check_visibility(false)
+ changed_properties(false),
+ prepared(false),
+ stacking_timeout_check_visibility(false),
+ stacking_timeout_timestamp(CurrentTime)
{
xcb_conn = XGetXCBConnection(QX11Info::display());
MWindowPropertyCache::set_xcb_connection(xcb_conn);
@@ -635,13 +729,17 @@
this, SLOT(callOngoing(bool)));
stacking_timer.setSingleShot(true);
connect(&stacking_timer, SIGNAL(timeout()), this, SLOT(stackingTimeout()));
- connect(this, SIGNAL(compositingEnabled()), MapRequesterPrivate::instance(this),
- SLOT(grantMapRequests()));
-
+ connect(this, SIGNAL(currentAppChanged(Window)), this,
+ SLOT(setupButtonWindows(Window)));
}
MCompositeManagerPrivate::~MCompositeManagerPrivate()
{
+ if (prepared)
+ // Advertise the world we're gone.
+ XDeleteProperty(QX11Info::display(), QX11Info::appRootWindow(),
+ ATOM(_NET_SUPPORTING_WM_CHECK));
+
delete watch;
delete atom;
watch = 0;
@@ -659,20 +757,6 @@
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();
-}
-
static void setup_key_grabs()
{
Display* dpy = QX11Info::display();
@@ -681,7 +765,10 @@
switcher_key = XKeysymToKeycode(dpy, XStringToKeysym("BackSpace"));
XGrabKey(dpy, switcher_key, Mod5Mask,
RootWindow(QX11Info::display(), 0), True,
- GrabModeSync, GrabModeSync);
+ GrabModeAsync, GrabModeAsync);
+ XGrabKey(dpy, switcher_key, Mod5Mask | LockMask,
+ RootWindow(QX11Info::display(), 0), True,
+ GrabModeAsync, GrabModeAsync);
}
if (!ignored_mod) {
@@ -695,6 +782,7 @@
0, 0);
XkbFreeControls(xkb_t, 0, True);
ignored_mod = true;
+ free(xkb_t);
}
}
@@ -731,7 +819,7 @@
overlay_mapped = false; // make sure we try to map it in startup
XReparentWindow(QX11Info::display(), localwin, xoverlay, 0, 0);
localwin_parent = xoverlay;
- enableInput();
+ XMoveWindow(QX11Info::display(), localwin, -2, -2);
XDamageQueryExtension(QX11Info::display(), &damage_event, &damage_error);
@@ -740,6 +828,7 @@
RootWindow(QX11Info::display(), 0),
-1, -1, 1, 1, 0, CopyFromParent,
InputOnly, CopyFromParent, 0, 0);
+ XStoreName(QX11Info::display(), close_button_win, "MCompositor close button");
XSelectInput(QX11Info::display(), close_button_win,
ButtonReleaseMask | ButtonPressMask);
XMapWindow(QX11Info::display(), close_button_win);
@@ -747,9 +836,34 @@
RootWindow(QX11Info::display(), 0),
-1, -1, 1, 1, 0, CopyFromParent,
InputOnly, CopyFromParent, 0, 0);
+ XStoreName(QX11Info::display(), home_button_win, "MCompositor home button");
XSelectInput(QX11Info::display(), home_button_win,
ButtonReleaseMask | ButtonPressMask);
XMapWindow(QX11Info::display(), home_button_win);
+
+ prepared = true;
+}
+
+void MCompositeManagerPrivate::loadPlugins()
+{
+ // hard-coded for now. move this to plugindir later
+#define PDIR "/usr/lib/mcompositor"
+ QDir pluginsDir = QDir(PDIR);
+
+ foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
+ QObject *plugin;
+ MCompmgrExtensionFactory* factory;
+
+ QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
+ if (!(plugin = loader.instance()))
+ qFatal("couldn't load %s: %s",
+ loader.fileName().toLatin1().constData(),
+ loader.errorString().toLatin1().constData());
+ if (!(factory = qobject_cast<MCompmgrExtensionFactory *>(plugin)))
+ qFatal("%s is not a MCompmgrExtensionFactory",
+ loader.fileName().toLatin1().constData());
+ factory->create();
+ }
}
bool MCompositeManagerPrivate::needDecoration(Window window,
@@ -801,8 +915,6 @@
void MCompositeManagerPrivate::damageEvent(XDamageNotifyEvent *e)
{
- if (device_state->displayOff())
- return;
XserverRegion r = XFixesCreateRegion(QX11Info::display(), 0, 0);
int num;
XDamageSubtract(QX11Info::display(), e->damage, None, r);
@@ -811,8 +923,11 @@
XFixesDestroyRegion(QX11Info::display(), r);
MCompositeWindow *item = COMPOSITE_WINDOW(e->drawable);
- if (item && rects)
- item->updateWindowPixmap(rects, num);
+ if (item && rects) {
+ item->updateWindowPixmap(rects, num, e->timestamp);
+ if (item->waitingForDamage())
+ item->damageReceived(false);
+ }
if (rects)
XFree(rects);
@@ -831,8 +946,9 @@
MCompositeWindow *item = COMPOSITE_WINDOW(e->window);
if (item) {
- if (!item->isClosing())
- item->deleteLater();
+ item->deleteLater();
+ removeWindow(item->window());
+ // PC deleted with the MCompositeWindow
} else {
// We got a destroy event from a framed window (or a window that was
// never mapped)
@@ -841,48 +957,55 @@
framed_windows.remove(e->window);
delete fd.frame;
}
- }
-
- if (prop_caches.contains(e->window) && (!item || (item && !item->isClosing()))) {
- delete prop_caches.value(e->window);
- prop_caches.remove(e->window);
+ if (prop_caches.contains(e->window)) {
+ delete prop_caches.value(e->window);
+ prop_caches.remove(e->window);
+ }
}
}
void MCompositeManagerPrivate::propertyEvent(XPropertyEvent *e)
{
- MWindowPropertyCache *pc = 0;
- if (prop_caches.contains(e->window))
- pc = prop_caches.value(e->window);
- if (pc && pc->propertyEvent(e) && pc->isMapped()) {
- dirtyStacking(false);
+ MWindowPropertyCache *pc;
+
+ if (!prop_caches.contains(e->window))
+ return;
+ pc = prop_caches.value(e->window);
+
+ if (pc->propertyEvent(e) && pc->isMapped()) {
+ changed_properties = true; // property change can affect stacking order
+ if (pc->isDecorator())
+ // in case decorator's transiency changes, make us update the value
+ pc->transientFor();
+ dirtyStacking(false, e->time);
MCompositeWindow *cw = COMPOSITE_WINDOW(e->window);
if (cw && !cw->isNewlyMapped()) {
- checkStacking(false);
+ checkStacking(false, e->time);
// window on top could have changed
if (!possiblyUnredirectTopmostWindow())
enableCompositing(false);
}
}
-#ifdef GLES2_VERSION
+
// global alpha events here. TODO: property cache class could handle this
// but it is straightforward to manipulate it from here
- if (pc && e->atom == ATOM(_MEEGOTOUCH_GLOBAL_ALPHA))
- set_alpha_onplane(0, pc->globalAlpha());
- if (pc && e->atom == ATOM(_MEEGOTOUCH_VIDEO_ALPHA))
- set_alpha_onplane(1, pc->videoGlobalAlpha());
-#endif
+ if (e->atom == ATOM(_MEEGOTOUCH_GLOBAL_ALPHA))
+ set_global_alpha(pc->globalAlpha(), -1);
+ else if (e->atom == ATOM(_MEEGOTOUCH_VIDEO_ALPHA))
+ set_global_alpha(-1, pc->videoGlobalAlpha());
}
Window MCompositeManagerPrivate::getLastVisibleParent(MWindowPropertyCache *pc)
{
Window last = 0, parent;
+ MWindowPropertyCache *orig_pc = pc;
while (pc && (parent = pc->transientFor())) {
- MCompositeWindow *cw = COMPOSITE_WINDOW(parent);
- if (cw)
- pc = cw->propertyCache();
- else
- break; // no-good parent
+ pc = prop_caches.value(parent, 0);
+ if (pc == orig_pc) {
+ qWarning("%s(): window 0x%lx belongs to a transiency loop!",
+ __func__, orig_pc->winId());
+ break;
+ }
if (pc && pc->isMapped())
last = parent;
else // no-good parent, bail out
@@ -892,27 +1015,60 @@
}
Window MCompositeManagerPrivate::getTopmostApp(int *index_in_stacking_list,
- Window ignore_window)
+ Window ignore_window,
+ bool skip_always_mapped)
{
for (int i = stacking_list.size() - 1; i >= 0; --i) {
Window w = stacking_list.at(i);
- if (w == ignore_window || !w) continue;
- if (w == stack[DESKTOP_LAYER])
+ GTA("considering 0x%lx", w);
+ if (w == ignore_window || !w) {
+ GTA("ignoring");
+ continue;
+ }
+ if (w == stack[DESKTOP_LAYER]) {
/* desktop is above all applications */
+ GTA(" desktop layer reached");
return 0;
+ }
+
MCompositeWindow *cw = COMPOSITE_WINDOW(w);
MWindowPropertyCache *pc;
- if (cw && cw->isMapped() && (pc = cw->propertyCache()) &&
- (cw->isAppWindow(true) ||
- /* non-transient TYPE_MENU is on the same stacking layer */
- (!getLastVisibleParent(pc) &&
- pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_MENU))) &&
- pc->windowState() == NormalState && !cw->isWindowTransitioning()) {
- if (index_in_stacking_list)
- *index_in_stacking_list = i;
- return w;
+ if (!cw) {
+ GTA(" has no MCompositeWindow");
+ continue;
+ } else if (!cw->isMapped()) {
+ GTA(" not mapped");
+ continue;
+ } else if (!(pc = cw->propertyCache())) {
+ GTA(" has no property cache");
+ continue;
+ } else if (skip_always_mapped && pc->alwaysMapped()) {
+ GTA(" has _MEEGOTOUCH_ALWAYS_MAPPED");
+ continue;
+ }
+ // NOTE: this WILL pass transient application window and non-transient
+ // menu (this is intended!)
+ if ((pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_MENU)
+ && getLastVisibleParent(pc))
+ || (pc->windowTypeAtom() != ATOM(_NET_WM_WINDOW_TYPE_MENU)
+ && !cw->isAppWindow(true))) {
+ GTA(" not an application window (or non-transient menu)");
+ continue;
+ }
+ if (pc->windowState() != NormalState) {
+ GTA(" not in normal state");
+ continue;
+ } else if (cw->isWindowTransitioning()) {
+ GTA(" is transitioning");
+ continue;
}
+
+ GTA(" suitable");
+ if (index_in_stacking_list)
+ *index_in_stacking_list = i;
+ return w;
}
+ GTA("no suitable window found");
return 0;
}
@@ -936,6 +1092,17 @@
return 0;
}
+bool MCompositeManagerPrivate::haveMappedWindow() const
+{
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ Window w = stacking_list.at(i);
+ MWindowPropertyCache *pc = prop_caches.value(w, 0);
+ if (pc && pc->is_valid && pc->isMapped())
+ return true;
+ }
+ return false;
+}
+
// TODO: merge this with disableCompositing() so that in the end we have
// stacking order sensitive logic
bool MCompositeManagerPrivate::possiblyUnredirectTopmostWindow()
@@ -958,6 +1125,9 @@
win_i = i;
break;
}
+ if (cw->isClosing())
+ // this window is unmapped and has unmap animation going on
+ return false;
if (cw->isMapped() && (cw->propertyCache()->hasAlpha()
|| cw->needDecoration()
|| cw->propertyCache()->isDecorator()
@@ -965,17 +1135,35 @@
|| !fs_r.subtracted(cw->propertyCache()->shapeRegion()).isEmpty()))
// this window prevents direct rendering
return false;
- if (cw->isMapped() && cw->isAppWindow(true)) {
+ // it is a fullscreen, non-transparent window of any type
+ if (cw->isMapped()) {
top = w;
win_i = i;
break;
}
}
-
- // compositing is always assumed when a window gets mapped because of our
- // MapRequester class
-
+
+ // this code prevents us disabling compositing when we have a window
+ // that has XMapWindow() called but we have not yet received the MapNotify
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ Window w = stacking_list.at(i);
+ if (w == stack[DESKTOP_LAYER]) break;
+ MWindowPropertyCache *pc = prop_caches.value(w, 0);
+ if (pc && pc->is_valid && pc->beingMapped())
+ return false;
+ }
+ if (!haveMappedWindow()) {
+ disableCompositing(FORCED);
+ return true;
+ }
+
if (top && cw && !MCompositeWindow::hasTransitioningWindow()) {
+#ifdef GLES2_VERSION
+ if (compositing) {
+ showOverlayWindow(false);
+ compositing = false;
+ }
+#endif
// unredirect the chosen window and any docks and OR windows above it
// TODO: what else should be unredirected?
if (!((MTexturePixmapItem *)cw)->isDirectRendered()) {
@@ -994,11 +1182,12 @@
}
}
}
+#ifndef GLES2_VERSION
if (compositing) {
- scene()->views()[0]->setUpdatesEnabled(false);
- XUnmapWindow(QX11Info::display(), xoverlay);
+ showOverlayWindow(false);
compositing = false;
}
+#endif
ret = true;
}
return ret;
@@ -1006,6 +1195,9 @@
void MCompositeManagerPrivate::unmapEvent(XUnmapEvent *e)
{
+ if (e->event != QX11Info::appRootWindow())
+ // handle root's SubstructureNotifys (top-levels) only
+ return;
if (configure_reqs.contains(e->window)) {
QList<XConfigureRequestEvent*> l = configure_reqs.value(e->window);
while (!l.isEmpty()) {
@@ -1019,6 +1211,8 @@
wpc = prop_caches.value(e->window);
wpc->setBeingMapped(false);
wpc->setIsMapped(false);
+ if (!wpc->isInputOnly())
+ XRemoveFromSaveSet(QX11Info::display(), e->window);
}
// do not keep unmapped windows in windows_as_mapped list
@@ -1031,15 +1225,12 @@
MCompositeWindow *item = COMPOSITE_WINDOW(e->window);
if (item) {
+ item->closeWindowAnimation();
item->stopPing();
item->setIsMapped(false);
- setWindowState(e->window, IconicState);
+ setWindowState(e->window, WithdrawnState);
if (item->isVisible() && !item->isClosing())
item->setVisible(false);
- if (!item->isClosing())
- // mark it direct-rendered so we create damage object etc.
- // in case it is re-mapped
- ((MTexturePixmapItem *)item)->enableDirectFbRendering();
if (MDecoratorFrame::instance()->managedWindow() == e->window) {
// decorate next window in the stack if any
@@ -1047,8 +1238,7 @@
if (!cw) {
MDecoratorFrame::instance()->lower();
MDecoratorFrame::instance()->setManagedWindow(0);
- positionWindow(MDecoratorFrame::instance()->winId(),
- STACK_BOTTOM);
+ positionWindow(MDecoratorFrame::instance()->winId(), false);
} else {
if (cw->status() == MCompositeWindow::Hung) {
MDecoratorFrame::instance()->setManagedWindow(cw, true);
@@ -1072,7 +1262,6 @@
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;
@@ -1084,18 +1273,14 @@
dirtyStacking(false);
-#ifdef GLES2_VERSION
// Set the global alpha if the window beneath this window has one
- Window newtop = getTopmostApp(0, e->window);
- MCompositeWindow *c_newtop = MCompositeWindow::compositeWindow(newtop);
- if(c_newtop) {
- set_alpha_onplane(0, c_newtop->propertyCache()->globalAlpha());
- set_alpha_onplane(1, c_newtop->propertyCache()->videoGlobalAlpha());
- } else {
- set_alpha_onplane(0, 255);
- set_alpha_onplane(1, 255);
- }
-#endif
+ MCompositeWindow *newtop = MCompositeWindow::compositeWindow(
+ getTopmostApp(0, e->window));
+ if (newtop)
+ set_global_alpha(newtop->propertyCache()->globalAlpha(),
+ newtop->propertyCache()->videoGlobalAlpha());
+ else
+ reset_global_alpha();
}
void MCompositeManagerPrivate::configureEvent(XConfigureEvent *e)
@@ -1114,8 +1299,10 @@
item->propertyCache()->setRealGeometry(r);
check_visibility = true;
}
- item->setPos(e->x, e->y);
- item->resize(e->width, e->height);
+ if (item->propertyCache()->windowState() != IconicState) {
+ item->setPos(e->x, e->y);
+ item->resize(e->width, e->height);
+ }
if (e->override_redirect == True) {
if (check_visibility)
dirtyStacking(true);
@@ -1141,12 +1328,13 @@
item->update();
dirtyStacking(check_visibility);
check_visibility = false;
- }
+ } else
+ dirtyStacking(check_visibility);
} else {
// FIXME: seems that this branch is never executed?
if (e->window == MDecoratorFrame::instance()->managedWindow())
MDecoratorFrame::instance()->lower();
- item->setIconified(true);
+ // item->setIconified(true);
// ensure ZValue is set only after the animation is done
item->requestZValue(0);
@@ -1193,36 +1381,42 @@
if (e->value_mask & CWSibling) {
int above_i = stacking_list.indexOf(e->above);
if (above_i >= 0) {
- if (above_i > win_i)
+ if (above_i > win_i) {
+ STACKING_MOVE(win_i, above_i);
safe_move(stacking_list, win_i, above_i);
- else
+ } else {
+ STACKING_MOVE(win_i, above_i+1);
safe_move(stacking_list, win_i, above_i + 1);
+ }
dirtyStacking(false);
}
} else {
Window parent = transient_for(e->window);
if (parent)
- positionWindow(parent, STACK_TOP);
+ positionWindow(parent, true);
else
- positionWindow(e->window, STACK_TOP);
+ positionWindow(e->window, true);
}
} else if (win_i >= 0 && e->detail == Below
&& (e->value_mask & CWStackMode)) {
if (e->value_mask & CWSibling) {
int above_i = stacking_list.indexOf(e->above);
if (above_i >= 0) {
- if (above_i > win_i)
+ if (above_i > win_i) {
+ STACKING_MOVE(win_i, above_i-1);
safe_move(stacking_list, win_i, above_i - 1);
- else
+ } else {
+ STACKING_MOVE(win_i, above_i);
safe_move(stacking_list, win_i, above_i);
+ }
dirtyStacking(false);
}
} else {
Window parent = transient_for(e->window);
if (parent)
- positionWindow(parent, STACK_BOTTOM);
+ positionWindow(parent, false);
else
- positionWindow(e->window, STACK_BOTTOM);
+ positionWindow(e->window, false);
}
}
@@ -1320,6 +1514,8 @@
// we know the parent due to SubstructureRedirectMask on root window
pc->setParentWindow(RootWindow(dpy, 0));
}
+ if(!pc->isInputOnly())
+ XAddToSaveSet(QX11Info::display(), e->window);
MCompAtoms::Type wtype = pc->windowType();
QRect a = pc->realGeometry();
@@ -1365,7 +1561,7 @@
}
#ifdef WINDOW_DEBUG
- overhead_measure.start();
+ if (debug_mode) overhead_measure.start();
#endif
const QList<Atom> &states = pc->netWmState();
@@ -1374,17 +1570,20 @@
fullscreen_wm_state(this, 1, e->window, &v);
}
- pc->setBeingMapped(true);
+ pc->setBeingMapped(true); // don't disable compositing & allow setting state
+ const XWMHints &h = pc->getWMHints();
+ if ((h.flags & StateHint) && (h.initial_state == IconicState))
+ setWindowState(e->window, IconicState);
+ else
+ setWindowState(e->window, NormalState);
if (needDecoration(e->window, pc)) {
- XAddToSaveSet(QX11Info::display(), e->window);
-
if (MDecoratorFrame::instance()->decoratorItem()) {
- enableCompositing();
- MapRequesterPrivate::instance()->requestMap(e->window);
// initially visualize decorator item so selective compositing
// checks won't disable compositing
MDecoratorFrame::instance()->decoratorItem()->setVisible(true);
} else {
+#if 0 /* FIXME/TODO: this does NOT work when mdecorator starts after the first
+ decorated window is shown. See NB#196194 */
// it will be non-toplevel, so mask needs to be set here
XSelectInput(dpy, e->window,
StructureNotifyMask | ColormapChangeMask |
@@ -1425,53 +1624,54 @@
XReparentWindow(QX11Info::display(), e->window,
frame->windowArea(), 0, 0);
- setWindowState(e->window, NormalState);
- MapRequesterPrivate::instance()->requestMap(e->window);
+ MapRequesterPrivate::instance()->requestMap(pc);
frame->show();
XSync(QX11Info::display(), False);
+#else
+ qWarning("%s: mdecorator hasn't started yet", __func__);
+#endif
}
- } else {
- const XWMHints &h = pc->getWMHints();
- if ((h.flags & StateHint) && (h.initial_state == IconicState))
- setWindowState(e->window, IconicState);
- else
- setWindowState(e->window, NormalState);
- MapRequesterPrivate::instance()->requestMap(e->window);
}
+ // create the damage object before mapping to get 'em all
+ if (!device_state->displayOff())
+ pc->damageTracking(true);
+ XMapWindow(QX11Info::display(), e->window);
}
/* recursion is needed to handle transients that are transient for other
* transients */
-static void raise_transients(QList<Window>& winlist, Window w, int last_i)
+void MCompositeManagerPrivate::raiseTransientsOf(MWindowPropertyCache *pc,
+ int last_i, bool recursion)
{
- Window first_moved = 0;
- for (int i = 0; i < last_i;) {
- Window iw = winlist.at(i);
- if (iw == first_moved)
- /* each window is only considered once */
- break;
- MCompositeWindow *cw = MCompositeWindow::compositeWindow(iw);
- if (cw && cw->propertyCache()->transientFor() == w) {
- safe_move(winlist, i, last_i);
- if (!first_moved) first_moved = iw;
- raise_transients(winlist, iw, last_i);
- } else ++i;
+ static MWindowPropertyCache *orig_pc = 0;
+ if (!recursion)
+ orig_pc = pc;
+ for (QList<Window>::const_iterator it = pc->transientWindows().begin();
+ it != pc->transientWindows().end(); ++it) {
+ int i = stacking_list.indexOf(*it);
+ if (i != -1) {
+ STACKING_MOVE(i, last_i);
+ stacking_list.move(i, last_i);
+ MWindowPropertyCache *p = prop_caches.value(*it, 0);
+ if (p == orig_pc && orig_pc) {
+ qWarning("%s(): window 0x%lx belongs to a transiency loop!",
+ __func__, orig_pc->winId());
+ break;
+ }
+ if (p && !p->transientWindows().isEmpty())
+ raiseTransientsOf(p, last_i, true);
+ }
}
}
-#if 0 // disabled due to bugs in applications (e.g. widgetsgallery)
-static Bool
-timestamp_predicate(Display *display,
- XEvent *xevent,
- XPointer arg)
+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;
}
@@ -1479,18 +1679,14 @@
{
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)
@@ -1500,10 +1696,8 @@
/* 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 = COMPOSITE_WINDOW(iw);
- MWindowPropertyCache *pc;
- if (cw) pc = cw->propertyCache();
- if (!cw || !cw->isMapped() || !pc->wantsFocus() || pc->isDecorator()
+ MWindowPropertyCache *pc = prop_caches.value(iw, 0);
+ if (!pc || !pc->isMapped() || !pc->wantsFocus() || pc->isDecorator()
|| pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DOCK))
continue;
if (!pc->isOverrideRedirect() &&
@@ -1523,6 +1717,10 @@
return;
prev_focus = w;
+ // timestamp is needed because Qt could set the focus some cases (i.e.
+ // startup and XEmbed)
+ if (timestamp == CurrentTime)
+ timestamp = get_server_time();
#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) {
@@ -1543,15 +1741,18 @@
XSendEvent(QX11Info::display(), w, False, NoEventMask, &ev);
} else
#endif
- XSetInputFocus(QX11Info::display(), w, RevertToPointerRoot, timestamp);
+ XSetInputFocus(QX11Info::display(), w, RevertToPointerRoot, timestamp);
XChangeProperty(QX11Info::display(), RootWindow(QX11Info::display(), 0),
ATOM(_NET_ACTIVE_WINDOW),
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
}
-void MCompositeManagerPrivate::dirtyStacking(bool force_visibility_check)
+void MCompositeManagerPrivate::dirtyStacking(bool force_visibility_check,
+ Time timestamp)
{
+ if (timestamp != CurrentTime)
+ stacking_timeout_timestamp = timestamp;
if (force_visibility_check)
stacking_timeout_check_visibility = true;
if (!stacking_timer.isActive())
@@ -1580,10 +1781,25 @@
}
}
-// TODO: make this know when the property changed, to avoid X calls
-void MCompositeManagerPrivate::setupButtonWindows(MCompositeWindow *topmost)
+void MCompositeManagerPrivate::setupButtonWindows(Window curr_app)
{
+ Q_UNUSED(curr_app);
+ MCompositeWindow *topmost = 0;
+ // find out highest application window
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ MCompositeWindow *cw;
+ Window w = stacking_list.at(i);
+ if (w == stack[DESKTOP_LAYER])
+ break;
+ if (!(cw = COMPOSITE_WINDOW(w)))
+ continue;
+ if (cw->isMapped() && cw->isAppWindow(true)) {
+ topmost = cw;
+ break;
+ }
+ }
bool home_set = false, close_set = false;
+ static bool home_lowered = true, close_lowered = true;
if (topmost) {
XWindowChanges wc = {0, 0, 0, 0, 0, topmost->window(), Above};
int mask = CWX | CWY | CWWidth | CWHeight | CWSibling | CWStackMode;
@@ -1594,6 +1810,7 @@
XConfigureWindow(QX11Info::display(), home_button_win, mask, &wc);
home_button_geom = h;
home_set = true;
+ home_lowered = false;
}
const QRect &c = topmost->propertyCache()->closeButtonGeometry();
if (c.width() > 1 && c.height() > 1) {
@@ -1602,42 +1819,86 @@
XConfigureWindow(QX11Info::display(), close_button_win, mask, &wc);
close_button_geom = c;
close_set = true;
+ close_lowered = false;
}
}
if ((home_set || close_set) && topmost) {
buttoned_win = topmost->window();
- if (!home_set)
+ if (!home_set && !home_lowered) {
XLowerWindow(QX11Info::display(), home_button_win);
- if (!close_set)
+ home_lowered = true;
+ }
+ if (!close_set && !close_lowered) {
XLowerWindow(QX11Info::display(), close_button_win);
+ close_lowered = true;
+ }
} else if (buttoned_win) {
buttoned_win = 0;
- XLowerWindow(QX11Info::display(), close_button_win);
- XLowerWindow(QX11Info::display(), home_button_win);
+ if (!close_lowered) {
+ XLowerWindow(QX11Info::display(), close_button_win);
+ close_lowered = true;
+ }
+ if (!home_lowered) {
+ XLowerWindow(QX11Info::display(), home_button_win);
+ home_lowered = true;
+ }
}
}
-void MCompositeManagerPrivate::setCurrentApp(Window w)
+void MCompositeManagerPrivate::setCurrentApp(Window w,
+ bool stacking_order_changed)
{
static Window prev = (Window)-1;
- if (prev == w)
+ if (prev == w) {
+ if (stacking_order_changed)
+ // signal listener could be interested in transients of
+ // the current application (could be also different signal?)
+ emit currentAppChanged(current_app);
return;
+ }
XChangeProperty(QX11Info::display(), RootWindow(QX11Info::display(), 0),
ATOM(_MEEGOTOUCH_CURRENT_APP_WINDOW),
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
+ current_app = w;
+ emit currentAppChanged(current_app);
prev = w;
}
+// XSetErrorHandler() function, used exclusively to catch errors
+// with XRestackWindows().
+static bool xrestackwindows_error;
+static int xrestackwindows_error_handler(Display *dpy, XErrorEvent *err)
+{
+ Q_UNUSED(dpy);
+
+ // XRestackWindows() is actually a series of XConfigureWindow()s.
+ if (err->request_code == X_ConfigureWindow)
+ xrestackwindows_error = true;
+ return 0;
+}
+
#define RAISE_MATCHING(X) { \
first_moved = 0; \
for (int i = 0; i < last_i;) { \
Window w = stacking_list.at(i); \
if (w == first_moved) break; \
MCompositeWindow *cw = COMPOSITE_WINDOW(w); \
- if (cw && (X)) { \
- safe_move(stacking_list, i, last_i); \
- raise_transients(stacking_list, w, last_i); \
+ if (cw && cw->propertyCache() && cw->isMapped() && (X)) { \
+ MCompositeWindow *orig_cw = cw; \
+ /* find the next window to move */ \
+ Window next = 0; \
+ for (int next_i = i + 1; next_i <= last_i; ++next_i) { \
+ next = stacking_list.at(next_i); \
+ cw = COMPOSITE_WINDOW(next); \
+ if (cw && cw->propertyCache() && cw->isMapped() && (X)) \
+ break; \
+ } \
+ STACKING_MOVE(i, last_i); \
+ stacking_list.move(i, last_i); \
+ raiseTransientsOf(orig_cw->propertyCache(), last_i); \
if (!first_moved) first_moved = w; \
+ if (!next || (i = stacking_list.indexOf(next)) < 0) \
+ break; \
} else ++i; \
} }
@@ -1654,9 +1915,9 @@
stacking_timeout_check_visibility = false;
}
stacking_timer.stop();
+ stacking_timeout_timestamp = CurrentTime;
}
- Window active_app = 0, duihome = stack[DESKTOP_LAYER], first_moved,
- set_as_current_app = 0;
+ Window active_app = 0, duihome = stack[DESKTOP_LAYER], first_moved;
int last_i = stacking_list.size() - 1;
bool desktop_up = false, fs_app = false;
int app_i = -1;
@@ -1666,11 +1927,8 @@
active_app = getTopmostApp(&app_i);
if (!active_app || app_i < 0) {
desktop_up = true;
- if (duihome)
- set_as_current_app = duihome;
} else {
aw = COMPOSITE_WINDOW(active_app);
- set_as_current_app = active_app;
if (aw) {
// getTopmostApp() can return a transient now
Window parent = getLastVisibleParent(aw->propertyCache());
@@ -1684,6 +1942,9 @@
/* raise active app with its transients, or duihome if
* there is no active application */
+ STACKING("checkStacking: desktop_up: %d, active_app: 0x%lx, app_i: %d",
+ desktop_up, active_app, app_i);
+ setExposeDesktop(desktop_up);
if (!desktop_up && active_app && app_i >= 0 && aw) {
/* raise application windows belonging to the same group */
XID group;
@@ -1693,6 +1954,7 @@
if (cw->propertyCache()->windowState() == NormalState
&& cw->isAppWindow()
&& cw->propertyCache()->windowGroup() == group) {
+ STACKING_MOVE(i, last_i);
safe_move(stacking_list, i, last_i);
/* active_app was moved, update the index */
app_i = stacking_list.indexOf(active_app);
@@ -1701,11 +1963,14 @@
}
}
+ STACKING_MOVE(app_i, last_i);
safe_move(stacking_list, app_i, last_i);
/* raise transients recursively */
- raise_transients(stacking_list, active_app, last_i);
+ if (aw->propertyCache())
+ raiseTransientsOf(aw->propertyCache(), last_i);
} else if (duihome) {
//qDebug() << "raising home window" << duihome;
+ STACKING_MOVE(stacking_list.indexOf(duihome), last_i);
safe_move(stacking_list, stacking_list.indexOf(duihome), last_i);
}
@@ -1716,18 +1981,14 @@
cw->propertyCache()->windowTypeAtom()
== ATOM(_NET_WM_WINDOW_TYPE_DOCK))
else if (active_app && aw && deco->decoratorItem() &&
- deco->managedWindow() == active_app &&
- (fs_app || aw->status() == MCompositeWindow::Hung)) {
+ deco->managedWindow() == active_app) {
// no dock => decorator starts from (0,0)
XMoveWindow(QX11Info::display(), deco->decoratorItem()->window(), 0, 0);
}
/* Meego layers 1-3: lock screen, ongoing call etc. */
- /* FIXME: we should check windowState() instead of iconifyState(), which
- is for animation purposes but that could lead to regressions with
- initial_state==IconicState windows */
for (unsigned int level = 1; level < 4; ++level)
RAISE_MATCHING(!getLastVisibleParent(cw->propertyCache()) &&
- cw->iconifyState() == MCompositeWindow::NoIconifyState
+ cw->propertyCache()->windowState() == NormalState
&& cw->propertyCache()->meegoStackingLayer() == level)
/* raise all system-modal dialogs */
RAISE_MATCHING(!getLastVisibleParent(cw->propertyCache())
@@ -1764,11 +2025,11 @@
for (int i = stacking_list.size() - 1; i >= 0; --i) {
MCompositeWindow *cw;
Window w = stacking_list.at(i);
- if (w == stack[DESKTOP_LAYER])
+ if (w == duihome)
break;
if (!(cw = COMPOSITE_WINDOW(w)))
continue;
- if (cw->isMapped() && cw->isAppWindow(true)) {
+ if (cw->propertyCache() && cw->isMapped() && cw->isAppWindow(true)) {
topmost = cw;
top_i = i;
break;
@@ -1783,10 +2044,13 @@
Window deco_w = deco->decoratorItem()->window();
int deco_i = stacking_list.indexOf(deco_w);
if (deco_i >= 0) {
- if (deco_i < top_i)
+ if (deco_i < top_i) {
+ STACKING_MOVE(deco_i, top_i);
safe_move(stacking_list, deco_i, top_i);
- else
+ } else {
+ STACKING_MOVE(deco_i, top_i+1);
safe_move(stacking_list, deco_i, top_i + 1);
+ }
if (!compositing)
// decor requires compositing
enableCompositing(true);
@@ -1808,23 +2072,48 @@
only_mapped.append(stacking_list.at(i));
}
static QList<Window> prev_only_mapped;
- bool order_changed = prev_only_mapped != only_mapped;
- if (order_changed) {
- /* fix Z-values */
- for (int i = 0; i <= last_i; ++i) {
- MCompositeWindow *witem = COMPOSITE_WINDOW(stacking_list.at(i));
- if (witem && witem->hasTransitioningWindow())
- // don't change Z values until animation is over
- break;
- if (witem)
- witem->requestZValue(i);
- }
+ // fix Z-values always to make sure we do it after an animation
+ for (int i = 0; i <= last_i; ++i) {
+ MCompositeWindow *witem = COMPOSITE_WINDOW(stacking_list.at(i));
+ if (witem && witem->hasTransitioningWindow())
+ // don't change Z values until animation is over
+ break;
+ if (witem)
+ witem->requestZValue(i);
+ }
+ bool order_changed = prev_only_mapped != only_mapped;
+ if (xrestackwindows_error || order_changed) {
QList<Window> reverse;
for (int i = last_i; i >= 0; --i)
reverse.append(stacking_list.at(i));
+
+#ifdef STACKING_DEBUGGING
+ // Log the actual arguments of XRestackWindows().
+ QList<Window>::const_iterator winit;
+ QString line;
+ for (winit = reverse.constBegin(); winit != reverse.constEnd();
+ ++winit) {
+ if (winit != reverse.constBegin())
+ line += ", ";
+ line += QString().sprintf("0x%lx", *winit);
+ }
+ STACKING("XRestackWindows([%s])", line.toLatin1().constData());
+#endif
+
+ // Watch out for errors, there may be BadWin:s in @reverse.
+ XSync(QX11Info::display(), False);
+ int (*xerr)(Display *dpy, XErrorEvent *);
+ xrestackwindows_error = false;
+ xerr = XSetErrorHandler(xrestackwindows_error_handler);
XRestackWindows(QX11Info::display(), reverse.toVector().data(),
reverse.size());
+ XSync(QX11Info::display(), False);
+ XSetErrorHandler(xerr);
+ if (xrestackwindows_error) {
+ STACKING("XRestackWindows() failed, retry later");
+ dirtyStacking(false);
+ }
// decorator and OR windows are not included to the property
QList<Window> no_decors = only_mapped;
@@ -1842,13 +2131,12 @@
(unsigned char *)no_decors.toVector().data(),
no_decors.size());
prev_only_mapped = QList<Window>(only_mapped);
-
- checkInputFocus(timestamp);
+ }
+ if (order_changed || changed_properties) {
if (!device_state->displayOff())
pingTopmost();
+ checkInputFocus(timestamp);
}
- // possibly set up InputOnly windows for close and home buttons
- setupButtonWindows(topmost);
if (order_changed || force_visibility_check) {
static int xres = ScreenOfDisplay(QX11Info::display(),
DefaultScreen(QX11Info::display()))->width;
@@ -1863,7 +2151,7 @@
break;
}
MCompositeWindow *cw = COMPOSITE_WINDOW(w);
- MWindowPropertyCache *pc;
+ MWindowPropertyCache *pc = 0;
if (cw && cw->isMapped())
pc = cw->propertyCache();
if (cw && cw->isMapped() && !pc->hasAlpha() &&
@@ -1906,13 +2194,34 @@
setWindowState(cw->window(), NormalState);
}
}
- setCurrentApp(set_as_current_app);
+ // current app has different semantics from getTopmostApp and pure isAppWindow
+ Window set_as_current_app = duihome;
+ for (int i = stacking_list.size() - 1; i >= 0; --i) {
+ Window w = stacking_list.at(i);
+ if (!w) continue;
+ MCompositeWindow *cw = COMPOSITE_WINDOW(w);
+ if (!cw || !cw->propertyCache() || !cw->propertyCache()->is_valid)
+ continue;
+ if (cw->propertyCache()->winId() == duihome)
+ break;
+ Atom type = cw->propertyCache()->windowTypeAtom();
+ if (type != ATOM(_NET_WM_WINDOW_TYPE_DIALOG) &&
+ type != ATOM(_NET_WM_WINDOW_TYPE_MENU) &&
+ cw->isMapped() && cw->isAppWindow(true)) {
+ set_as_current_app = w;
+ break;
+ }
+ }
+ setCurrentApp(set_as_current_app, order_changed || changed_properties);
+ changed_properties = false;
}
void MCompositeManagerPrivate::stackingTimeout()
{
- checkStacking(stacking_timeout_check_visibility);
+ checkStacking(stacking_timeout_check_visibility,
+ stacking_timeout_timestamp);
stacking_timeout_check_visibility = false;
+ stacking_timeout_timestamp = CurrentTime;
if (!device_state->displayOff() && !possiblyUnredirectTopmostWindow())
enableCompositing(true);
}
@@ -1922,8 +2231,7 @@
Window win = e->window;
if (win == xoverlay) {
- overlay_mapped = true;
- enableRedirection();
+ showOverlayWindow(true);
return;
}
if (win == localwin || win == localwin_parent || win == close_button_win
@@ -1942,6 +2250,7 @@
prop_caches[win] = wpc;
}
wpc->setBeingMapped(false);
+ wpc->setIsMapped(true);
FrameData fd = framed_windows.value(win);
if (fd.frame) {
@@ -1981,50 +2290,54 @@
}
}
-#ifdef GLES2_VERSION
- int g_alpha = wpc->globalAlpha();
- int v_alpha = wpc->videoGlobalAlpha();
- set_alpha_onplane(0, g_alpha);
- set_alpha_onplane(1, v_alpha);
-#endif
+ set_global_alpha(wpc->globalAlpha(), wpc->videoGlobalAlpha());
MWindowPropertyCache *pc = 0;
MCompositeWindow *item = COMPOSITE_WINDOW(win);
if (item) {
item->setIsMapped(true);
- if (windows_as_mapped.indexOf(win) == -1)
- windows_as_mapped.append(win);
pc = item->propertyCache();
if (!pc) return;
+ if (!pc->isDecorator() && !pc->isOverrideRedirect()
+ && windows_as_mapped.indexOf(win) == -1)
+ windows_as_mapped.append(win);
}
- // Compositing is assumed to be enabled at this point if a window
+ // Compositing is always assumed to be enabled at this point if a window
// has alpha channels
- if (!compositing && (pc && pc->hasAlpha())) {
- qWarning("mapEvent(): compositing not enabled!");
- return;
- }
+ if (!compositing && (pc && pc->hasAlpha()))
+ enableCompositing(true);
+
if (item && pc) {
if (wtype == MCompAtoms::NORMAL)
pc->setWindowTypeAtom(ATOM(_NET_WM_WINDOW_TYPE_NORMAL));
else
pc->setWindowTypeAtom(atom->getType(win));
#ifdef WINDOW_DEBUG
- qDebug() << "Composition overhead (existing pixmap):"
- << overhead_measure.elapsed();
+ if (debug_mode)
+ qDebug() << "Composition overhead (existing pixmap):"
+ << overhead_measure.elapsed();
#endif
if (((MTexturePixmapItem *)item)->isDirectRendered()) {
((MTexturePixmapItem *)item)->enableRedirectedRendering();
setWindowDebugProperties(item->window());
} else
- item->saveBackingStore(true);
+ item->saveBackingStore();
// TODO: don't show the animation if the window is not stacked on top
const XWMHints &h = pc->getWMHints();
if ((!(h.flags & StateHint) || h.initial_state != IconicState)
- && !pc->isInputOnly())
- item->showWindow();
- else {
- item->setVisible(true);
+ && !pc->alwaysMapped() && e->send_event == False
+ && !pc->isInputOnly()) {
+ // remapped/prestarted apps should also have startup animation
+ // FIXME: assumes this window is on top
+ item->requestZValue(scene()->items().count() + 1);
+ item->setNewlyMapped(true);
+ if (!item->showWindow()) {
+ item->setNewlyMapped(false);
+ item->setVisible(true);
+ }
+ } else {
item->setNewlyMapped(false);
+ item->setVisible(true);
}
goto stack_and_return;
}
@@ -2037,17 +2350,21 @@
return;
pc = item->propertyCache();
#ifdef WINDOW_DEBUG
- if (pc->hasAlpha())
+ if (debug_mode && pc->hasAlpha())
qDebug() << "Composition overhead (new pixmap):"
<< overhead_measure.elapsed();
#endif
const XWMHints &h = pc->getWMHints();
if ((!(h.flags & StateHint) || h.initial_state != IconicState)
- && !pc->isInputOnly() && item->isAppWindow())
- item->showWindow();
- else {
- item->setVisible(true);
+ && !pc->alwaysMapped() && e->send_event == False
+ && !pc->isInputOnly() && item->isAppWindow()) {
+ if (!item->showWindow()) {
+ item->setNewlyMapped(false);
+ item->setVisible(true);
+ }
+ } else {
item->setNewlyMapped(false);
+ item->setVisible(true);
}
// the current decorated window got mapped
@@ -2087,11 +2404,11 @@
/* do this after bindWindow() so that the window is in stacking_list */
if (pc->windowState() == NormalState &&
- (stack[DESKTOP_LAYER] != win || !getTopmostApp(0, win)))
+ (stack[DESKTOP_LAYER] != win || !getTopmostApp(0, win, true)))
activateWindow(win, CurrentTime, false);
else
// desktop is stacked below the active application
- positionWindow(win, STACK_BOTTOM);
+ positionWindow(win, false);
dirtyStacking(false);
}
@@ -2100,6 +2417,7 @@
{
MWindowPropertyCache *pc = cw->propertyCache();
if (pc->supportedProtocols().indexOf(ATOM(_NET_WM_PING)) != -1
+ && pc->windowTypeAtom() != ATOM(_NET_WM_WINDOW_TYPE_NOTIFICATION)
&& pc->windowTypeAtom() != ATOM(_NET_WM_WINDOW_TYPE_DOCK)
&& pc->windowTypeAtom() != ATOM(_NET_WM_WINDOW_TYPE_MENU)
&& !pc->isDecorator() && !pc->isOverrideRedirect()
@@ -2119,27 +2437,25 @@
if (event->window != stack[DESKTOP_LAYER])
setExposeDesktop(false);
- Window raise = event->window;
- MCompositeWindow *d_item = COMPOSITE_WINDOW(stack[DESKTOP_LAYER]);
- bool needComp = false;
- if (d_item && d_item->isDirectRendered()
- && raise != stack[DESKTOP_LAYER]) {
- needComp = true;
- enableCompositing(true);
- }
- if (i && i->propertyCache()->windowState() == IconicState) {
- i->setZValue(windows.size() + 1);
- QRectF iconGeometry = i->propertyCache()->iconGeometry();
- i->restore(iconGeometry, needComp);
-#ifdef GLES2_VERSION
- int g_alpha = i->propertyCache()->globalAlpha();
- if (g_alpha < 255)
- set_alpha_onplane(0, g_alpha);
- int v_alpha = i->propertyCache()->videoGlobalAlpha();
- if (v_alpha < 255)
- set_alpha_onplane(1, v_alpha);
-#endif
+ if (!getTopmostApp()) {
+ // Not necessary to animate if not in desktop view.
+ Window raise = event->window;
+ MCompositeWindow *d_item = COMPOSITE_WINDOW(stack[DESKTOP_LAYER]);
+ bool needComp = false;
+ if (d_item && d_item->isDirectRendered()
+ && raise != stack[DESKTOP_LAYER]) {
+ needComp = true;
+ enableCompositing(true);
+ }
+ if (i && i->propertyCache()->windowState() == IconicState) {
+ i->setZValue(windows.size() + 1);
+ QRectF iconGeometry = i->propertyCache()->iconGeometry();
+ i->restore(iconGeometry, needComp);
+ set_global_alpha(i->propertyCache()->globalAlpha(),
+ i->propertyCache()->videoGlobalAlpha());
+ }
}
+
if (fd.frame)
setWindowState(fd.frame->managedWindow(), NormalState);
else
@@ -2147,26 +2463,14 @@
if (event->window == stack[DESKTOP_LAYER]) {
// Mark normal applications on top of home Iconic to make our
// qsort() function to work
- for (int wi = stacking_list.size() - 1; wi >= 0; --wi) {
- Window w = stacking_list.at(wi);
- if (w == stack[DESKTOP_LAYER])
- break;
- MCompositeWindow *cw = COMPOSITE_WINDOW(w);
- if (cw && cw->isMapped() &&
- !cw->propertyCache()->meegoStackingLayer()
- && cw->isAppWindow(true))
- setWindowState(cw->window(), IconicState);
- }
+ iconifyApps();
activateWindow(event->window, CurrentTime, true);
} else
// use composition due to the transition effect
activateWindow(event->window, CurrentTime, false);
} else if (i && event->message_type == ATOM(_NET_CLOSE_WINDOW)) {
-
- i->closeWindow();
- // update stacking lit to remove window from switcher
- checkStacking(false);
-
+ // save pixmap and delete or kill this window
+ i->closeWindowRequest();
} else if (event->message_type == ATOM(WM_PROTOCOLS)) {
if (event->data.l[0] == (long) ATOM(_NET_WM_PING)) {
MCompositeWindow *ping_source = COMPOSITE_WINDOW(event->data.l[2]);
@@ -2215,6 +2519,13 @@
d_item->setZValue(i->zValue() - 1);
Window lower, topmost = getTopmostApp();
+ if (i->window() != topmost) {
+ /* Request from a background app. Don't do anything,
+ * just make sure the states are not screwed. */
+ i->stopPing();
+ setWindowState(i->window(), IconicState);
+ return;
+ }
if (topmost)
lower = topmost;
else
@@ -2224,7 +2535,6 @@
bool needComp = false;
if (i->isDirectRendered() || d_item->isDirectRendered()) {
d_item->setVisible(true);
- enableCompositing(true);
needComp = true;
}
@@ -2244,13 +2554,19 @@
if (w == stack[DESKTOP_LAYER])
break;
MCompositeWindow *cw = COMPOSITE_WINDOW(w);
- if (cw && cw->isMapped() && cw->isAppWindow(true) &&
+ if (cw && cw->isMapped() && (cw->isAppWindow(true)
+ // mark transient dialogs Iconic too, so that
+ // restoreHandler() is called when they are maximised
+ || (cw->propertyCache()->windowTypeAtom()
+ == ATOM(_NET_WM_WINDOW_TYPE_DIALOG)
+ && getLastVisibleParent(cw->propertyCache()))) &&
// skip devicelock and screenlock windows
- (cw->propertyCache()->meegoStackingLayer() > 2 ||
- cw->propertyCache()->meegoStackingLayer() == 0))
+ !cw->propertyCache()->dontIconify())
setWindowState(cw->window(), IconicState);
}
Q_ASSERT(lower_i > 0);
+ STACKING_MOVE(stacking_list.indexOf(stack[DESKTOP_LAYER]),
+ lower_i-1);
safe_move(stacking_list, stacking_list.indexOf(stack[DESKTOP_LAYER]),
lower_i - 1);
@@ -2258,6 +2574,8 @@
// that have selective compositing. This is triggered
// when windows are rendered off-screen
i->iconify(i->propertyCache()->iconGeometry(), needComp);
+ if (needComp)
+ enableCompositing(true);
if (i->needDecoration())
i->startTransition();
i->stopPing();
@@ -2294,16 +2612,19 @@
if ((!delete_sent || window->status() == MCompositeWindow::Hung)) {
kill_window(window->window());
- MDecoratorFrame::instance()->lower();
+ if (MDecoratorFrame::instance()->managedWindow() == window->window())
+ MDecoratorFrame::instance()->lower();
}
- window->deleteLater();
+ /* DO NOT deleteLater() this window yet because
+ a) it can remove a mapped window from stacking_list
+ b) delete can be ignored (e.g. "Do you want to exit?" dialog)
+ c) _NET_WM_PID could be wrong (i.e. the window does not go away)
+ d) we get UnmapNotify/DestroyNotify anyway when it _really_ closes */
}
+// window iconified or unmapping animation ended
void MCompositeManagerPrivate::lowerHandler(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
@@ -2314,20 +2635,16 @@
if (i)
i->iconify();
}
- // set for roughSort() before raising duihome
- setWindowState(window->window(), IconicState);
-
- if (stack[DESKTOP_LAYER]) {
- // redirect windows for the switcher
- enableCompositing();
- positionWindow(stack[DESKTOP_LAYER], STACK_TOP);
- dirtyStacking(false);
+ if (window->isMapped()) {
+ // set for roughSort()
+ setWindowState(window->window(), IconicState);
+ roughSort();
}
-#ifdef GLES2_VERSION
+ // checkStacking() will redirect windows for the switcher
+ dirtyStacking(false);
+
// Reset the global alpha on minimize
- set_alpha_onplane(0, 255);
- set_alpha_onplane(1, 255);
-#endif
+ reset_global_alpha();
}
void MCompositeManagerPrivate::restoreHandler(MCompositeWindow *window)
@@ -2339,11 +2656,16 @@
else
to_stack = window;
setWindowState(to_stack->window(), NormalState);
-
- if (window->isNewlyMapped())
- window->setNewlyMapped(false);
- positionWindow(to_stack->window(), STACK_TOP);
+ // FIXME: call these for the whole transiency chain
+ window->setNewlyMapped(false);
+ if (to_stack != window)
+ to_stack->setNewlyMapped(false);
+ window->setUntransformed();
+ if (window != to_stack)
+ to_stack->setUntransformed();
+
+ positionWindow(to_stack->window(), true);
/* the animation is finished, compositing needs to be reconsidered */
dirtyStacking(false);
@@ -2403,7 +2725,7 @@
MCompositeWindow *to_stack = cw;
if (last) to_stack = COMPOSITE_WINDOW(last);
// move it to the correct position in the stack
- positionWindow(to_stack->window(), STACK_TOP);
+ positionWindow(to_stack->window(), true);
// possibly set decorator
if (cw == getHighestDecorated() || cw->status() == MCompositeWindow::Hung) {
if (FULLSCREEN_WINDOW(cw)) {
@@ -2424,10 +2746,10 @@
}
} else if (pc->isDecorator()) {
// if decorator crashes and reappears, stack it to bottom, raise later
- positionWindow(w, STACK_BOTTOM);
+ positionWindow(w, false);
} else if (w == stack[DESKTOP_LAYER]) {
setExposeDesktop(true);
- positionWindow(w, STACK_TOP);
+ positionWindow(w, true);
} else
checkInputFocus(timestamp);
@@ -2440,20 +2762,31 @@
{
if (display_off) {
// keep compositing to have synthetic events to obscure all windows
- enableCompositing(true);
+ if (!haveMappedWindow())
+ enableCompositing(true);
scene()->views()[0]->setUpdatesEnabled(false);
/* stop pinging to save some battery */
for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
it != windows.end(); ++it) {
- MCompositeWindow *i = it.value();
+ MCompositeWindow *i = it.value();
i->stopPing();
+ // stop damage tracking while the display is off
+ if (i->propertyCache())
+ i->propertyCache()->damageTracking(false);
}
} else {
- scene()->views()[0]->setUpdatesEnabled(true);
if (!possiblyUnredirectTopmostWindow())
enableCompositing(false);
/* start pinging again */
pingTopmost();
+ // restart damage tracking
+ for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
+ it != windows.end(); ++it) {
+ MCompositeWindow *i = it.value();
+ if (i->propertyCache() && (i->propertyCache()->isMapped() ||
+ i->propertyCache()->beingMapped()))
+ i->propertyCache()->damageTracking(true);
+ }
}
dirtyStacking(true); // VisibilityNotify generation
}
@@ -2484,12 +2817,16 @@
void MCompositeManagerPrivate::setWindowState(Window w, int state)
{
- MCompositeWindow* i = COMPOSITE_WINDOW(w);
- if(i && i->propertyCache()->windowState() == state)
+ MWindowPropertyCache *pc = prop_caches.value(w, 0);
+ if (pc && pc->windowState() == state)
+ return;
+ else if (pc && (!pc->isMapped() && !pc->beingMapped())
+ && (state == NormalState || state == IconicState)) {
+ // qWarning("%s: window 0x%lx is in wrong state", __func__, w);
return;
- else if (i)
+ } else if (pc)
// cannot wait for the property change notification
- i->propertyCache()->setWindowState(state);
+ pc->setWindowState(state);
CARD32 d[2];
d[0] = state;
@@ -2498,9 +2835,15 @@
32, PropModeReplace, (unsigned char *)d, 2);
}
+void MCompositeManager::setWindowState(Window w, int state)
+{
+ d->setWindowState(w, state);
+}
+
void MCompositeManagerPrivate::setWindowDebugProperties(Window w)
{
#ifdef WINDOW_DEBUG
+ if (!debug_mode) return;
MCompositeWindow *i = COMPOSITE_WINDOW(w);
if (!i)
return;
@@ -2526,6 +2869,7 @@
bool MCompositeManagerPrivate::x11EventFilter(XEvent *event)
{
+ // Core non-subclassable events
static const int damage_ev = damage_event + XDamageNotify;
static int shape_event_base = 0;
if (!shape_event_base) {
@@ -2549,8 +2893,22 @@
}
return true;
}
+
+ if (processX11EventFilters(event, false))
+ return true;
+
+ bool ret = true;
switch (event->type) {
-
+ case FocusIn: {
+ XFocusChangeEvent *e = (XFocusChangeEvent*)event;
+ // make sure we focus right window on reverting the focus to root
+ if (e->window == RootWindow(QX11Info::display(), 0)
+ && e->mode == NotifyNormal) {
+ prev_focus = e->window;
+ checkInputFocus(CurrentTime);
+ }
+ break;
+ }
case DestroyNotify:
destroyEvent(&event->xdestroywindow); break;
case PropertyNotify:
@@ -2568,10 +2926,11 @@
case ClientMessage:
clientMessageEvent(&event->xclient); break;
case ButtonRelease:
- case ButtonPress:
+ case ButtonPress:
buttonEvent(&event->xbutton);
// Qt needs to handle this event for the window frame buttons
- return false;
+ ret = false;
+ break;
case KeyPress:
case KeyRelease:
XAllowEvents(QX11Info::display(), ReplayKeyboard, event->xkey.time);
@@ -2588,9 +2947,28 @@
}
break;
default:
- return false;
+ ret = false;
+ break;
}
- return true;
+ processX11EventFilters(event, true);
+ return ret;
+}
+
+bool MCompositeManagerPrivate::processX11EventFilters(XEvent *event, bool after)
+{
+ if (!m_extensions.contains(event->type))
+ return false;
+
+ QList<MCompositeManagerExtension*> evlist = m_extensions.values(event->type);
+ bool processed = false;
+ if (after)
+ for (int i = 0; i < evlist.size(); ++i)
+ evlist[i]->afterX11Event(event);
+ else
+ for (int i = 0; i < evlist.size(); ++i)
+ processed = evlist[i]->x11Event(event);
+
+ return processed;
}
void MCompositeManagerPrivate::keyEvent(XKeyEvent* e)
@@ -2613,8 +2991,13 @@
memset(&ev, 0, sizeof(ev));
ev.type = ClientMessage;
ev.window = buttoned_win;
- ev.message_type = ATOM(_NET_CLOSE_WINDOW);
- rootMessageEvent(&ev);
+ ev.message_type = ATOM(WM_PROTOCOLS);
+ ev.format = 32;
+ ev.data.l[0] = ATOM(WM_DELETE_WINDOW);
+ ev.data.l[1] = CurrentTime;
+ XSendEvent(QX11Info::display(), buttoned_win, False, NoEventMask,
+ (XEvent*)&ev);
+ return;
}
if (buttoned_win) {
XButtonEvent ev = *e;
@@ -2652,13 +3035,17 @@
xcb_get_window_attributes_reply_t *attr;
attr = xcb_get_window_attributes_reply(xcb_conn,
xcb_get_window_attributes(xcb_conn, kids[i]), 0);
- if (!attr)
+ if (!attr || attr->_class == XCB_WINDOW_CLASS_INPUT_ONLY) {
+ if (attr) free(attr);
continue;
+ }
xcb_get_geometry_reply_t *geom;
geom = xcb_get_geometry_reply(xcb_conn,
xcb_get_geometry(xcb_conn, kids[i]), 0);
- if (!geom)
+ if (!geom) {
+ free(attr);
continue;
+ }
// Pre-create MWindowPropertyCache for likely application windows
if (localwin != kids[i] && (attr->map_state == XCB_MAP_STATE_VIEWABLE
|| (geom->width == xres && geom->height == yres))
@@ -2668,6 +3055,8 @@
attr, geom);
if (!p->is_valid) {
delete p;
+ free(attr);
+ free(geom);
continue;
}
prop_caches[kids[i]] = p;
@@ -2679,33 +3068,33 @@
if (attr && attr->map_state == XCB_MAP_STATE_VIEWABLE &&
localwin != kids[i] && geom &&
(geom->width > 1 && geom->height > 1)) {
+ // TODO: remove this bindWindow call -- shouldn't be needed
MCompositeWindow* window = bindWindow(kids[i]);
if (window) {
window->setNewlyMapped(false);
window->setVisible(true);
+ // synthetise MapNotify, to use the usual code path for plugins
+ XMapEvent e;
+ e.type = MapNotify;
+ e.serial = 0;
+ e.send_event = True;
+ e.event = RootWindow(QX11Info::display(), 0);
+ e.window = kids[i];
+ e.override_redirect = False;
+ XSendEvent(QX11Info::display(),
+ RootWindow(QX11Info::display(), 0),
+ False, SubstructureNotifyMask, (XEvent*)&e);
}
}
}
if (kids)
XFree(kids);
- scene()->views()[0]->setUpdatesEnabled(true);
- checkStacking(false);
-
- MCompositeWindow *item = getHighestDecorated();
- if (item && FULLSCREEN_WINDOW(item)) {
- // fullscreen window has decorator above it during ongoing call
- MDecoratorFrame::instance()->setManagedWindow(item, true);
- MDecoratorFrame::instance()->setOnlyStatusbar(true);
- } else if (item) {
- MDecoratorFrame::instance()->setManagedWindow(item);
- MDecoratorFrame::instance()->setOnlyStatusbar(false);
- }
// Wait for the MapNotify for the overlay (show() of the graphicsview
// in main() causes it even if we don't map it explicitly)
XEvent xevent;
XIfEvent(QX11Info::display(), &xevent, map_predicate, (XPointer)xoverlay);
- XUnmapWindow(QX11Info::display(), xoverlay);
+ showOverlayWindow(false);
if (!possiblyUnredirectTopmostWindow())
enableCompositing(true);
}
@@ -2715,22 +3104,20 @@
return (COMPOSITE_WINDOW(w) != 0);
}
-bool MCompositeManagerPrivate::removeWindow(Window w)
+void MCompositeManagerPrivate::removeWindow(Window w)
{
// Item is already removed from scene when it is deleted
+ int removed = 0;
+
+ STACKING("remove 0x%lx from stack", w);
+ removed += windows_as_mapped.removeAll(w);
+ removed += windows.remove(w);
+ removed += stacking_list.removeAll(w);
- bool ret = true;
- windows_as_mapped.removeAll(w);
- if (windows.remove(w) == 0)
- ret = false;
-
- stacking_list.removeAll(w);
-
for (int i = 0; i < TOTAL_LAYERS; ++i)
if (stack[i] == w) stack[i] = 0;
- updateWinList();
- return ret;
+ if (removed > 0) updateWinList();
}
static QList<Window> orig_list;
@@ -2744,6 +3131,11 @@
MCompositeWindow *cw_b = MCompositeWindow::compositeWindow(w_b);
MDecoratorFrame *deco = MDecoratorFrame::instance();
+ if (!cw_a->propertyCache()) // a is already destroyed
+ return true;
+ if (!cw_b->propertyCache()) // b is already destroyed
+ return false;
+
// a is unused decorator?
if (cw_a->propertyCache()->isDecorator() &&
(!deco->managedClient() ||
@@ -2808,6 +3200,7 @@
void MCompositeManagerPrivate::roughSort()
{
+ STACKING("sorting stack");
orig_list = stacking_list;
qSort(stacking_list.begin(), stacking_list.end(), compareWindows);
}
@@ -2819,7 +3212,6 @@
// no need for StructureNotifyMask because of root's SubstructureNotifyMask
XSelectInput(display, window, PropertyChangeMask);
XShapeSelectInput(display, window, ShapeNotifyMask);
- XCompositeRedirectWindow(display, window, CompositeRedirectManual);
MWindowPropertyCache *wpc;
if (prop_caches.contains(window)) {
@@ -2833,7 +3225,7 @@
prop_caches[window] = wpc;
}
wpc->setIsMapped(true);
- MCompositeWindow *item = new MTexturePixmapItem(window, wpc, glwidget);
+ MCompositeWindow *item = new MTexturePixmapItem(window, wpc);
if (!item->isValid()) {
item->deleteLater();
return 0;
@@ -2872,10 +3264,13 @@
item->updateWindowPixmap();
int i = stacking_list.indexOf(window);
- if (i == -1)
+ if (i == -1) {
+ STACKING("adding 0x%lx to stack", window);
stacking_list.append(window);
- else
+ } else {
+ STACKING_MOVE(i, stacking_list.size()-1);
safe_move(stacking_list, i, stacking_list.size() - 1);
+ }
roughSort();
addItem(item);
@@ -2886,14 +3281,10 @@
} else if (pc->windowType() == MCompAtoms::DESKTOP) {
// just in case startup sequence changes
stack[DESKTOP_LAYER] = window;
- connect(this, SIGNAL(inputEnabled()), item,
- SLOT(setUnBlurred()));
dirtyStacking(false);
return item;
}
- item->manipulationEnabled(true);
-
// the decorator got mapped. this is here because the compositor
// could be restarted at any point
if (pc->isDecorator() && !MDecoratorFrame::instance()->decoratorItem()) {
@@ -2913,7 +3304,6 @@
watch->addItem(item);
updateWinList();
setWindowDebugProperties(item->window());
- connect(item, SIGNAL(acceptingInput()), SLOT(enableInput()));
if (atom->windowType(item->window()) == MCompAtoms::DESKTOP) {
connect(item, SIGNAL(desktopActivated(MCompositeWindow *)),
@@ -2925,7 +3315,8 @@
connect(this, SIGNAL(compositingEnabled()), item, SLOT(startTransition()));
connect(item, SIGNAL(itemRestored(MCompositeWindow *)), SLOT(restoreHandler(MCompositeWindow *)));
connect(item, SIGNAL(itemIconified(MCompositeWindow *)), SLOT(lowerHandler(MCompositeWindow *)));
- connect(item, SIGNAL(windowClosed(MCompositeWindow *)), SLOT(closeHandler(MCompositeWindow *)));
+ connect(item, SIGNAL(closeWindowRequest(MCompositeWindow *)),
+ SLOT(closeHandler(MCompositeWindow *)));
// ping protocol
@@ -2949,13 +3340,25 @@
dirtyStacking(false);
}
+// mark application windows iconic (except those that shouldn't be)
+void MCompositeManagerPrivate::iconifyApps()
+{
+ for (int wi = stacking_list.size() - 1; wi >= 0; --wi) {
+ Window w = stacking_list.at(wi);
+ MCompositeWindow *cw = COMPOSITE_WINDOW(w);
+ if (cw && cw->propertyCache() && cw->propertyCache()->isMapped()
+ && !cw->propertyCache()->dontIconify()
+ && !cw->propertyCache()->meegoStackingLayer()
+ && cw->isAppWindow(true))
+ setWindowState(cw->window(), IconicState);
+ }
+}
+
/*!
Helper function to arrange arrange the order of the windows
in the _NET_CLIENT_LIST_STACKING
*/
-void
-MCompositeManagerPrivate::positionWindow(Window w,
- MCompositeManagerPrivate::StackPosition pos)
+void MCompositeManagerPrivate::positionWindow(Window w, bool on_top)
{
if (stacking_list.isEmpty())
return;
@@ -2964,67 +3367,112 @@
if (wp == -1 || wp >= stacking_list.size())
return;
- switch (pos) {
- case STACK_BOTTOM: {
- //qDebug() << __func__ << "to bottom:" << w;
- safe_move(stacking_list, wp, 0);
- break;
- }
- case STACK_TOP: {
+ if (on_top) {
//qDebug() << __func__ << "to top:" << w;
setWindowState(w, NormalState);
+ if (w == stack[DESKTOP_LAYER])
+ // iconify apps for roughSort()
+ iconifyApps();
+ STACKING_MOVE(wp, stacking_list.size()-1);
safe_move(stacking_list, wp, stacking_list.size() - 1);
// needed so that checkStacking() finds the current application
roughSort();
- break;
- }
- default:
- break;
-
+ } else {
+ //qDebug() << __func__ << "to bottom:" << w;
+ STACKING_MOVE(wp, 0);
+ safe_move(stacking_list, wp, 0);
}
updateWinList();
}
+void MCompositeManager::positionWindow(Window w,
+ MCompositeManager::StackPosition pos)
+{
+ d->positionWindow(w, pos == MCompositeManager::STACK_TOP ? true : false);
+}
+
+const QRect &MCompositeManager::decoratorRect() const
+{
+ return MDecoratorFrame::instance()->decoratorRect();
+}
+
+const QList<Window> &MCompositeManager::stackingList() const
+{
+ return d->stacking_list;
+}
+
void MCompositeManagerPrivate::enableCompositing(bool forced)
{
if (compositing && !forced)
return;
if (!overlay_mapped)
- mapOverlayWindow();
+ showOverlayWindow(true);
else
- enableRedirection();
+ enableRedirection(true);
}
-void MCompositeManagerPrivate::mapOverlayWindow()
+void MCompositeManagerPrivate::showOverlayWindow(bool show)
{
- // Freeze painting of framebuffer as of this point
- scene()->views()[0]->setUpdatesEnabled(false);
- XMoveWindow(QX11Info::display(), localwin, -2, -2);
- XMapWindow(QX11Info::display(), xoverlay);
+ static bool first_call = true;
+ static XRectangle empty = {0, 0, 0, 0},
+ fs = {0, 0,
+ ScreenOfDisplay(QX11Info::display(),
+ DefaultScreen(QX11Info::display()))->width + 2,
+ ScreenOfDisplay(QX11Info::display(),
+ DefaultScreen(QX11Info::display()))->height + 2};
+ if (!show && (overlay_mapped || first_call)) {
+ scene()->views()[0]->setUpdatesEnabled(false);
+ XShapeCombineRectangles(QX11Info::display(), xoverlay,
+ ShapeBounding, 0, 0, &empty, 1,
+ ShapeSet, Unsorted);
+ XShapeCombineRectangles(QX11Info::display(), localwin,
+ ShapeBounding, 0, 0, &empty, 1,
+ ShapeSet, Unsorted);
+ overlay_mapped = false;
+ } else if (show && (!overlay_mapped || first_call)) {
+#ifdef GLES2_VERSION
+ enableRedirection(false);
+#endif
+ XShapeCombineRectangles(QX11Info::display(), xoverlay,
+ ShapeBounding, 0, 0, &fs, 1,
+ ShapeSet, Unsorted);
+ XShapeCombineRectangles(QX11Info::display(), localwin,
+ ShapeBounding, 0, 0, &fs, 1,
+ ShapeSet, Unsorted);
+ XserverRegion r = XFixesCreateRegion(QX11Info::display(), &empty, 1);
+ XFixesSetWindowShapeRegion(QX11Info::display(), xoverlay,
+ ShapeInput, 0, 0, r);
+ XFixesSetWindowShapeRegion(QX11Info::display(), localwin,
+ ShapeInput, 0, 0, r);
+ XFixesDestroyRegion(QX11Info::display(), r);
+ overlay_mapped = true;
+#ifndef GLES2_VERSION
+ enableRedirection(false);
+#endif
+ emit compositingEnabled();
+ }
+ first_call = false;
}
-void MCompositeManagerPrivate::enableRedirection()
+void MCompositeManagerPrivate::enableRedirection(bool emit_signal)
{
for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
it != windows.end(); ++it) {
- MCompositeWindow *tp = it.value();
- if (tp->windowVisible())
+ MCompositeWindow *tp = it.value();
+ if (tp->isValid() && tp->isDirectRendered() && tp->propertyCache()
+ && (tp->propertyCache()->isMapped()
+ || tp->propertyCache()->beingMapped()))
((MTexturePixmapItem *)tp)->enableRedirectedRendering();
setWindowDebugProperties(it.key());
}
- XSync(QX11Info::display(), False);
-
compositing = true;
- QTimer::singleShot(100, this, SLOT(enablePaintedCompositing()));
-}
-
-void MCompositeManagerPrivate::enablePaintedCompositing()
-{
+ // no delay: application does not need to redraw when maximizing it
scene()->views()[0]->setUpdatesEnabled(true);
- glwidget->update();
- // At this point everything should be rendered off-screen
- emit compositingEnabled();
+ // NOTE: enableRedirectedRendering() calls glwidget->update() if needed
+ if (emit_signal)
+ // At this point everything should be rendered off-screen
+ emit compositingEnabled();
}
void MCompositeManagerPrivate::disableCompositing(ForcingLevel forced)
@@ -3046,7 +3494,9 @@
return;
}
- scene()->views()[0]->setUpdatesEnabled(false);
+#ifdef GLES2_VERSION
+ showOverlayWindow(false);
+#endif
for (QHash<Window, MCompositeWindow *>::iterator it = windows.begin();
it != windows.end(); ++it) {
@@ -3058,8 +3508,9 @@
setWindowDebugProperties(it.key());
}
- XUnmapWindow(QX11Info::display(), xoverlay);
- XFlush(QX11Info::display());
+#ifndef GLES2_VERSION
+ showOverlayWindow(false);
+#endif
if (MDecoratorFrame::instance()->decoratorItem())
MDecoratorFrame::instance()->lower();
@@ -3097,8 +3548,7 @@
if (!i->isAppWindow(true) ||
i->propertyCache()->windowState() == IconicState ||
// skip devicelock and screenlock windows
- i->propertyCache()->meegoStackingLayer() == 1 ||
- i->propertyCache()->meegoStackingLayer() == 2 ||
+ i->propertyCache()->dontIconify() ||
i->propertyCache()->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DESKTOP))
continue;
@@ -3119,6 +3569,12 @@
}
}
+void MCompositeManagerPrivate::installX11EventFilter(long xevent,
+ MCompositeManagerExtension* extension)
+{
+ m_extensions.insert(xevent, extension);
+}
+
void MCompositeManagerPrivate::showLaunchIndicator(int timeout)
{
if (!launchIndicator) {
@@ -3139,17 +3595,458 @@
launchIndicator->hide();
}
+static void sigusr1_handler(int signo)
+{
+ Q_UNUSED(signo)
+#ifdef WINDOW_DEBUG
+ debug_mode = !debug_mode;
+#endif
+}
+
+#ifdef WINDOW_DEBUG
+void MCompositeManager::dumpState(const char *heading)
+{
+ static const char *tf[] = { "false", "true" };
+ static const char *yn[] = { "no", "yes" };
+ int i;
+ QString line;
+ const QRect *r;
+ MCompositeWindow *cw;
+ QHash<const MCompositeManagerExtension*, QList<int> > extensions;
+
+ if (heading)
+ qDebug("%s: ", heading);
+
+ qDebug( "display: %s",
+ d->device_state->displayOff() ? "off" : "on");
+ qDebug( "call state: %s",
+ d->device_state->ongoingCall() ? "ongoing" : "idle");
+
+ qDebug( "composition: %s", isCompositing() ? "on" : "off");
+ qDebug( "xoverlay: 0x%lx, %s", d->xoverlay,
+ d->overlay_mapped ? "mapped" : "unmapped");
+ qDebug( "avail screen: %dx%d%+d%+d",
+ availScreenRect.width(), availScreenRect.height(),
+ availScreenRect.x(), availScreenRect.y());
+
+ qDebug( "current_app: 0x%lx", d->current_app);
+ qDebug( "topmostApp: 0x%lx (index: %d)",
+ d->getTopmostApp(&i), i);
+ if ((cw = d->getHighestDecorated()) != NULL)
+ qDebug("highestDecorated: 0x%lx", cw->window());
+ else
+ qDebug("highestDecorated: None");
+
+ qDebug( "local_win: 0x%lx, parent: 0x%lx",
+ d->localwin, d->localwin_parent);
+
+ qDebug( "prev_focus: 0x%lx", d->prev_focus);
+ qDebug( "buttoned_win: 0x%lx", d->buttoned_win);
+
+ // Decoration button geometries.
+ r = &d->home_button_geom;
+ qDebug( "home button: 0x%lx (%dx%d%+d%+d)",
+ d->home_button_win,
+ r->width(), r->height(), r->x(), r->y());
+ r = &d->close_button_geom;
+ qDebug( "close button: 0x%lx (%dx%d%+d%+d)",
+ d->close_button_win,
+ r->width(), r->height(), r->x(), r->y());
+
+ line = "dock_region: ";
+ if (!d->dock_region.isEmpty()) {
+ foreach (const QRect &rect, d->dock_region.rects())
+ line += QString().sprintf(" %dx%d%+d%+d",
+ rect.width(), rect.height(),
+ rect.x(), rect.y());
+ } else
+ line += " <Empty>";
+ qDebug() << line.toLatin1().constData();
+
+ // Stacking
+ qDebug( "stacking_timer: %s",
+ d->stacking_timer.isActive() ? "active" : "idle");
+ qDebug( "check_visibility: %s",
+ tf[d->stacking_timeout_check_visibility]);
+
+ // Top windows per stacking layer.
+ qDebug("stacking layers:");
+ qDebug(" input: 0x%lx", d->stack[INPUT_LAYER]);
+ qDebug(" dock: 0x%lx", d->stack[DOCK_LAYER]);
+ qDebug(" system: 0x%lx", d->stack[SYSTEM_LAYER]);
+ qDebug(" app: 0x%lx", d->stack[APPLICATION_LAYER]);
+ qDebug(" desktop: 0x%lx", d->stack[DESKTOP_LAYER]);
+
+ // Stacking order of mapped windows and mapping order of windows.
+ QList<Window>::const_iterator winit;
+ qDebug("window stack:");
+ for (winit = d->stacking_list.constEnd();
+ --winit != d->stacking_list.constBegin(); )
+ qDebug(" 0x%lx", *winit);
+ qDebug("mapping order:");
+ for (winit = d->windows_as_mapped.constEnd();
+ --winit != d->windows_as_mapped.constBegin(); )
+ qDebug(" 0x%lx", *winit);
+
+ // All MCompositeWindow:s we know about.
+ QHash<Window, MCompositeWindow *>::const_iterator cwit;
+ qDebug("windows:");
+ for (cwit = d->windows.constBegin(); cwit != d->windows.constEnd();
+ ++cwit) {
+ static const char *winstates[] = {
+ "normal", "hung", "minimizing", "closing"
+ };
+ static const char *iconstates[] = { "none", "manual", "transition" };
+ MCompositeWindow *cw, *behind;
+ char *name;
+
+ cw = *cwit;
+ Q_ASSERT(cwit.key() == cw->window());
+
+ // Determine the window's name.
+ name = NULL;
+ XFetchName(QX11Info::display(), cw->window(), &name);
+ if (!name) {
+ XClassHint cls;
+
+ cls.res_name = cls.res_class = NULL;
+ XGetClassHint(QX11Info::display(), cw->window(), &cls);
+ if (!(name = cls.res_name))
+ name = cls.res_class;
+ else if (cls.res_class)
+ XFree(cls.res_class);
+ }
+
+ qDebug(" ptr %p == xwin 0x%lx%s: %s", cw, cw->window(),
+ cw->isValid() ? "" : " (not valid anymore)",
+ name ? name : "[noname]");
+ qDebug(" mapped: %s, newly mapped: %s",
+ yn[cw->isMapped()], yn[cw->isNewlyMapped()]);
+ qDebug(" visible: %s, direct rendered: %s",
+ yn[cw->windowVisible()], yn[cw->isDirectRendered()]);
+ qDebug(" is app: %s, needs decoration: %s",
+ yn[cw->isAppWindow()], yn[cw->needDecoration()]);
+ qDebug(" status: %s, iconified: %s, iconification status: %s",
+ winstates[cw->status()], yn[cw->isIconified()],
+ iconstates[cw->iconifyState()]);
+ qDebug(" has transitioning windows: %s, transitioning: %s, "
+ "closing: %s",
+ yn[cw->hasTransitioningWindow()],
+ yn[cw->isWindowTransitioning()],
+ yn[cw->isClosing()]);
+ qDebug(" dimmed: %s, blurred: %s, scaled: %s",
+ yn[cw->dimmedEffect()], yn[cw->blurred()],
+ yn[cw->isScaled()]);
+ behind = cw->behind();
+ qDebug(" stack index: %d, behind window: 0x%lx, "
+ "last visible parent: 0x%lx", cw->indexInStack(),
+ behind ? behind->window() : 0, cw->lastVisibleParent());
+
+ // MWindowPropertyCache::transientFor() can change state,
+ // transientWindows() doesn't.
+ QList<Window> const &transients = cw->propertyCache()->transientWindows();
+ if (!transients.empty()) {
+ line = " transients:";
+ QList<Window>::const_iterator trit;
+ for (trit = transients.constBegin();
+ trit != transients.constEnd(); ++trit)
+ line += QString().sprintf(" 0x%lx", *trit);
+ qDebug() << line.toLatin1().constData();
+ }
+
+ if (name)
+ XFree(name);
+ }
+
+ if (!d->framed_windows.isEmpty()) {
+ QHash<Window, MCompositeManagerPrivate::FrameData>::const_iterator fwit;
+
+ qDebug("framed_windows:");
+ for (fwit = d->framed_windows.constBegin();
+ fwit != d->framed_windows.constEnd(); ++fwit)
+ qDebug(" 0x%lx: parent=0x%lx, mapped=%s", fwit.key(),
+ fwit->parentWindow, fwit->mapped ? "yes" : "no");
+ } else
+ qDebug("framed_windows: <None>");
+
+ // Which windows are in the property cache?
+ line = "with property cache:";
+ QHash<Window, MWindowPropertyCache*>::const_iterator pcit;
+ for (pcit = d->prop_caches.constBegin();
+ pcit != d->prop_caches.constEnd(); ++pcit)
+ line += QString().sprintf(" 0x%lx", pcit.key());
+ qDebug() << line.toLatin1().constData();
+
+ // Pending XConfigureRequestEvent:s.
+ if (!d->configure_reqs.isEmpty()) {
+ // Print each as "0x123456: 10x20+30+40 [XYWH] Above 0xABCDE"
+ QHash<Window, QList<XConfigureRequestEvent*> >::const_iterator it;
+ QList<XConfigureRequestEvent*>::const_iterator ot;
+
+ qDebug("configure_reqs:");
+ for (it = d->configure_reqs.constBegin();
+ it != d->configure_reqs.constEnd(); ++it) {
+ for (ot = it->constBegin(); ot != it->constEnd(); ++ot) {
+ const XConfigureRequestEvent *ev = *ot;
+
+ // The requested geometry
+ line = QString().sprintf(" 0x%lx: %dx%d%+d%+d", it.key(),
+ ev->width, ev->height,
+ ev->x, ev->y);
+
+ // What is to be changed
+ if (ev->value_mask & (CWX|CWY|CWWidth|CWHeight)) {
+ line += " [";
+ if (ev->value_mask & CWX)
+ line += 'X';
+ if (ev->value_mask & CWY)
+ line += 'Y';
+ if (ev->value_mask & CWWidth)
+ line += 'W';
+ if (ev->value_mask & CWHeight)
+ line += 'H';
+ line += ']';
+ }
+
+ // Print the new stack mode and possibly the new sibling.
+ if (ev->value_mask & CWStackMode) {
+ line += " stacking: ";
+ if (ev->detail == Above)
+ line += "above";
+ else if (ev->detail == Below)
+ line += "below";
+ else if (ev->detail == TopIf)
+ line += "topif";
+ else if (ev->detail == BottomIf)
+ line += "bottomif";
+ else if (ev->detail == Opposite)
+ line += "opposite";
+ else
+ line += QString().sprintf("%d", ev->detail);
+
+ if (ev->value_mask & CWSibling)
+ line += QString().sprintf(" 0x%lx", ev->above);
+ }
+ }
+ }
+ } else
+ qDebug("configure_reqs: <None>");
+
+ // Dump the scene items from top to bottom.
+ qDebug("scene:");
+ foreach (const QGraphicsItem *gi, d->watch->items()) {
+ if (!gi) {
+ qDebug(" NULL (WTF?)");
+ continue;
+ }
+
+ const QRectF &r = gi->sceneBoundingRect();
+ const MCompositeWindow *cw = dynamic_cast<const MCompositeWindow *>(gi);
+ qDebug(" %p: %dx%d%+d%+d %s", cw ? (void *)cw : (void *)gi,
+ (int)r.width(), (int)r.height(), (int)r.x(), (int)r.y(),
+ gi->isVisible() ? "visible" : "hidden");
+ }
+
+ // Show the current state of extensions.
+ // @m_extenions is a QMultiHash of X events an extension reacts to
+ // pointing to the object. Invert the hash so we can iterate over
+ // each extension once.
+ qDebug("plugins:");
+ for (QHash<int, MCompositeManagerExtension*>::const_iterator exit = d->m_extensions.constBegin(); exit != d->m_extensions.constEnd(); ++exit)
+ extensions[*exit].append(exit.key());
+ for (QHash<const MCompositeManagerExtension*, QList<int> >::const_iterator exit = extensions.constBegin(); exit != extensions.constEnd(); ++exit) {
+ int event;
+ bool first;
+ QString events;
+
+ // Print the extension's class name followed by its X events.
+ first = true;
+ foreach (event, *exit) {
+ if (first)
+ first = false;
+ else
+ events += ", ";
+
+ // Translate common event numbers to strings.
+ switch (event) {
+ case ButtonPress: events += "ButtonPress"; break;
+ case ButtonRelease: events += "ButtonRelease"; break;
+ case MotionNotify: events += "Motion"; break;
+ case UnmapNotify: events += "Unmap"; break;
+ case MapNotify: events += "Map"; break;
+ case ConfigureNotify: events += "Configure"; break;
+ case PropertyNotify: events += "Property"; break;
+ default:
+ events += QString().sprintf("%u", event);
+ break;
+ }
+ }
+ qDebug("-- %s for event(s) %s:",
+ exit.key()->metaObject()->className(),
+ events.toLatin1().constData());
+ exit.key()->dumpState();
+ }
+}
+
+// Called when the remote control pipe has got input.
+void MCompositeManager::remoteControl(int cmdfd)
+{
+ int lcmd;
+ char cmd[128];
+
+ if ((lcmd = ::read(cmdfd, cmd, sizeof(cmd)-1)) < 0)
+ return;
+ if (lcmd > 0 && cmd[lcmd-1] == '\n')
+ lcmd--;
+ cmd[lcmd] = '\0';
+
+ if (!strcmp(cmd, "state")) {
+ dumpState();
+ } else if (!strncmp(cmd, "state ", strlen("state "))) {
+ const char *space = &cmd[strlen("state")];
+ dumpState(space+strspn(space, " "));
+ } else if (!strcmp(cmd, "save")
+ || !strncmp(cmd, "save ", strlen("save "))) {
+ // dumpState() into a file
+ static unsigned cnt = 0;
+ int pos;
+ time_t now;
+ QString fname;
+ const char *cfname;
+ FILE *out, *saved_stderr;
+ QRegExp rex("%(\\.\\d+)?[diuxX]");
+
+ // Get the output file name.
+ if ((cfname = strchr(cmd, ' ')) != NULL)
+ cfname += strspn(cfname, " ");
+ fname = cfname && *cfname ? cfname : "mc.log.%.2u";
+
+ // Substitute @res with @cnt if necessary.
+ if ((pos = rex.indexIn(fname)) >= 0)
+ fname.replace(pos, rex.cap(0).length(),
+ QString().sprintf(rex.cap(0).toLatin1().constData(),
+ cnt++));
+
+ // Open @out.
+ cfname = fname.toLatin1().constData();
+ if (!(out = fopen(cfname, "w"))) {
+ qDebug("couldn't open %s", cfname);
+ return;
+ }
+
+ // Temporarily replace @stderr, so qDebug() will output to @out.
+ saved_stderr = stderr;
+ stderr = out;
+ time(&now);
+ dumpState(ctime(&now));
+ stderr = saved_stderr;
+
+ fclose(out);
+ qDebug("state dumped into %s", fname.toLatin1().constData());
+ } else if (!strcmp(cmd, "restart")) {
+ QString me = qApp->applicationFilePath();
+ QStringList args = qApp->arguments();
+ const char **argv;
+ unsigned i;
+
+ delete d;
+ XFlush(QX11Info::display());
+
+ // Convert the QStringList of args into a char *[].
+ i = 0;
+ argv = new const char *[args.count()+1];
+ foreach (const QString &arg, args)
+ argv[i] = arg.toLatin1().constData();
+ argv[i] = NULL;
+
+ // Restart ourselves.
+ qDebug("Die, you son of a bitch!");
+ execv(me.toLatin1().constData(), (char **)argv);
+ qDebug("Gothca!");
+ } else if (!strcmp(cmd, "exit") || !strcmp(cmd, "quit")) {
+ // exit() deadlocks, go the fast route
+ delete d;
+ XFlush(QX11Info::display());
+ _exit(0);
+ } else if (!strcmp(cmd, "help")) {
+ qDebug("Commands i understand:");
+ qDebug(" state [<tag>] dump MCompositeManager, MCompositeWindow:s ");
+ qDebug(" and QGraphicsScene state information");
+ qDebug(" save [<fname>] dump it into <fname>");
+ qDebug(" exit, quit geez");
+ qDebug(" restart re-execute mcompositor");
+ } else
+ qDebug("%s: unknown command", cmd);
+}
+
+void MCompositeManager::xtrace(const char *fun, const char *msg, int lmsg)
+{
+ MCompositeManager *p = static_cast<MCompositeManager *>(qApp);
+ char str[160];
+
+ // Normalize @fun and @msg so that @msg != NULL in the end,
+ // and turn synopsis [2] into MCompositor::xtrace(NULL, msg).
+ if (!msg) {
+ if (fun) {
+ msg = fun;
+ fun = NULL;
+ } else {
+ msg = "HERE";
+ lmsg = strlen("HERE");
+ }
+ }
+
+ // Fail if we don't have an X connection yet.
+ if (!p || !p->d || !p->d->xcb_conn) {
+ qWarning("cannot xtrace yet from %s", fun ? fun : msg);
+ return;
+ }
+
+ // Format @str to include both @fun and @msg if @fun was specified,
+ // and count the length of @str.
+ if (fun != NULL) {
+ lmsg = snprintf(str, sizeof(str), "%s from %s", msg, fun);
+ msg = str;
+ } else if (lmsg < 0)
+ lmsg = strlen(msg);
+
+ // Make @str visible in xtrace by sending it along with an innocent
+ // X request. Unfortunately this makes this function a synchronisation
+ // point (it has to wait for the reply). Use xcb rather than libx11
+ // because the latter maintains a hashtable of known Atom:s.
+ xcb_intern_atom_reply(p->d->xcb_conn,
+ xcb_intern_atom(p->d->xcb_conn, False, lmsg, msg),
+ NULL);
+}
+
+void MCompositeManager::xtracef(const char *fun, const char *fmt, ...)
+{
+ va_list printf_args;
+ char msg[160];
+ int lmsg;
+
+ va_start(printf_args, fmt);
+ lmsg = vsnprintf(msg, sizeof(msg), fmt, printf_args);
+ va_end(printf_args);
+ xtrace(fun, msg, lmsg);
+}
+#endif // WINDOW_DEBUG
+
MCompositeManager::MCompositeManager(int &argc, char **argv)
: QApplication(argc, argv)
{
- if (QX11Info::isCompositingManagerRunning()) {
- qCritical("Compositing manager already running.");
- ::exit(0);
- }
+ signal(SIGUSR1, sigusr1_handler);
d = new MCompositeManagerPrivate(this);
MRmiServer *s = new MRmiServer(".mcompositor", this);
s->exportObject(this);
+
+#ifdef WINDOW_DEBUG
+ // Open the remote control interface.
+ mknod("/tmp/mrc", S_IFIFO | 0666, 0);
+ connect(new QSocketNotifier(open("/tmp/mrc", O_RDWR), QSocketNotifier::Read),
+ SIGNAL(activated(int)), SLOT(remoteControl(int)));
+#endif
}
MCompositeManager::~MCompositeManager()
@@ -3158,21 +4055,41 @@
d = 0;
}
+const QHash<Window, MWindowPropertyCache*>& MCompositeManager::propCaches() const
+{
+ return d->prop_caches;
+}
+
void MCompositeManager::setGLWidget(QGLWidget *glw)
{
d->glwidget = glw;
}
+QGLWidget *MCompositeManager::glWidget() const
+{
+ return d->glwidget;
+}
+
QGraphicsScene *MCompositeManager::scene()
{
return d->scene();
}
void MCompositeManager::prepareEvents()
-{
+{
+ if (QX11Info::isCompositingManagerRunning()) {
+ qCritical("Compositing manager already running.");
+ ::exit(0);
+ }
+
d->prepare();
}
+void MCompositeManager::loadPlugins()
+{
+ d->loadPlugins();
+}
+
bool MCompositeManager::x11EventFilter(XEvent *event)
{
return d->x11EventFilter(event);
@@ -3193,9 +4110,9 @@
return d->isRedirected(w);
}
-void MCompositeManager::enableCompositing()
+void MCompositeManager::enableCompositing(bool forced)
{
- d->enableCompositing();
+ d->enableCompositing(forced);
}
void MCompositeManager::disableCompositing()
@@ -3218,4 +4135,23 @@
return d->compositing;
}
-#include "mcompositemanager.moc"
+void MCompositeManager::debug(const QString& d)
+{
+ const char* msg = d.toAscii();
+ _log("%s\n", msg);
+}
+
+bool MCompositeManager::displayOff()
+{
+ return d->device_state->displayOff();
+}
+
+bool MCompositeManager::possiblyUnredirectTopmostWindow()
+{
+ return d->possiblyUnredirectTopmostWindow();
+}
+
+void MCompositeManager::exposeSwitcher()
+{
+ d->exposeSwitcher();
+}
--- src/mcompositemanager.h
+++ src/mcompositemanager.h
@@ -22,6 +22,7 @@
#include <QApplication>
#include <QGLWidget>
+#include "mwindowpropertycache.h"
class QGraphicsScene;
class MCompositeManagerPrivate;
@@ -68,6 +69,9 @@
*/
void setGLWidget(QGLWidget *glw);
+ /*! QGLWidget accessor for static initialisations. */
+ QGLWidget *glWidget() const;
+
/*!
* Reimplemented from QApplication::x11EventFilter() to catch X11 events
*/
@@ -94,6 +98,8 @@
*/
void redirectWindows();
+ void loadPlugins();
+
/*!
* Returns whether a Window is redirected or not
*
@@ -106,9 +112,51 @@
* or not
*/
bool isCompositing();
+
+ /*!
+ * Try to direct-render the topmost window
+ */
+ bool possiblyUnredirectTopmostWindow();
+ /*!
+ * Returns if the display is off
+ */
+ bool displayOff();
+
+ void debug(const QString& d);
+ const QHash<Window, MWindowPropertyCache*>& propCaches() const;
+
+ enum StackPosition {
+ STACK_BOTTOM = 0,
+ STACK_TOP
+ };
+ void positionWindow(Window w, StackPosition pos);
+ void setWindowState(Window, int);
+ const QList<Window> &stackingList() const;
+
+#ifdef WINDOW_DEBUG
+ // Dump the current state of MCompositeManager and MCompositeWindow:s
+ // to qDebug(). Only available if compiled with TESTABILITY=on
+ // (-DWINDOW_DEBUG).
+ void dumpState(const char *heading = 0);
+
+ // "Print" @msg in xtrace, to show you where your program's control was
+ // between the various X requests, responses and events.
+ // Synopsis:
+ // [1] MCompositeManager::xtrace();
+ // [2] MCompositeManager::xtrace("HEI");
+ // [3] MCompositeManager::xtrace(__PRETTY_FUNCTION__, "HEI");
+ //
+ // xtracef() is the same, except that it sends a formatted message.
+ // You can leave @fun NULL if you want.
+ static void xtrace (const char *fun = NULL, const char *msg = NULL,
+ int lmsg = -1);
+ static void xtracef(const char *fun, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+#endif
+
public slots:
- void enableCompositing();
+ void enableCompositing(bool forced = false);
void disableCompositing();
/*! Invoked remotely by MRmiClient to show a launch indicator
@@ -118,6 +166,20 @@
*/
void showLaunchIndicator(int timeout);
void hideLaunchIndicator();
+
+ /*!
+ * Invoke to show the desktop window, possibly with switcher contents
+ */
+ void exposeSwitcher();
+
+ /*!
+ * Area the decorator occupies.
+ */
+ const QRect &decoratorRect() const;
+
+#ifdef WINDOW_DEBUG
+ void remoteControl(int fd);
+#endif
signals:
void decoratorRectChanged(const QRect& rect);
@@ -127,6 +189,9 @@
friend class MCompositeWindow;
friend class MCompWindowAnimator;
+ friend class MCompositeManagerExtension;
+ friend class MTexturePixmapPrivate;
+ friend class MWindowPropertyCache;
};
#endif
--- src/mcompositemanager_p.h
+++ src/mcompositemanager_p.h
@@ -39,6 +39,7 @@
class MCompositeWindow;
class MDeviceState;
class MWindowPropertyCache;
+class MCompositeManagerExtension;
enum {
INPUT_LAYER = 0,
@@ -57,10 +58,6 @@
{
Q_OBJECT
public:
- enum StackPosition {
- STACK_BOTTOM = 0,
- STACK_TOP
- };
enum ForcingLevel {
NO_FORCED = 0,
FORCED
@@ -74,12 +71,14 @@
QGraphicsScene *scene();
void prepare();
+ void loadPlugins();
void activateWindow(Window w, Time timestamp,
bool disableCompositing = true);
void updateWinList();
void setWindowState(Window , int);
void setWindowDebugProperties(Window w);
- void positionWindow(Window w, StackPosition pos);
+ void iconifyApps();
+ void positionWindow(Window w, bool on_top);
void addItem(MCompositeWindow *item);
void damageEvent(XDamageNotifyEvent *);
void destroyEvent(XDestroyWindowEvent *);
@@ -93,10 +92,11 @@
void clientMessageEvent(XClientMessageEvent *);
void keyEvent(XKeyEvent*);
void buttonEvent(XButtonEvent*);
+ void installX11EventFilter(long xevent, MCompositeManagerExtension* extension);
void redirectWindows();
- void mapOverlayWindow();
- void enableRedirection();
+ void showOverlayWindow(bool show);
+ void enableRedirection(bool emit_signal);
void setExposeDesktop(bool exposed);
void checkStacking(bool force_visibility_check,
Time timestamp = CurrentTime);
@@ -104,26 +104,31 @@
void configureWindow(MCompositeWindow *cw, XConfigureRequestEvent *e);
Window getTopmostApp(int *index_in_stacking_list = 0,
- Window ignore_window = 0);
+ Window ignore_window = 0,
+ bool skip_always_mapped = false);
Window getLastVisibleParent(MWindowPropertyCache *pc);
- void setupButtonWindows(MCompositeWindow *topmost);
bool possiblyUnredirectTopmostWindow();
+ bool haveMappedWindow() const;
bool isRedirected(Window window);
bool x11EventFilter(XEvent *event);
- bool removeWindow(Window w);
+ bool processX11EventFilters(XEvent *event, bool after);
+ void removeWindow(Window w);
bool needDecoration(Window w, MWindowPropertyCache *pc = 0);
MCompositeWindow *getHighestDecorated();
static bool compareWindows(Window w_a, Window w_b);
void roughSort();
- void setCurrentApp(Window w);
+ void setCurrentApp(Window w, bool stacking_order_changed);
+ void raiseTransientsOf(MWindowPropertyCache *pc, int last_i,
+ bool recursion = false);
MCompositeScene *watch;
Window localwin, localwin_parent;
Window xoverlay;
Window prev_focus;
Window close_button_win, home_button_win, buttoned_win;
+ Window current_app;
QRect home_button_geom, close_button_geom;
static Window stack[TOTAL_LAYERS];
@@ -144,6 +149,7 @@
QHash<Window, FrameData> framed_windows;
QHash<Window, QList<XConfigureRequestEvent*> > configure_reqs;
QHash<Window, MWindowPropertyCache*> prop_caches;
+ QMultiHash<int, MCompositeManagerExtension* > m_extensions;
QRegion dock_region;
int damage_event;
@@ -151,25 +157,29 @@
bool compositing;
bool overlay_mapped;
+ bool changed_properties;
MDeviceState *device_state;
+ // Indicates whether MCompositeManager::prepare() has finished.
+ // Used by the destructor.
+ bool prepared;
+
xcb_connection_t *xcb_conn;
// mechanism for lazy stacking
QTimer stacking_timer;
bool stacking_timeout_check_visibility;
- void dirtyStacking(bool force_visibility_check);
+ Time stacking_timeout_timestamp;
+ void dirtyStacking(bool force_visibility_check, Time t = CurrentTime);
void pingTopmost();
signals:
- void inputEnabled();
void compositingEnabled();
+ void currentAppChanged(Window w);
public slots:
void gotHungWindow(MCompositeWindow *window);
- void enableInput();
- void disableInput();
void enableCompositing(bool forced = false);
void disableCompositing(ForcingLevel forced = NO_FORCED);
void showLaunchIndicator(int timeout);
@@ -181,12 +191,12 @@
void onDesktopActivated(MCompositeWindow*);
void exposeDesktop();
- void enablePaintedCompositing();
void exposeSwitcher();
void displayOff(bool display_off);
void callOngoing(bool call_ongoing);
void stackingTimeout();
+ void setupButtonWindows(Window topmost);
};
#endif
--- src/mcompositemanagerextension.cpp
+++ src/mcompositemanagerextension.cpp
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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 "mcompositemanager.h"
+#include "mcompositemanager_p.h"
+#include "mcompositemanagerextension.h"
+
+MCompositeManagerExtension::MCompositeManagerExtension(QObject *parent)
+ :QObject(parent)
+{
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ connect(p->d, SIGNAL(currentAppChanged(Window)), SLOT(q_currentAppChanged(Window)) );
+}
+
+MCompositeManagerExtension::~MCompositeManagerExtension()
+{
+}
+
+void MCompositeManagerExtension::q_currentAppChanged(Window window)
+{
+ emit currentAppChanged(window);
+}
+
+void MCompositeManagerExtension::listenXEventType(long XEventType)
+{
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ p->d->installX11EventFilter(XEventType, this);
+}
+
+void MCompositeManagerExtension::dumpState() const
+{
+ /* NOP */
+}
+
+Qt::HANDLE MCompositeManagerExtension::desktopWindow()
+{
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ return p->d->stack[DESKTOP_LAYER];
+}
+
+Qt::HANDLE MCompositeManagerExtension::currentAppWindow()
+{
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ return p->d->current_app;
+}
+
+bool MCompositeManagerExtension::windowIconified(MCompositeWindow* window,
+ bool deferred)
+{
+ Q_UNUSED(window)
+ Q_UNUSED(deferred)
+
+ return false;
+}
+
+bool MCompositeManagerExtension::windowRestored(MCompositeWindow* window,
+ bool deferred)
+{
+ Q_UNUSED(window)
+ Q_UNUSED(deferred)
+
+ return false;
+}
+
+bool MCompositeManagerExtension::windowShown(MCompositeWindow* window)
+{
+ Q_UNUSED(window)
+
+ return false;
+}
+
+bool MCompositeManagerExtension::windowClosed(MCompositeWindow* window)
+{
+ Q_UNUSED(window)
+
+ return false;
+}
--- src/mcompositemanagerextension.h
+++ src/mcompositemanagerextension.h
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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.
+**
+****************************************************************************/
+
+#ifndef MCOMPOSITEMANAGEREXTENSION_H
+#define MCOMPOSITEMANAGEREXTENSION_H
+
+#include <QObject>
+#include <X11/Xlib.h>
+
+class MCompositeManagerPrivate;
+class MCompositeWindow;
+
+class MCompositeManagerExtension: public QObject
+{
+ Q_OBJECT
+
+ public:
+ MCompositeManagerExtension(QObject *parent = 0);
+ ~MCompositeManagerExtension();
+
+ void testFn();
+
+ /*
+ * Returns the desktop window
+ */
+ Qt::HANDLE desktopWindow();
+
+ /*
+ * Returns the current application window
+ */
+ Qt::HANDLE currentAppWindow();
+
+ /*!
+ * Informs the composite manager that this extension is subscribed
+ * to an XEvent type and requests delivery of only the registered event to
+ * prevent it from being spammed by unwanted events.
+ * Note: Do not combine the types in the same way as the
+ * event mask in XSelectInput. XEvent type is not a mask enum type and will
+ * not work. Call this function once for each XEvent type you need to
+ * register with.
+ *
+ * \param XEventType Specifies the XEvent type that this extension is
+ * interested in listening
+ */
+ void listenXEventType(long XEventType);
+
+ //! qDebug() any state information indented by three spaces you want
+ //! to be included in MCompositeManager::dumpState()'s output.
+ virtual void dumpState() const;
+
+ signals:
+ void currentAppChanged(Qt::HANDLE window);
+
+ protected:
+ /*!
+ * Special event handler to receive native X11 events passed in the event
+ * parameter. Return true to stop the composite manager from handling
+ * the event, otherwise return false to try forwarding the native event to
+ * the composite manager.
+ *
+ * If there are other extensions that are subscribed to the same event and
+ * this function returns true, the event will still be blocked from
+ * propagating to the composite manager even if this function
+ * returns false. Be careful when returning true when there are other
+ * extensions around and only use as a last resort to reimplement core
+ * functionality.
+ */
+ virtual bool x11Event(XEvent *event) = 0;
+
+ /*!
+ * Called for each event after MCompositeManager's event handling.
+ */
+ virtual void afterX11Event(XEvent *event) = 0;
+
+ virtual bool windowIconified(MCompositeWindow* window, bool deferred);
+ virtual bool windowRestored(MCompositeWindow* window, bool deferred);
+ virtual bool windowShown(MCompositeWindow* window);
+ virtual bool windowClosed(MCompositeWindow* window);
+
+ private slots:
+ void q_currentAppChanged(Window window);
+
+ private:
+ friend class MCompositeManagerPrivate;
+ friend class MCompositeWindow;
+};
+
+#endif
--- src/mcompositescene.cpp
+++ src/mcompositescene.cpp
@@ -27,6 +27,7 @@
#include "mcompositewindow.h"
#include "mcompositescene.h"
+#include "mcompositewindowgroup.h"
#include <X11/extensions/Xfixes.h>
#ifdef HAVE_SHAPECONST
@@ -67,67 +68,71 @@
XSetWindowAttributes sattr;
sattr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask | PropertyChangeMask;
- //XCompositeRedirectSubwindows (dpy, root, CompositeRedirectAutomatic);
+ // All newly mapped windows should be redirected to avoid double Expose
+ XCompositeRedirectSubwindows(dpy, root, CompositeRedirectManual);
XChangeWindowAttributes(dpy, root, CWEventMask, &sattr);
- XSelectInput(dpy, root, SubstructureNotifyMask | SubstructureRedirectMask | StructureNotifyMask | PropertyChangeMask);
+ XSelectInput(dpy, root, SubstructureNotifyMask | SubstructureRedirectMask
+ | StructureNotifyMask | PropertyChangeMask
+ | FocusChangeMask);
XSetErrorHandler(error_handler);
}
-
-void MCompositeScene::setupOverlay(Window window, const QRect &geom,
- bool restoreInput)
-{
- Display *dpy = QX11Info::display();
- XRectangle rect;
-
- rect.x = geom.x();
- rect.y = geom.y();
- rect.width = geom.width();
- rect.height = geom.height();
- XserverRegion region = XFixesCreateRegion(dpy, &rect, 1);
-
- XFixesSetWindowShapeRegion(dpy, window, ShapeBounding, 0, 0, 0);
- if (!restoreInput)
- XFixesSetWindowShapeRegion(dpy, window, ShapeInput, 0, 0, region);
- else
- XFixesSetWindowShapeRegion(dpy, window, ShapeInput, 0, 0, 0);
-
- XFixesDestroyRegion(dpy, region);
-}
-
void MCompositeScene::drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[], QWidget *widget)
{
QRegion visible(sceneRect().toRect());
QVector<int> to_paint(10);
int size = 0;
+ bool desktop_painted = false;
// visibility is determined from top to bottom
for (int i = numItems - 1; i >= 0; --i) {
MCompositeWindow *cw = (MCompositeWindow *) items[i];
-
- if (cw->isDirectRendered() || !cw->isVisible()
- || !cw->propertyCache()->isMapped()
- || cw->propertyCache()->isInputOnly())
- continue;
- if (visible.isEmpty())
- // nothing below is visible anymore
- break;
-
- // FIXME: this region is always the same as the window's shape,
- // some transformations would be needed...
- QRegion r(cw->sceneMatrix().map(cw->propertyCache()->shapeRegion()));
+
+#ifdef GLES2_VERSION
+ static int item_type = MCompositeWindowGroup::Type;
+#else
+ static int item_type = QGraphicsItem::Type + 2;
+#endif
+ if (cw->type() != item_type) {
+ if (!cw->propertyCache()) // this window is dead
+ continue;
+ if (cw->hasTransitioningWindow() && cw->propertyCache()->isDecorator())
+ // if we have a transition animation, don't draw the decorator
+ // lest we can have it drawn with the transition (especially
+ // when desktop window is not yet shown, NB#192454)
+ continue;
+ if (cw->isDirectRendered() || !cw->isVisible()
+ || !(cw->propertyCache()->isMapped() || cw->isWindowTransitioning())
+ || cw->propertyCache()->isInputOnly())
+ continue;
+ if (visible.isEmpty())
+ // nothing below is visible anymore
+ break;
+ }
+
+ // Ensure that intersects() still work, otherwise, painting a window
+ // is skipped when another window above it is scaled or moved to an
+ // area that exposed the lower window and causes an ugly flicker.
+ // r reflects the applied transformation and position of the window
+ QRegion r = cw->sceneTransform().map(cw->propertyCache()->shapeRegion());
// transitioning window can be smaller than shapeRegion(), so paint
// all transitioning windows
- if (cw->isWindowTransitioning() || visible.intersects(r)) {
+ if (cw->isWindowTransitioning() || visible.intersects(r)
+ || cw->type() == MCompositeWindowGroup::Type) {
if (size >= 9)
to_paint.resize(to_paint.size()+1);
to_paint[size++] = i;
+ if (cw->propertyCache()->windowTypeAtom()
+ == ATOM(_NET_WM_WINDOW_TYPE_DESKTOP))
+ desktop_painted = true;
}
// subtract opaque regions
if (!cw->isWindowTransitioning()
- && !cw->propertyCache()->hasAlpha() && cw->opacity() == 1.0)
+ && !cw->propertyCache()->hasAlpha()
+ && cw->opacity() == 1.0
+ && !cw->group()) // window is not renderered off-screen)
visible -= r;
}
if (size > 0) {
@@ -135,6 +140,20 @@
for (int i = size - 1; i >= 0; --i) {
int item_i = to_paint[i];
painter->save();
+ if (!desktop_painted) {
+ // clear rubbish from the root window during startup when
+ // desktop window does not exist and we show zoom animations
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ desktop_painted = true;
+ if (((MCompositeWindow*)items[item_i])->
+ propertyCache()->isDecorator()) {
+ // don't paint decorator on top of plain black background
+ // (see NB#182860, NB#192454)
+ painter->restore();
+ continue;
+ }
+ }
// TODO: paint only the intersected region (glScissor?)
painter->setMatrix(items[item_i]->sceneMatrix(), true);
items[item_i]->paint(painter, &options[item_i], widget);
--- src/mcompositescene.h
+++ src/mcompositescene.h
@@ -52,16 +52,6 @@
*/
void prepareRoot();
- /*!
- * Creates an event "hole" within the Window win so that events generated
- * by win can pass through down to the Window below it.
- *
- * \param win Window id of the Window you intend to generate a pass-through
- * area
- * \param geom Geometry of the area within win used as a pass-through area
- */
- void setupOverlay(Window win, const QRect &geom,
- bool restoreInput = false);
protected:
void drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], const QStyleOptionGraphicsItem options[], QWidget *widget);
--- src/mcompositewindow.cpp
+++ src/mcompositewindow.cpp
@@ -23,6 +23,8 @@
#include "mcompositemanager_p.h"
#include "mtexturepixmapitem.h"
#include "mdecoratorframe.h"
+#include "mcompositemanagerextension.h"
+#include "mcompositewindowgroup.h"
#include <QX11Info>
#include <QGraphicsScene>
@@ -42,6 +44,8 @@
scaleto(1),
scaled(false),
zval(1),
+ sent_ping_timestamp(0),
+ received_ping_timestamp(0),
blur(false),
iconified(false),
iconified_final(false),
@@ -49,20 +53,18 @@
destroyed(false),
window_status(Normal),
need_decor(false),
- window_obscured(true), // true to synthesize initial visibility event
+ window_obscured(-1),
is_transitioning(false),
pinging_enabled(false),
dimmed_effect(false),
+ waiting_for_damage(0),
win_id(window)
{
thumb_mode = false;
- if (!mpc->is_valid) {
+ if (!mpc || (mpc && !mpc->is_valid)) {
is_valid = false;
- pc = 0;
anim = 0;
newly_mapped = false;
- sent_ping_timestamp = 0;
- received_ping_timestamp = 0;
t_ping = 0;
window_visible = false;
return;
@@ -70,11 +72,16 @@
is_valid = true;
anim = new MCompWindowAnimator(this);
connect(anim, SIGNAL(transitionDone()), SLOT(finalizeState()));
- connect(anim, SIGNAL(transitionStart()), SLOT(windowTransitioning()));
+ connect(anim, SIGNAL(transitionStart()), SLOT(beginAnimation()));
+ connect(mpc, SIGNAL(iconGeometryUpdated()), SLOT(updateIconGeometry()));
setAcceptHoverEvents(true);
t_ping = new QTimer(this);
connect(t_ping, SIGNAL(timeout()), SLOT(pingTimeout()));
+
+ damage_timer = new QTimer(this);
+ damage_timer->setSingleShot(true);
+ connect(damage_timer, SIGNAL(timeout()), SLOT(damageTimeout()));
MCompAtoms* atoms = MCompAtoms::instance();
if (pc->windowType() == MCompAtoms::NORMAL)
@@ -91,12 +98,13 @@
// We initially prevent item visibility from compositor itself
// or it's corresponding thumbnail rendered by the switcher
bool is_app = isAppWindow();
+ newly_mapped = is_app;
if (!pc->isInputOnly()) {
// never paint InputOnly windows
window_visible = !is_app;
- setVisible(window_visible);
+ setVisible(window_visible); // newly_mapped used here
}
- newly_mapped = is_app;
+ origPosition = QPointF(pc->realGeometry().x(), pc->realGeometry().y());
if (fadeRect.isEmpty()) {
QRectF d = QApplication::desktop()->availableGeometry();
@@ -109,18 +117,14 @@
MCompositeWindow::~MCompositeWindow()
{
MCompositeManager *p = (MCompositeManager *) qApp;
- if (!p->d->removeWindow(window()))
- qWarning("destroyEvent(): Error removing window");
+ p->d->removeWindow(window());
- if (t_ping) {
+ if (window() && t_ping) {
stopPing();
t_ping = 0;
}
- if (is_transitioning) {
- // we got destroyed during animation
- --window_transitioning;
- is_transitioning = false;
- }
+ endAnimation();
+
anim = 0;
if (pc) {
@@ -135,12 +139,6 @@
update();
}
-void MCompositeWindow::setUnBlurred()
-{
- blur = false;
- update();
-}
-
bool MCompositeWindow::blurred()
{
return blur;
@@ -173,29 +171,54 @@
*/
void MCompositeWindow::iconify(const QRectF &icongeometry, bool defer)
{
+ if (iconify_state == ManualIconifyState) {
+ setIconified(true);
+ window_status = Normal;
+ return;
+ }
+
if (window_status != MCompositeWindow::Closing)
window_status = MCompositeWindow::Minimizing;
-
- if (icongeometry.isEmpty())
- this->iconGeometry = fadeRect;
- else
- this->iconGeometry = icongeometry;
+
+ // Custom iconify handler
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ QList<MCompositeManagerExtension*> evlist = p->d->m_extensions.values(MapNotify);
+ for (int i = 0; i < evlist.size(); ++i) {
+ if (evlist[i]->windowIconified(this, defer)) {
+ iconified = true;
+ window_status = Normal;
+ return;
+ }
+ }
+
+ this->iconGeometry = icongeometry;
if (!iconified)
origPosition = pos();
// horizontal and vert. scaling factors
qreal sx = iconGeometry.width() / boundingRect().width();
qreal sy = iconGeometry.height() / boundingRect().height();
-
+
anim->deferAnimation(defer);
anim->translateScale(qreal(1.0), qreal(1.0), sx, sy,
iconGeometry.topLeft());
iconified = true;
// do this to avoid stacking code disturbing Z values
- if (!is_transitioning) {
- ++window_transitioning;
- is_transitioning = true;
- }
+ beginAnimation();
+}
+
+void MCompositeWindow::setUntransformed()
+{
+ endAnimation();
+
+ if (anim)
+ anim->stopAnimation(); // stop and restore the matrix
+ newly_mapped = false;
+ setVisible(true);
+ setOpacity(1.0);
+ setScale(1.0);
+ setScaled(false);
+ iconified = false;
}
void MCompositeWindow::setIconified(bool iconified)
@@ -213,11 +236,19 @@
return iconify_state;
}
+void MCompositeWindow::setIconifyState(MCompositeWindow::IconifyState state)
+{
+ iconify_state = state;
+}
+
void MCompositeWindow::setWindowObscured(bool obscured, bool no_notify)
{
- if (obscured == window_obscured)
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ short new_value = obscured ? 1 : 0;
+ if ((new_value == window_obscured && !newly_mapped)
+ || (!obscured && p->displayOff()))
return;
- window_obscured = obscured;
+ window_obscured = new_value;
if (!no_notify) {
XVisibilityEvent c;
@@ -231,8 +262,19 @@
}
}
+/*
+ * We ensure that ensure there are ALWAYS updated thumbnails in the
+ * switcher by letting switcher know in advance of the presence of this window.
+ * Delay the minimize animation until we receive an iconGeometry update from
+ * the switcher
+ */
void MCompositeWindow::startTransition()
{
+ if (iconified) {
+ if (iconGeometry.isNull())
+ return;
+ setWindowObscured(true);
+ }
if (anim->pendingAnimation()) {
MCompositeWindow::setVisible(true);
anim->startAnimation();
@@ -240,9 +282,38 @@
}
}
+void MCompositeWindow::updateIconGeometry()
+{
+ if (!pc)
+ return;
+
+ iconGeometry = pc->iconGeometry();
+ if (iconGeometry.isNull())
+ return;
+
+ // trigger transition the second time around and update animation values
+ if (iconified) {
+ qreal sx = iconGeometry.width() / boundingRect().width();
+ qreal sy = iconGeometry.height() / boundingRect().height();
+ anim->translateScale(qreal(1.0), qreal(1.0), sx, sy,
+ iconGeometry.topLeft());
+ startTransition();
+ }
+}
+
// TODO: have an option of disabling the animation
void MCompositeWindow::restore(const QRectF &icongeometry, bool defer)
{
+ // Custom restore handler
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ QList<MCompositeManagerExtension*> evlist = p->d->m_extensions.values(MapNotify);
+ for (int i = 0; i < evlist.size(); ++i) {
+ if (evlist[i]->windowRestored(this, defer)) {
+ iconified = false;
+ return;
+ }
+ }
+
if (icongeometry.isEmpty())
this->iconGeometry = fadeRect;
else
@@ -258,59 +329,96 @@
anim->translateScale(qreal(1.0), qreal(1.0), sx, sy, origPosition, true);
iconified = false;
// do this to avoid stacking code disturbing Z values
- if (!is_transitioning) {
- ++window_transitioning;
- is_transitioning = true;
- }
+ beginAnimation();
}
-void MCompositeWindow::showWindow()
+bool MCompositeWindow::showWindow()
{
// defer putting this window in the _NET_CLIENT_LIST
// only after animation is done to prevent the switcher from rendering it
- if (!isAppWindow())
- return;
+ if (!isAppWindow() || !pc || !pc->is_valid
+ // isAppWindow() returns true for system dialogs
+ || pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DIALOG))
+ return false;
findBehindWindow();
- if (!is_transitioning) {
- ++window_transitioning;
- is_transitioning = true;
- }
- if (newly_mapped)
- QTimer::singleShot(700, this, SLOT(q_fadeIn()));
- else
+ beginAnimation();
+ if (newly_mapped) {
+ // NB#180628 - some stupid apps are listening for visibilitynotifies.
+ // Well, all of the toolkit anyways
+ setWindowObscured(false);
+ // waiting for two damage events seems to work for Meegotouch apps
+ // at least, for the rest, there is a timeout
+ waiting_for_damage = 2;
+ damage_timer->start(500);
+ } else
q_fadeIn();
+ return true;
+}
+
+void MCompositeWindow::damageTimeout()
+{
+ damageReceived(true);
+}
+
+void MCompositeWindow::damageReceived(bool timeout)
+{
+ if (timeout || (waiting_for_damage > 0 && !--waiting_for_damage)) {
+ damage_timer->stop();
+ waiting_for_damage = 0;
+ q_fadeIn();
+ }
}
void MCompositeWindow::q_fadeIn()
{
- if (is_transitioning) {
- --window_transitioning;
- is_transitioning = false;
- }
+ endAnimation();
+
newly_mapped = false;
setVisible(true);
setOpacity(0.0);
updateWindowPixmap();
origPosition = pos();
+ newly_mapped = true;
+
+ // Custom fade-in handler
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ QList<MCompositeManagerExtension*> evlist = p->d->m_extensions.values(MapNotify);
+ for (int i = 0; i < evlist.size(); ++i) {
+ if (evlist[i]->windowShown(this))
+ return;
+ }
+
setPos(fadeRect.topLeft());
restore(fadeRect, false);
- newly_mapped = true;
}
-void MCompositeWindow::closeWindow()
+void MCompositeWindow::closeWindowRequest()
{
- if (!isAppWindow() || propertyCache()->windowState() == IconicState) {
- setVisible(false);
- emit windowClosed(this);
+ if (!pc || !pc->is_valid || (!isMapped() && !pc->beingMapped()))
return;
- }
- if (window_status == MCompositeWindow::Hung) {
- hide();
- emit windowClosed(this);
+ if (!windowPixmap() && !pc->isInputOnly()) {
+ // get a Pixmap for the possible unmap animation
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ if (!p->isCompositing())
+ p->d->enableCompositing(true);
+ updateWindowPixmap();
+ }
+ emit closeWindowRequest(this);
+}
+
+void MCompositeWindow::closeWindowAnimation()
+{
+ if (!pc || !pc->is_valid || window_status == Closing
+ || pc->isInputOnly() || pc->isOverrideRedirect()
+ || !windowPixmap() || !isAppWindow()
+ // isAppWindow() returns true for system dialogs
+ || pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DIALOG)
+ || propertyCache()->windowState() == IconicState
+ || window_status == MCompositeWindow::Hung) {
return;
}
- window_status = MCompositeWindow::Closing;
+ window_status = Closing; // animating, do not disturb
MCompositeManager *p = (MCompositeManager *) qApp;
bool defer = false;
@@ -320,16 +428,27 @@
defer = true;
}
- updateWindowPixmap();
origPosition = pos();
+
+ // Custom close window animation handler
+ QList<MCompositeManagerExtension*> evlist = p->d->m_extensions.values(MapNotify);
+ for (int i = 0; i < evlist.size(); ++i) {
+ if (evlist[i]->windowClosed(this)) {
+ window_status = Normal; // can't guarantee that Closing is cleared
+ return;
+ }
+ }
iconify(fadeRect, defer);
}
-void MCompositeWindow::deleteLater()
+bool MCompositeWindow::event(QEvent *e)
{
- destroyed = true;
- if (!is_transitioning)
- QObject::deleteLater();
+ if (e->type() == QEvent::DeferredDelete && is_transitioning) {
+ // Can't delete the object yet, try again in the next iteration.
+ deleteLater();
+ return true;
+ } else
+ return QObject::event(e);
}
void MCompositeWindow::prettyDestroy()
@@ -341,31 +460,30 @@
void MCompositeWindow::finalizeState()
{
- if (is_transitioning) {
- --window_transitioning;
- is_transitioning = false;
- }
- if (pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DESKTOP))
+ endAnimation();
+
+ // as far as this window is concerned, it's OK to direct render
+ window_status = Normal;
+ if (pc && pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_DESKTOP))
emit desktopActivated(this);
// iconification status
if (iconified) {
+ iconified = false;
iconified_final = true;
hide();
iconify_state = TransitionIconifyState;
emit itemIconified(this);
- if (isClosing()) {
- emit windowClosed(this);
- return;
- }
} else {
iconify_state = NoIconifyState;
iconified_final = false;
show();
- QTimer::singleShot(200, this, SLOT(q_itemRestored()));
+ // no delay: window does not need to be repainted when restoring
+ // from the switcher (even then the animation should take long enough
+ // to allow it)
+ q_itemRestored();
}
- window_status = Normal;
-
+
// item lifetime
if (destroyed)
deleteLater();
@@ -380,7 +498,7 @@
{
// when animating, Z-value is set again after finishing the animation
// (setting it later in finalizeState() caused flickering)
- if (!anim->isActive() && !anim->pendingAnimation())
+ if (anim && !anim->isActive() && !anim->pendingAnimation())
setZValue(zvalue);
}
@@ -400,51 +518,13 @@
scaled = s;
}
-void MCompositeWindow::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
-{
- if (thumb_mode) {
- zval = zValue();
- setZValue(scene()->items().count() + 1);
- setZValue(100);
- anim->translateScale(scalefrom, scalefrom, scaleto, scaleto, pos());
-
- }
- return QGraphicsItem::hoverEnterEvent(e);
-}
-
-void MCompositeWindow::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
-{
- if (thumb_mode) {
- setZValue(zval);
- anim->translateScale(scalefrom, scalefrom, scaleto, scaleto, pos(), true);
- }
- return QGraphicsItem::hoverLeaveEvent(e);
-}
-
-void MCompositeWindow::mouseReleaseEvent(QGraphicsSceneMouseEvent *m)
-{
- anim->restore();
- setThumbMode(false);
- setScaled(false);
- setZValue(100);
-
- emit acceptingInput();
- windowRaised();
- QGraphicsItem::mouseReleaseEvent(m);
-}
-
-void MCompositeWindow::manipulationEnabled(bool isEnabled)
-{
- setFlag(QGraphicsItem::ItemIsMovable, isEnabled);
- setFlag(QGraphicsItem::ItemIsSelectable, isEnabled);
-}
-
void MCompositeWindow::setVisible(bool visible)
{
- if (pc->isInputOnly() || (visible && newly_mapped && isAppWindow()) ||
- (!visible && is_transitioning))
+ if ((pc && pc->isInputOnly())
+ || (visible && newly_mapped && isAppWindow())
+ || (!visible && is_transitioning))
return;
-
+
// Set the iconification status as well
iconified_final = !visible;
if (visible != window_visible)
@@ -454,6 +534,10 @@
QGraphicsItem::setVisible(visible);
MCompositeManager *p = (MCompositeManager *) qApp;
p->d->setWindowDebugProperties(window());
+
+ QGraphicsScene* sc = scene();
+ if (sc && !visible && sc->items().count() == 1)
+ clearTexture();
}
void MCompositeWindow::startPing()
@@ -483,7 +567,7 @@
{
received_ping_timestamp = serverTimeStamp;
- if (window_status != Minimizing || window_status != Closing)
+ if (window_status != Minimizing && window_status != Closing)
window_status = Normal;
if (blurred())
setBlurred(false);
@@ -492,11 +576,10 @@
void MCompositeWindow::pingTimeout()
{
if (pinging_enabled && received_ping_timestamp < sent_ping_timestamp
- && window_status != Hung) {
+ && window_status != Hung
+ && window_status != Minimizing && window_status != Closing) {
setBlurred(true);
-
- if (window_status != Minimizing || window_status != Closing)
- window_status = Hung;
+ window_status = Hung;
emit windowHung(this);
}
if (pinging_enabled)
@@ -544,25 +627,28 @@
return p->d->windows.value(window, 0);
}
-void MCompositeWindow::windowTransitioning()
+void MCompositeWindow::beginAnimation()
{
+ if (!isMapped() && window_status != Closing)
+ return;
+
if (!is_transitioning) {
- ++window_transitioning;
+ ++window_transitioning;
is_transitioning = true;
}
}
-bool MCompositeWindow::hasTransitioningWindow()
-{
- return window_transitioning > 0;
+void MCompositeWindow::endAnimation()
+{
+ if (is_transitioning) {
+ --window_transitioning;
+ is_transitioning = false;
+ }
}
-void MCompositeWindow::q_delayShow()
+bool MCompositeWindow::hasTransitioningWindow()
{
- MCompositeWindow::setVisible(true);
- updateWindowPixmap();
- MCompositeManager *p = (MCompositeManager *) qApp;
- p->d->updateWinList();
+ return window_transitioning > 0;
}
QVariant MCompositeWindow::itemChange(GraphicsItemChange change, const QVariant &value)
@@ -574,8 +660,13 @@
p->d->setWindowDebugProperties(window());
}
+ /* disabled to avoid glSwapBuffers call without painting any item (Qt bug)
+ * see NB#189519
if (zvalChanged || change == ItemVisibleHasChanged || change == ItemParentHasChanged)
+ {
p->d->glwidget->update();
+ }
+ */
return QGraphicsItem::itemChange(change, value);
}
@@ -615,7 +706,7 @@
if (!include_transients && p->d->getLastVisibleParent(pc))
return false;
- if (!pc->isOverrideRedirect() &&
+ if (pc && !pc->isOverrideRedirect() &&
(pc->windowTypeAtom() == ATOM(_NET_WM_WINDOW_TYPE_NORMAL) ||
pc->windowTypeAtom() == ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE) ||
/* non-modal, non-transient dialogs behave like applications */
@@ -640,7 +731,10 @@
Window MCompositeWindow::lastVisibleParent() const
{
MCompositeManager *p = (MCompositeManager *) qApp;
- return p->d->getLastVisibleParent(propertyCache());
+ if (pc && pc->is_valid)
+ return p->d->getLastVisibleParent(pc);
+ else
+ return None;
}
int MCompositeWindow::indexInStack() const
@@ -651,10 +745,21 @@
void MCompositeWindow::setIsMapped(bool mapped)
{
- pc->setIsMapped(mapped);
+ if (mapped)
+ window_status = Normal; // make sure Closing -> Normal when remapped
+ if (pc) pc->setIsMapped(mapped);
}
bool MCompositeWindow::isMapped() const
{
- return pc->isMapped();
+ return pc ? pc->isMapped() : false;
+}
+
+MCompositeWindowGroup* MCompositeWindow::group() const
+{
+#ifdef GLES2_VERSION
+ return renderer()->current_window_group;
+#else
+ return 0;
+#endif
}
--- src/mcompositewindow.h
+++ src/mcompositewindow.h
@@ -24,10 +24,11 @@
#include <QtOpenGL>
#include <QPointer>
#include <X11/Xutil.h>
-#include "mcompatoms_p.h"
#include "mwindowpropertycache.h"
class MCompWindowAnimator;
+class MTexturePixmapPrivate;
+class MCompositeWindowGroup;
/*!
* This is the base class for composited window items. It provided general
@@ -40,8 +41,9 @@
Q_OBJECT
#if QT_VERSION >= 0x040600
Q_INTERFACES(QGraphicsItem)
+ Q_PROPERTY(QPointF pos READ pos WRITE setPos)
+ Q_PROPERTY(qreal scale READ scale WRITE setScale)
#endif
-
public:
enum WindowStatus {
@@ -72,10 +74,8 @@
Qt::HANDLE window() const { return win_id; }
- /*!
- * Overriden QObject::deleteLater()
- */
- void deleteLater();
+ // Reimplemented to defer deleteLater()s until transitions are over.
+ virtual bool event(QEvent *);
/*!
* Saves the global state of this item. Possibly transformations and
@@ -165,10 +165,26 @@
void setIconified(bool iconified);
/*!
+ * Set scale, opacity etc. to normal values.
+ */
+ void setUntransformed();
+
+ /*!
+ * True if this window is waiting for damage event before it can animate.
+ */
+ bool waitingForDamage() const { return waiting_for_damage > 0; }
+
+ /*!
* Returns how this window was iconified.
*/
IconifyState iconifyState() const;
+
+ /*!
+ * Sets how this window was iconified.
+ */
+ void setIconifyState(IconifyState state);
+
/*!
* Returns true if this window needs a decoration
*/
@@ -196,7 +212,7 @@
* Overrides QGraphicsItem::update() so we have complete control of item
* updates.
*/
- void update();
+ static void update();
bool blurred();
@@ -217,22 +233,18 @@
static MCompositeWindow *compositeWindow(Qt::HANDLE window);
- virtual void windowRaised() = 0;
-
/*!
* Ensures that the corresponding texture reflects the contents of the
* associated pixmap and schedules a redraw of this item.
*/
- virtual void updateWindowPixmap(XRectangle *rects = 0, int num = 0) = 0;
+ virtual void updateWindowPixmap(XRectangle *rects = 0, int num = 0,
+ Time when = 0) = 0;
/*!
- * Creates the pixmap id and saves the offscreen buffer that represents
- * this window
- *
- * \param renew Set to true if the window was just mapped or resized. This
- * will update the offscreen backing store.
+ * Recreates the pixmap id and saves the offscreen buffer that represents
+ * this window. This will update the offscreen backing store.
*/
- virtual void saveBackingStore(bool renew = false) = 0;
+ virtual void saveBackingStore() = 0;
/*!
Clears the texture that is associated with the offscreen pixmap
@@ -240,6 +252,11 @@
virtual void clearTexture() = 0;
/*!
+ Returns pixmap for the window.
+ */
+ virtual Pixmap windowPixmap() const = 0;
+
+ /*!
Returns true if the window corresponding to the offscreen pixmap
is rendering directly to the framebuffer, otherwise return false.
*/
@@ -285,22 +302,49 @@
* Returns whatever window is directly behind this window. 0 if there is none.
*/
MCompositeWindow* behind() const { return behind_window; }
+
+ /*!
+ * Returns a pointer to this window's group if it belongs to a group and 0
+ * if 0 if not a member
+ */
+ MCompositeWindowGroup* group() const;
/*! Disabled alpha-blending for a dim-effect instead */
void setDimmedEffect(bool dimmed) { dimmed_effect = dimmed; }
bool dimmedEffect() const { return dimmed_effect; }
-
+
public slots:
+ void updateIconGeometry();
void startTransition();
- void manipulationEnabled(bool isEnabled);
- void setUnBlurred();
void setBlurred(bool);
/* Operations with transition animations*/
- void closeWindow();
- void showWindow();
+ // set to Closing state and send delete/kill
+ void closeWindowRequest();
+ // start unmap animation
+ void closeWindowAnimation();
+ bool showWindow();
+
+ /*!
+ * This slot is called whenever a start of window animation occurs. This
+ * is an atomic operation. Ensure that endTransition() is invoked when
+ * the animation is finished.
+ */
+ void beginAnimation();
+
+ /*!
+ * This slot is called whenever the window has finished animating its
+ * effects
+ */
+ void endAnimation();
+
+ /*!
+ * Called on first showing of a window when first damage event is received
+ * or on timeout.
+ */
+ void damageReceived(bool timeout);
private slots:
@@ -310,9 +354,8 @@
void finalizeState();
void pingTimeout();
+ void damageTimeout();
void pingWindow();
- void windowTransitioning();
- void q_delayShow();
void q_itemRestored();
void q_fadeIn();
@@ -322,7 +365,6 @@
*/
void windowHung(MCompositeWindow *window);
- void acceptingInput();
void visualized(bool);
/*! Emitted when this window gets restored from an iconified state */
@@ -331,18 +373,18 @@
void itemIconified(MCompositeWindow *window);
/*! Emitted when desktop is raised */
void desktopActivated(MCompositeWindow *window);
- /*! Emitted when this window is closed */
- void windowClosed(MCompositeWindow *window);
+ /*! Emitted when the user wants to close this window */
+ void closeWindowRequest(MCompositeWindow *window);
protected:
- virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *);
- virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *);
- virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
virtual QPainterPath shape() const;
-
+
private:
+ /* re-implemented in GL/GLES2 backends for internal interaction
+ between shader effects */
+ virtual MTexturePixmapPrivate* renderer() const = 0;
void findBehindWindow();
QPointer<MWindowPropertyCache> pc;
@@ -363,13 +405,13 @@
WindowStatus window_status;
bool need_decor;
bool window_visible;
- bool window_obscured;
+ short window_obscured;
bool is_valid;
bool newly_mapped;
- bool is_closing;
bool is_transitioning;
bool pinging_enabled;
bool dimmed_effect;
+ char waiting_for_damage;
static int window_transitioning;
@@ -379,9 +421,11 @@
// Main ping timer
QTimer *t_ping;
+ QTimer *damage_timer;
Qt::HANDLE win_id;
friend class MTexturePixmapPrivate;
+ friend class MCompositeWindowShaderEffect;
};
#endif
--- src/mcompositewindowgroup.cpp
+++ src/mcompositewindowgroup.cpp
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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.
+**
+****************************************************************************/
+/*!
+ \class MCompositeWindowGroup
+ \brief MCompositeWindowGroup allows a collection of windows to be rendered
+ as a single texture.
+
+ This class is useful for rendering a list of windows that needs to
+ be treated as one item with transformations applied only to a single texture.
+ Unlike QGraphicsItemGroup where each item is rendered separately for each
+ frame, MCompositeWindowGroup can pre-render all items to an off-screen buffer
+ before a frame starts and can render the resulting texture afterwards as a
+ single quad which reduces GPU load and helps performance especially in panning
+ and scaling transformations.
+
+ Use this class to render a main window with its transient windows. Another
+ use case is for a window that needs to have similar transformations to a
+ main window. The addChildWindow() function adds windows that need to
+ animate synchronously with the main window. The removeChildWindow() function
+ removes a window from the group.
+
+ Implementation is hw-dependent. It relies on framebuffer objects on GLES2
+ and the GL_EXT_framebuffer_object extension on the desktop.
+*/
+
+#include <QtOpenGL>
+#include <QList>
+#include <mcompositewindowgroup.h>
+
+#include <mtexturepixmapitem.h>
+#include <mcompositemanager.h>
+
+#ifdef GLES2_VERSION
+#define FORMAT GL_RGBA
+#define DEPTH GL_DEPTH_COMPONENT16
+#else
+#define FORMAT GL_RGBA8
+#define DEPTH GL_DEPTH_COMPONENT
+#endif
+
+class MCompositeWindowGroupPrivate
+{
+public:
+ MCompositeWindowGroupPrivate(MTexturePixmapItem* mainWindow)
+ :main_window(mainWindow),
+ texture(0),
+ fbo(0),
+ depth_buffer(0),
+ valid(false),
+ renderer(new MTexturePixmapPrivate(0, mainWindow))
+ {
+ }
+ MTexturePixmapItem* main_window;
+ GLuint texture;
+ GLuint fbo;
+ GLuint depth_buffer;
+
+ bool valid;
+ QList<MTexturePixmapItem*> item_list;
+ MTexturePixmapPrivate* renderer;
+};
+
+/*!
+ Creates a window group object. Specify the main window
+ with \a mainWindow
+ */
+MCompositeWindowGroup::MCompositeWindowGroup(MTexturePixmapItem* mainWindow)
+ :MCompositeWindow(0, MWindowDummyPropertyCache::get()),
+ d_ptr(new MCompositeWindowGroupPrivate(mainWindow))
+{
+ MCompositeManager *p = (MCompositeManager *) qApp;
+ p->scene()->addItem(this);
+
+ mainWindow->d->current_window_group = this;
+ connect(mainWindow, SIGNAL(destroyed()), SLOT(deleteLater()));
+ init();
+ updateWindowPixmap();
+ setZValue(mainWindow->zValue());
+ stackBefore(mainWindow);
+}
+
+/*!
+ Destroys this window group and frees resources. All windows that are within
+ this group revert back to directly rendering its own texture.
+ */
+MCompositeWindowGroup::~MCompositeWindowGroup()
+{
+ Q_D(MCompositeWindowGroup);
+
+ if (!QGLContext::currentContext()) {
+ qWarning("MCompositeWindowGroup::%s(): no current GL context",
+ __func__);
+ return;
+ }
+
+ GLuint texture = d->texture;
+ glDeleteTextures(1, &texture);
+ GLuint depth_buffer = d->depth_buffer;
+ glDeleteRenderbuffers(1, &depth_buffer);
+ GLuint fbo = d->fbo;
+ glDeleteFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ update();
+}
+
+void MCompositeWindowGroup::init()
+{
+ Q_D(MCompositeWindowGroup);
+
+ if (!QGLContext::currentContext()) {
+ qWarning("MCompositeWindowGroup::%s(): no current GL context",
+ __func__);
+ d->valid = false;
+ return;
+ }
+ d->renderer->current_window_group = this;
+
+ glGenFramebuffers(1, &d->fbo);
+ glGenRenderbuffers(1, &d->depth_buffer);
+ glBindFramebuffer(GL_RENDERBUFFER, d->fbo);
+
+ glGenTextures(1, &d->texture);
+ glBindTexture(GL_TEXTURE_2D, d->texture);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindRenderbuffer(GL_RENDERBUFFER, d->depth_buffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, DEPTH,
+ d->main_window->boundingRect().width(),
+ d->main_window->boundingRect().height());
+
+ glBindFramebuffer(GL_FRAMEBUFFER, d->fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, d->texture, 0);
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, d->depth_buffer);
+
+ glBindTexture(GL_TEXTURE_2D, d->texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, FORMAT,
+ d->main_window->boundingRect().width(),
+ d->main_window->boundingRect().height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ GLenum ret = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (ret == GL_FRAMEBUFFER_COMPLETE)
+ d->valid = true;
+ else
+ qWarning("MCompositeWindowGroup::%s(): incomplete FBO attachment 0x%x",
+ __func__, ret);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+static bool behindCompare(MTexturePixmapItem* a, MTexturePixmapItem* b)
+{
+ return a->indexInStack() < b->indexInStack();
+}
+
+/*!
+ Adds the given \a window to this group. The window's transformations will
+ remain unmodified
+ */
+void MCompositeWindowGroup::addChildWindow(MTexturePixmapItem* window)
+{
+ Q_D(MCompositeWindowGroup);
+ window->d->current_window_group = this;
+ connect(window, SIGNAL(destroyed()), SLOT(q_removeWindow()));
+ d->item_list.append(window);
+
+ // ensure group windows are already stacked in proper order in advance
+ // for back to front rendering. Could use depth buffer attachment at some
+ // point so this might be unecessary
+ qSort(d->item_list.begin(), d->item_list.end(), behindCompare);
+ updateWindowPixmap();
+}
+
+/*!
+ Removes the given \a window to from group.
+ */
+void MCompositeWindowGroup::removeChildWindow(MTexturePixmapItem* window)
+{
+ Q_D(MCompositeWindowGroup);
+ window->d->current_window_group = 0;
+ d->item_list.removeAll(window);
+}
+
+void MCompositeWindowGroup::q_removeWindow()
+{
+ Q_D(MCompositeWindowGroup);
+ MTexturePixmapItem* w = qobject_cast<MTexturePixmapItem*>(sender());
+ if (w)
+ d->item_list.removeAll(w);
+}
+
+void MCompositeWindowGroup::saveBackingStore() {}
+
+void MCompositeWindowGroup::resize(int , int) {}
+
+void MCompositeWindowGroup::clearTexture() {}
+
+bool MCompositeWindowGroup::isDirectRendered() const { return false; }
+
+QRectF MCompositeWindowGroup::boundingRect() const
+{
+ Q_D(const MCompositeWindowGroup);
+ return d->main_window->boundingRect();
+}
+
+void MCompositeWindowGroup::paint(QPainter* painter,
+ const QStyleOptionGraphicsItem* options,
+ QWidget* widget)
+{
+ Q_D(MCompositeWindowGroup);
+ Q_UNUSED(options)
+ Q_UNUSED(widget)
+
+#if QT_VERSION < 0x040600
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL)
+ return;
+#else
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL2) {
+ return;
+ }
+#endif
+
+ glBindTexture(GL_TEXTURE_2D, d->texture);
+ if (d->main_window->propertyCache()->hasAlpha() ||
+ (opacity() < 1.0f && !dimmedEffect()) ) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ d->renderer->drawTexture(painter->combinedTransform(), boundingRect(),
+ opacity());
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glDisable(GL_BLEND);
+}
+
+void MCompositeWindowGroup::windowRaised()
+{
+}
+
+void MCompositeWindowGroup::updateWindowPixmap(XRectangle *rects, int num,
+ Time t)
+{
+ Q_UNUSED(rects)
+ Q_UNUSED(num)
+ Q_UNUSED(t)
+ Q_D(MCompositeWindowGroup);
+
+ if (!d->valid) {
+ qDebug() << "invalid fbo";
+ return;
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, d->fbo);
+ GLenum ret = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (ret == GL_FRAMEBUFFER_COMPLETE)
+ d->valid = true;
+ else {
+ qWarning("MCompositeWindowGroup::%s(): incomplete FBO attachment 0x%x",
+ __func__, ret);
+ d->valid = false;
+ }
+ d->main_window->d->inverted_texture = false;
+ d->main_window->renderTexture(d->main_window->sceneTransform());
+ d->main_window->d->inverted_texture = true;
+ for (int i = 0; i < d->item_list.size(); ++i) {
+ MTexturePixmapItem* item = d->item_list[i];
+ item->d->inverted_texture = false;
+ item->renderTexture(item->sceneTransform());
+ item->d->inverted_texture = true;
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+// internal re-implementation from MCompositeWindow
+MTexturePixmapPrivate* MCompositeWindowGroup::renderer() const
+{
+ Q_D(const MCompositeWindowGroup);
+ return d->renderer;
+}
+
+/*!
+ \return Returns the texture used by the off-screen buffer
+*/
+GLuint MCompositeWindowGroup::texture()
+{
+ Q_D(MCompositeWindowGroup);
+ return d->texture;
+}
+
+
--- src/mcompositewindowgroup.h
+++ src/mcompositewindowgroup.h
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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.
+**
+****************************************************************************/
+
+#ifndef MCOMPOSITEWINDOWGROUP_H
+#define MCOMPOSITEWINDOWGROUP_H
+
+#include <mcompositewindow.h>
+
+class MTexturePixmapItem;
+class MCompositeWindowGroupPrivate;
+
+class MCompositeWindowGroup: public MCompositeWindow
+{
+ Q_OBJECT
+ public:
+ enum { Type = UserType + 2 };
+
+ MCompositeWindowGroup(MTexturePixmapItem* mainWindow);
+ virtual ~MCompositeWindowGroup();
+
+ void addChildWindow(MTexturePixmapItem* window);
+ void removeChildWindow(MTexturePixmapItem* window);
+ GLuint texture();
+
+ //! \reimp
+ virtual void windowRaised();
+ virtual void updateWindowPixmap(XRectangle *rects = 0, int num = 0,
+ Time = 0);
+ virtual void saveBackingStore();
+ virtual void resize(int , int);
+ virtual void clearTexture();
+ virtual bool isDirectRendered() const;
+ virtual QRectF boundingRect() const;
+ virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
+ virtual Pixmap windowPixmap() const { return 0; }
+ //! \reimp_end
+
+ virtual int type () const { return Type; }
+
+ private slots:
+ void q_removeWindow();
+
+ private:
+ Q_DECLARE_PRIVATE(MCompositeWindowGroup)
+ void init();
+ virtual MTexturePixmapPrivate* renderer() const;
+
+ QScopedPointer<MCompositeWindowGroupPrivate> d_ptr;
+};
+
+#endif // MCOMPOSITEWINDOWGROUP_H
--- src/mcompositewindowshadereffect.cpp
+++ src/mcompositewindowshadereffect.cpp
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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.
+**
+****************************************************************************/
+
+/*!
+ \class MCompositeWindowShaderEffect
+ \brief MCompositeWindowShaderEffect is the base class for shader effects on windows
+
+ Shader effects can change the appearance of composited windows by hooking
+ into the rendering pipeline of the compositor. It allows manipulation
+ of how a composited window is rendered by using custom GLSL fragment shaders
+ and direct access to the texture rendering function.
+ An effect can be disabled by calling setEnabled(false). If effects are
+ disabled, the window texture is rendered directly.
+
+ To add special effects to composited windows, subclass MCompositeWindowShaderEffect
+ and reimplement the pure virtual function drawTexture()
+*/
+
+#include "mtexturepixmapitem.h"
+#include "mcompositewindowshadereffect.h"
+#include "mtexturepixmapitem_p.h"
+#include "mcompositewindowgroup.h"
+#include <QByteArray>
+
+static const char default_frag[] = "\
+ lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords) { \
+ return texture2D(imageTexture, textureCoords); \
+ }\n";
+
+MCompositeWindowShaderEffectPrivate::MCompositeWindowShaderEffectPrivate(MCompositeWindowShaderEffect* e)
+ :effect(e),
+ priv_render(0),
+ active_fragment(0)
+{
+}
+
+void MCompositeWindowShaderEffectPrivate::drawTexture(MTexturePixmapPrivate* render, const QTransform &transform, const QRectF &drawRect, qreal opacity)
+{
+ priv_render = render;
+ effect->drawTexture(transform, drawRect, opacity);
+ enabled = false;
+}
+
+/*!
+ * Creates a window effect object
+ */
+MCompositeWindowShaderEffect::MCompositeWindowShaderEffect(QObject* parent)
+ :QObject(parent),
+ d(new MCompositeWindowShaderEffectPrivate(this))
+{
+ // install default pixel shader
+ QByteArray code = default_frag;
+ d->active_fragment = installShaderFragment(code);
+}
+
+/*!
+ Destroys the graphics effect and deletes GLSL fragment programs from
+ the compositor's rendering engine if any are installed from this effect
+*/
+MCompositeWindowShaderEffect::~MCompositeWindowShaderEffect()
+{
+}
+
+/*!
+ Adds and installs the source code for a pixel shader fragment to \a code.
+
+ The \a code must define a GLSL function with the signature
+ \c{lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords)}
+ that returns the source pixel value to use in the paint engine's
+ shader program. The following is the default pixel shader fragment,
+ which draws a texture with no effect applied:
+
+ \code
+ lowp vec4 customShader(lowp sampler2D imageTexture, highp vec2 textureCoords)
+ {
+ return texture2D(imageTexture, textureCoords);
+ }
+ \endcode
+
+ \return The id of the fragment shader. If the fragment shader is invalid,
+ return 0.
+
+ \sa setUniforms()
+*/
+GLuint MCompositeWindowShaderEffect::installShaderFragment(const QByteArray& code)
+{
+ GLuint id = MTexturePixmapPrivate::installPixelShader(code);
+ d->pixfrag_ids.push_back(id);
+ return id;
+}
+
+ /*!
+ \return Texture id of the currently rendered window
+ */
+GLuint MCompositeWindowShaderEffect::texture() const
+{
+ // TODO: This assumes we have always have hadware TFP support
+ if (d->priv_render) {
+#ifdef GLES2_VERSION
+ if (!d->priv_render->current_window_group)
+ return d->priv_render->textureId;
+ else
+ return d->priv_render->current_window_group->texture();
+#else
+ return d->priv_render->textureId;
+#endif
+ }
+ return 0;
+}
+
+const QVector<GLuint>& MCompositeWindowShaderEffect::fragmentIds() const
+{
+ return d->pixfrag_ids;
+}
+
+/*!
+ Sets the rendering pipeline to use shader fragment specified by \a id
+*/
+void MCompositeWindowShaderEffect::setActiveShaderFragment(GLuint id)
+{
+ d->active_fragment = id;
+}
+
+/*!
+ \return The id of the currently active shader fragment
+*/
+GLuint MCompositeWindowShaderEffect::activeShaderFragment() const
+{
+ return d->active_fragment;
+}
+
+/*!
+ \fn void MCompositeWindowShaderEffect::enabledChanged( bool enabled);
+ Emmitted if this effect gets disabled or enabled
+*/
+
+/*!
+ \fn virtual void MCompositeWindowShaderEffect::drawTexture(const QTransform &transform, const QRectF &drawRect, qreal opacity) = 0;
+
+ This pure virtual function is called whenever the windows texture is
+ rendered. Reimplement it to completely customize how the texture of this
+ window is rendered using the given transformation
+ matrix \a transform, texture geometry \a drawRect, and \a opacity.
+*/
+
+/*!
+ Draws currently bound source window texture. Specify a transformation matrix to
+ \a transform and texture geometry to \a drawRect. Opacity can be set
+ by specifying \a opacity (0.0 - 1.0).
+ This function should only be called inside drawTexture()
+*/
+void MCompositeWindowShaderEffect::drawSource(const QTransform &transform,
+ const QRectF &drawRect,
+ qreal opacity)
+{
+ if (d->priv_render)
+ d->priv_render->q_drawTexture(transform, drawRect, opacity);
+}
+
+/*!
+ Install this effect on a composite window object \a window. Note that
+ we override QGraphicsItem::setGraphicsEffect() because
+ we have a completely different painting engine.
+ If a window has a previous effect, it will be overriden by the new one
+*/
+void MCompositeWindowShaderEffect::installEffect(MCompositeWindow* window)
+{
+ if (!window->isValid() && (window->type() != MCompositeWindowGroup::Type))
+ return;
+
+ // only happens with GL. sorry n800 guys :p
+#ifdef QT_OPENGL_LIB
+ window->renderer()->installEffect(this);
+#endif
+}
+
+/*!
+ Remove this effect from a composite window object \a window. After removing
+ the effect the window will use default shaders.
+*/
+void MCompositeWindowShaderEffect::removeEffect(MCompositeWindow* window)
+{
+ if (!window->isValid() && (window->type() != MCompositeWindowGroup::Type))
+ return;
+
+#ifdef QT_OPENGL_LIB
+ window->renderer()->installEffect(0);
+#endif
+}
+
+/*!
+ \return Whether this effect is enabled or not
+*/
+bool MCompositeWindowShaderEffect::enabled() const
+{
+ return d->enabled;
+}
+
+/*!
+ Enable or disable this effect
+*/
+void MCompositeWindowShaderEffect::setEnabled(bool enabled)
+{
+ d->enabled = enabled;
+ emit enabledChanged(enabled);
+}
+
+/*!
+ Set the uniform values on the currently active shader \a program.
+ Default implementation does nothing. Reimplement this function to
+ set values if you specified a custom pixel shader in your effects.
+*/
+void MCompositeWindowShaderEffect::setUniforms(QGLShaderProgram* program)
+{
+ Q_UNUSED(program);
+}
+
--- src/mcompositewindowshadereffect.h
+++ src/mcompositewindowshadereffect.h
+/***************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (directui at nokia.com)
+**
+** This file is part of mcompositor.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at directui at 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.
+**
+****************************************************************************/
+
+#ifndef MCOMPOSITEWINDOWSHADEREFFECT_H
+#define MCOMPOSITEWINDOWSHADEREFFECT_H
+
+#include <QObject>
+#include <QVector>
+#include <QGLShaderProgram>
+
+class QTransform;
+class QRectF;
+class MCompositeWindowShaderEffect;
+class MTexturePixmapPrivate;
+class MCompositeWindow;
+class MCompositeWindowShaderEffectPrivate;
+
+class MCompositeWindowShaderEffect: public QObject
+{
+ Q_OBJECT
+ public:
+ MCompositeWindowShaderEffect(QObject* parent = 0);
+ virtual ~MCompositeWindowShaderEffect();
+
+ GLuint installShaderFragment(const QByteArray& code);
+ GLuint texture() const;
+ void setActiveShaderFragment(GLuint id);
+ GLuint activeShaderFragment() const;
+
+ void installEffect(MCompositeWindow* window);
+ void removeEffect(MCompositeWindow* window);
+ bool enabled() const;
+
+ public slots:
+ void setEnabled(bool enabled);
+
+ signals:
+ void enabledChanged( bool enabled);
+
+ protected:
+ void drawSource(const QTransform &transform,
+ const QRectF &drawRect, qreal opacity);
+ virtual void drawTexture(const QTransform &transform,
+ const QRectF &drawRect, qreal opacity) = 0;
+ virtual void setUniforms(QGLShaderProgram* program);
+
+ private:
+ /* \cond */
+ const QVector<GLuint>& fragmentIds() const;
+
+ MCompositeWindowShaderEffectPrivate* d;
+ friend class MTexturePixmapPrivate;
+ friend class MCompositeWindowShaderEffectPrivate;
+ /* \endcond */
+
+};
+
+/* \cond
+ * Internal class. Do not use! Not part of public API
+ */
+class MCompositeWindowShaderEffectPrivate
+{
+ public:
+ void drawTexture(MTexturePixmapPrivate* render,
+ const QTransform &transform,
+ const QRectF &drawRect, qreal opacity);
+
+ private:
+ explicit MCompositeWindowShaderEffectPrivate(MCompositeWindowShaderEffect*);
+
+ MCompositeWindowShaderEffect* effect;
+ MTexturePixmapPrivate* priv_render;
+ // QMap<GLuint, QByteArray> pixelfragments;
+ QVector<GLuint> pixfrag_ids;
+ GLuint active_fragment;
+
+ bool enabled;
+
+ friend class MCompositeWindowShaderEffect;
+};
+
+/* \endcond */
+#endif
--- src/mcompwindowanimator.cpp
+++ src/mcompwindowanimator.cpp
@@ -29,13 +29,13 @@
return ((x2 - x1) * step) + x1;
}
-MCompWindowAnimator::MCompWindowAnimator(MCompositeWindow *item)
- : QObject(item),
+MCompWindowAnimator::MCompWindowAnimator(MCompositeWindow *comp_win)
+ : QObject(comp_win),
timer(200),
reversed(false),
deferred_animation(false)
{
- this->item = item;
+ item = comp_win;
timer.setFrameRange(0, 2000);
timer.setUpdateInterval(int(1000.0 / Fps));
@@ -88,9 +88,9 @@
// TODO: move calculation to GPU to imrpove speed
// TODO: Use QPropertyAnimation instead
- ((MCompositeWindow*)item)->setDimmedEffect(false);
+ item->setDimmedEffect(false);
item->setOpacity(!reversed ? opac_norm : opac_rev);
- MCompositeWindow* behind = ((MCompositeWindow*)item)->behind();
+ MCompositeWindow* behind = item->behind();
if (behind) {
behind->setDimmedEffect(true);
behind->setOpacity(!reversed ? opac_rev : opac_norm);
@@ -102,7 +102,12 @@
void MCompWindowAnimator::resetState()
{
- MCompositeWindow* behind = ((MCompositeWindow*)item)->behind();
+ if (!reversed) {
+ item->setPos(anim.posAt(1.0));
+ item->setOpacity(1.0);
+ item->setTransform(matrix);
+ }
+ MCompositeWindow* behind = item->behind();
if (behind) {
behind->setOpacity(1.0);
}
@@ -201,6 +206,12 @@
}
}
+void MCompWindowAnimator::stopAnimation()
+{
+ timer.stop();
+ item->setTransform(matrix);
+}
+
void MCompWindowAnimator::deferAnimation(bool defer)
{
deferred_animation = defer;
--- src/mcompwindowanimator.h
+++ src/mcompwindowanimator.h
@@ -73,6 +73,7 @@
bool isActive();
void startAnimation();
+ void stopAnimation();
void deferAnimation(bool);
//! There is a pending animation to be executed soon
@@ -99,7 +100,7 @@
QTransform matrix;
QTransform local;
bool visibility;
- QGraphicsItem *item;
+ MCompositeWindow *item;
QGraphicsItemAnimation anim;
QTimeLine timer;
int zval;
--- src/mdecoratorframe.cpp
+++ src/mdecoratorframe.cpp
@@ -152,6 +152,7 @@
void MDecoratorFrame::destroyDecorator()
{
+ setDecoratorAvailableRect(QRect());
decorator_item = 0;
decorator_window = 0;
}
--- src/mdecoratorframe.h
+++ src/mdecoratorframe.h
@@ -92,6 +92,7 @@
void setDecoratorItem(MCompositeWindow *window);
MCompositeWindow *decoratorItem() const;
+ const QRect &decoratorRect() const { return decorator_rect; }
public slots:
void setDecoratorAvailableRect(const QRect& r);
--- src/mdevicestate.cpp
+++ src/mdevicestate.cpp
@@ -31,7 +31,7 @@
if (mode == MCE_DISPLAY_OFF_STRING) {
display_off = true;
emit displayStateChange(true);
- } else if (mode == MCE_DISPLAY_ON_STRING) {
+ } else { // "on" or "dimmed"
display_off = false;
emit displayStateChange(false);
}
--- src/mtexturepixmapitem.h
+++ src/mtexturepixmapitem.h
@@ -21,6 +21,7 @@
#define DUITEXTUREPIXMAPITEM_H
#include "mcompositewindow.h"
+#include "mtexturepixmapitem_p.h"
#include <QtOpenGL>
#include <QPixmap>
@@ -40,6 +41,7 @@
*/
class MTexturePixmapItem: public MCompositeWindow
{
+ Q_OBJECT
public:
/*!
* Constructs a MTexturePixmapItem
@@ -49,7 +51,7 @@
* \param parent QGraphicsItem, defaults to 0
*/
MTexturePixmapItem(Window window, MWindowPropertyCache *mpc,
- QGLWidget *glwidget, QGraphicsItem *parent = 0);
+ QGraphicsItem *parent = 0);
/*!
* Destroys the MTexturePixmapItem and frees the allocated
* surface and texture
@@ -60,16 +62,14 @@
* Ensures that the corresponding texture reflects the contents of the
* associated pixmap and schedules a redraw of this item.
*/
- void updateWindowPixmap(XRectangle *rects = 0, int num = 0);
+ void updateWindowPixmap(XRectangle *rects = 0, int num = 0,
+ Time when = 0);
/*!
- * Creates the pixmap id and saves the offscreen buffer that represents
- * this window
- *
- * \param renew Set to true if the window was just mapped or resized. This
- * will update the offscreen backing store.
+ * Recreates the pixmap id and saves the offscreen buffer that represents
+ * this window. This will update the offscreen backing store.
*/
- void saveBackingStore(bool renew = false);
+ void saveBackingStore();
/*!
Clears the texture that is associated with the offscreen pixmap
@@ -90,6 +90,8 @@
void enableDirectFbRendering();
void enableRedirectedRendering();
+ virtual Pixmap windowPixmap() const { return d->windowp; }
+
protected:
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
@@ -97,17 +99,22 @@
QSizeF sizeHint(Qt::SizeHint, const QSizeF &) const;
QRectF boundingRect() const;
- virtual void windowRaised();
private:
+ virtual MTexturePixmapPrivate* renderer() const;
void init();
void initCustomTfp();
void cleanup();
void rebindPixmap();
+ void doTFP();
+ void renderTexture(const QTransform& transform);
MTexturePixmapPrivate *const d;
friend class MTexturePixmapPrivate;
friend class MCompositeManagerPrivate;
+ friend class MCompositeWindowShaderEffect;
+ friend class MCompositeWindowGroupPrivate;
+ friend class MCompositeWindowGroup;
};
#endif // DUIGLXTEXTUREPIXMAPITEM_H
--- src/mtexturepixmapitem_egl.cpp
+++ src/mtexturepixmapitem_egl.cpp
@@ -19,6 +19,7 @@
#include "mtexturepixmapitem.h"
#include "mtexturepixmapitem_p.h"
+#include "mcompositewindowgroup.h"
#include <QPainterPath>
#include <QRect>
@@ -91,14 +92,13 @@
QString exts = QLatin1String(eglQueryString(dpy, EGL_EXTENSIONS));
if ((exts.contains("EGL_KHR_image") &&
exts.contains("EGL_KHR_image_pixmap") &&
- exts.contains("EGL_KHR_gl_texture_2D_image")) || 1) {
+ exts.contains("EGL_KHR_gl_texture_2D_image"))) {
has_tfp = true;
eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR");
eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR");
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress("glEGLImageTargetTexture2DOES");
} else {
- qCritical() << "EGL extensions:" << exts;
- qFatal("no EGL tfp support, aborting\n");
+ qDebug("No EGL tfp support.\n");
}
texman = new EglTextureManager();
}
@@ -133,56 +133,40 @@
d->eglresource = new EglResourceManager();
d->custom_tfp = !d->eglresource->texturePixmapSupport();
- if (d->custom_tfp) {
- initCustomTfp();
- return;
- }
- saveBackingStore();
- d->ctx->makeCurrent();
- d->egl_image = eglCreateImageKHR(d->eglresource->dpy, 0,
- EGL_NATIVE_PIXMAP_KHR,
- (EGLClientBuffer)d->windowp,
- attribs);
- if (d->egl_image == EGL_NO_IMAGE_KHR) {
- qWarning("MTexturePixmapItem::%s(): Cannot create EGL image: 0x%x",
- __func__, eglGetError());
- return;
- }
-
d->textureId = d->eglresource->texman->getTexture();
glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, d->textureId);
- if (d->egl_image != EGL_NO_IMAGE_KHR)
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, d->egl_image);
+ if (d->custom_tfp)
+ d->inverted_texture = false;
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ d->saveBackingStore();
}
MTexturePixmapItem::MTexturePixmapItem(Window window, MWindowPropertyCache *mpc,
- QGLWidget *glwidget,
QGraphicsItem* parent)
: MCompositeWindow(window, mpc, parent),
- d(new MTexturePixmapPrivate(window, glwidget, this))
+ d(new MTexturePixmapPrivate(window, this))
{
- if (!d->ctx)
- d->ctx = const_cast<QGLContext *>(glwidget->context());
init();
}
-void MTexturePixmapItem::saveBackingStore(bool renew)
+void MTexturePixmapItem::saveBackingStore()
{
- d->saveBackingStore(renew);
+ d->saveBackingStore();
}
void MTexturePixmapItem::rebindPixmap()
{
- if (d->egl_image != EGL_NO_IMAGE_KHR) {
+ if (!d->custom_tfp && d->egl_image != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
+ d->egl_image = EGL_NO_IMAGE_KHR;
glBindTexture(GL_TEXTURE_2D, 0);
}
@@ -192,31 +176,23 @@
}
d->ctx->makeCurrent();
- d->egl_image = eglCreateImageKHR(d->eglresource->dpy, 0,
- EGL_NATIVE_PIXMAP_KHR,
- (EGLClientBuffer)d->windowp,
- attribs);
- if (d->egl_image == EGL_NO_IMAGE_KHR)
- qWarning("MTexturePixmapItem::%s(): Cannot create EGL image: 0x%x",
- __func__, eglGetError());
-
- glBindTexture(GL_TEXTURE_2D, d->textureId);
- if (d->egl_image != EGL_NO_IMAGE_KHR)
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, d->egl_image);
+ doTFP();
}
void MTexturePixmapItem::enableDirectFbRendering()
{
- d->damageTracking(false);
-
- if (d->direct_fb_render && d->egl_image == EGL_NO_IMAGE_KHR)
+ if (d->direct_fb_render)
return;
+ if (d->item->propertyCache())
+ d->item->propertyCache()->damageTracking(false);
d->direct_fb_render = true;
- glBindTexture(GL_TEXTURE_2D, 0);
- eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
- d->egl_image = EGL_NO_IMAGE_KHR;
+ if(!d->custom_tfp && d->egl_image != EGL_NO_IMAGE_KHR) {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
+ d->egl_image = EGL_NO_IMAGE_KHR;
+ }
if (d->windowp) {
XFreePixmap(QX11Info::display(), d->windowp);
d->windowp = 0;
@@ -227,15 +203,15 @@
void MTexturePixmapItem::enableRedirectedRendering()
{
- d->damageTracking(true);
-
- if (!d->direct_fb_render && d->egl_image != EGL_NO_IMAGE_KHR)
+ if (!d->direct_fb_render)
return;
+ if (d->item->propertyCache())
+ d->item->propertyCache()->damageTracking(true);
d->direct_fb_render = false;
XCompositeRedirectWindow(QX11Info::display(), window(),
CompositeRedirectManual);
- saveBackingStore(true);
+ saveBackingStore();
updateWindowPixmap();
}
@@ -252,24 +228,21 @@
void MTexturePixmapItem::initCustomTfp()
{
- d->ctextureId = d->eglresource->texman->getTexture();
-
- glBindTexture(GL_TEXTURE_2D, d->ctextureId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ // UNUSED.
+ // TODO: GLX backend should probably use same approach as here and
+ // re-use same texture id
}
void MTexturePixmapItem::cleanup()
-{
- EGLImageKHR egl_image = d->egl_image;
+{
EGLDisplay dpy = d->eglresource->dpy;
- eglDestroyImageKHR(dpy, egl_image);
- d->egl_image = EGL_NO_IMAGE_KHR;
- if (!d->custom_tfp)
- d->eglresource->texman->closeTexture(d->textureId);
- else
- d->eglresource->texman->closeTexture(d->ctextureId);
+ if (!d->custom_tfp && d->egl_image != EGL_NO_IMAGE_KHR) {
+ EGLImageKHR egl_image = d->egl_image;
+ eglDestroyImageKHR(dpy, egl_image);
+ d->egl_image = EGL_NO_IMAGE_KHR;
+ }
+ d->eglresource->texman->closeTexture(d->textureId);
// Work-around for crashes on some versions of below Qt 4.6
#if (QT_VERSION < 0x040600)
@@ -279,36 +252,72 @@
XFreePixmap(QX11Info::display(), d->windowp);
}
-void MTexturePixmapItem::updateWindowPixmap(XRectangle *rects, int num)
+void MTexturePixmapItem::updateWindowPixmap(XRectangle *rects, int num,
+ Time when)
{
- if (hasTransitioningWindow() || d->direct_fb_render || !windowVisible()
- || propertyCache()->isInputOnly())
+ // When a window is in transitioning limit the number of updates
+ // to @limit/@expiry miliseconds.
+ const unsigned expiry = 1000;
+ const int limit = 10;
+
+ if (hasTransitioningWindow()) {
+ // Limit the number of damages we're willing to process if we're
+ // in the middle of a transition, so the competition for the GL
+ // resources will be less tight.
+ if (d->pastDamages) {
+ // Forget about pastDamages we received long ago.
+ while (d->pastDamages->size() > 0
+ && d->pastDamages->first() + expiry < when)
+ d->pastDamages->removeFirst();
+ if (d->pastDamages->size() >= limit)
+ // Too many damages in the given timeframe, throttle.
+ return;
+ } else
+ d->pastDamages = new QList<Time>;
+ // Can afford this damage, but recoed when we received it,
+ // so to know when to forget about them.
+ d->pastDamages->append(when);
+ } else if (d->pastDamages) {
+ // The window is not transitioning, forget about all pastDamages.
+ delete d->pastDamages;
+ d->pastDamages = NULL;
+ }
+
+ // we want to update the pixmap even if the item is not visible because
+ // certain animations require up-to-date pixmap (alternatively we could mark
+ // it dirty and update it before the animation starts...)
+ if (d->direct_fb_render || propertyCache()->isInputOnly())
return;
QRegion r;
for (int i = 0; i < num; ++i)
r += QRegion(rects[i].x, rects[i].y, rects[i].width, rects[i].height);
d->damageRegion = r;
-
- // Our very own custom texture from pixmap
+
+ bool new_image = false;
if (d->custom_tfp) {
QPixmap qp = QPixmap::fromX11Pixmap(d->windowp);
-
+
QImage img = d->glwidget->convertToGLFormat(qp.toImage());
- glBindTexture(GL_TEXTURE_2D, d->ctextureId);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
- update();
- } else {
- if (d->egl_image == EGL_NO_IMAGE_KHR)
- saveBackingStore(true);
- d->glwidget->update();
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width(),
+ img.height(), GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ new_image = true;
+ } else if (d->egl_image == EGL_NO_IMAGE_KHR) {
+ saveBackingStore();
+ new_image = true;
+ }
+ if (new_image || !d->damageRegion.isEmpty()) {
+ if (!d->current_window_group)
+ d->glwidget->update();
+ else
+ d->current_window_group->updateWindowPixmap();
}
}
void MTexturePixmapItem::paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget)
+ const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
@@ -330,11 +339,17 @@
if (!d->ctx)
d->ctx = const_cast<QGLContext *>(gl->context());
+ if (!d->current_window_group)
+ renderTexture(painter->combinedTransform());
+}
+
+void MTexturePixmapItem::renderTexture(const QTransform& transform)
+{
if (propertyCache()->hasAlpha() || (opacity() < 1.0f && !dimmedEffect()) ) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
- glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);
+ }
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
const QRegion &shape = propertyCache()->shapeRegion();
// FIXME: not optimal. probably would be better to replace with
@@ -355,7 +370,7 @@
d->damageRegion.rects().at(i).height()),
d->damageRegion.rects().at(i).width(),
d->damageRegion.rects().at(i).height());
- d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
+ d->drawTexture(transform, boundingRect(), opacity());
}
} else if (shape_on) {
// draw a shaped window using glScissor
@@ -366,11 +381,10 @@
shape.rects().at(i).height()),
shape.rects().at(i).width(),
shape.rects().at(i).height());
- d->drawTexture(painter->combinedTransform(),
- boundingRect(), opacity());
+ d->drawTexture(transform, boundingRect(), opacity());
}
} else
- d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
+ d->drawTexture(transform, boundingRect(), opacity());
if (scissor_on)
glDisable(GL_SCISSOR_TEST);
@@ -381,11 +395,6 @@
glDisable(GL_BLEND);
}
-void MTexturePixmapItem::windowRaised()
-{
- d->windowRaised();
-}
-
void MTexturePixmapItem::resize(int w, int h)
{
d->resize(w, h);
@@ -403,7 +412,45 @@
void MTexturePixmapItem::clearTexture()
{
- glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 0);
+
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void MTexturePixmapItem::doTFP()
+{
+ if (isClosing()) // Pixmap is already freed. No sense to create EGL image
+ return; // from it again
+
+ //no EGL texture from pixmap extensions available
+ //use regular X11/GL calls to copy pixels from Pixmap to GL Texture
+ if (d->custom_tfp) {
+ QPixmap qp = QPixmap::fromX11Pixmap(d->windowp);
+ QImage img = QGLWidget::convertToGLFormat(qp.toImage());
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ }
+ //use EGL extensions
+ else {
+ d->egl_image = eglCreateImageKHR(d->eglresource->dpy, 0,
+ EGL_NATIVE_PIXMAP_KHR,
+ (EGLClientBuffer)d->windowp,
+ attribs);
+ if (d->egl_image == EGL_NO_IMAGE_KHR) {
+ qWarning("MTexturePixmapItem::%s(): Cannot create EGL image: 0x%x",
+ __func__, eglGetError());
+ return;
+ } else {
+ glBindTexture(GL_TEXTURE_2D, d->textureId);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, d->egl_image);
+ }
+ }
+}
+
+MTexturePixmapPrivate* MTexturePixmapItem::renderer() const
+{
+ return d;
}
--- src/mtexturepixmapitem_glx.cpp
+++ src/mtexturepixmapitem_glx.cpp
@@ -93,7 +93,6 @@
return;
d->glpixmap = 0;
- saveBackingStore();
d->custom_tfp = !hasTextureFromPixmap();
@@ -158,21 +157,21 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glXBindTexImageEXT(QX11Info::display(), d->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
+ saveBackingStore();
}
MTexturePixmapItem::MTexturePixmapItem(Window window,
MWindowPropertyCache *pc,
- QGLWidget *glwidget,
QGraphicsItem *parent)
: MCompositeWindow(window, pc, parent),
- d(new MTexturePixmapPrivate(window, glwidget, this))
+ d(new MTexturePixmapPrivate(window, this))
{
init();
}
-void MTexturePixmapItem::saveBackingStore(bool renew)
+void MTexturePixmapItem::saveBackingStore()
{
- d->saveBackingStore(renew);
+ d->saveBackingStore();
}
void MTexturePixmapItem::rebindPixmap()
@@ -184,7 +183,7 @@
None
};
- if (!d->custom_tfp) {
+ if (!d->custom_tfp && d->windowp) {
Display *display = QX11Info::display();
glXReleaseTexImageEXT(display, d->glpixmap, GLX_FRONT_LEFT_EXT);
glXDestroyPixmap(display, d->glpixmap);
@@ -198,7 +197,8 @@
void MTexturePixmapItem::enableDirectFbRendering()
{
- d->damageTracking(false);
+ if (d->item->propertyCache())
+ d->item->propertyCache()->damageTracking(false);
if ((d->direct_fb_render || d->glpixmap == 0) && !d->custom_tfp)
return;
@@ -224,7 +224,8 @@
void MTexturePixmapItem::enableRedirectedRendering()
{
- d->damageTracking(true);
+ if (d->item->propertyCache())
+ d->item->propertyCache()->damageTracking(true);
if ((!d->direct_fb_render || d->glpixmap != 0) && !d->custom_tfp)
return;
@@ -233,7 +234,7 @@
XCompositeRedirectWindow(QX11Info::display(), window(),
CompositeRedirectManual);
XSync(QX11Info::display(), FALSE);
- saveBackingStore(true);
+ saveBackingStore();
updateWindowPixmap();
}
@@ -272,10 +273,12 @@
XFreePixmap(QX11Info::display(), d->windowp);
}
-void MTexturePixmapItem::updateWindowPixmap(XRectangle *rects, int num)
+void MTexturePixmapItem::updateWindowPixmap(XRectangle *rects, int num,
+ Time when)
{
Q_UNUSED(rects);
Q_UNUSED(num);
+ Q_UNUSED(when);
if (isWindowTransitioning() || d->direct_fb_render || !windowVisible())
return;
@@ -284,10 +287,14 @@
if (d->custom_tfp && d->windowp) {
QPixmap qp = QPixmap::fromX11Pixmap(d->windowp);
- QImage img = d->glwidget->convertToGLFormat(qp.toImage());
- glBindTexture(GL_TEXTURE_2D, d->ctextureId);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ QT_TRY {
+ QImage img = d->glwidget->convertToGLFormat(qp.toImage());
+ glBindTexture(GL_TEXTURE_2D, d->ctextureId);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
+ } QT_CATCH(std::bad_alloc e) {
+ /* XGetImage() failed, the window has been unmapped. */;
+ }
}
update();
}
@@ -303,7 +310,8 @@
if (painter->paintEngine()->type() != QPaintEngine::OpenGL)
return;
#else
- if (painter->paintEngine()->type() != QPaintEngine::OpenGL2) {
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL2 &&
+ painter->paintEngine()->type() != QPaintEngine::OpenGL) {
return;
}
#endif
@@ -365,11 +373,6 @@
}
-void MTexturePixmapItem::windowRaised()
-{
- d->windowRaised();
-}
-
void MTexturePixmapItem::resize(int w, int h)
{
d->resize(w, h);
@@ -390,4 +393,12 @@
glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
GL_UNSIGNED_BYTE, 0);
+
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+MTexturePixmapPrivate* MTexturePixmapItem::renderer() const
+{
+ return d;
}
--- src/mtexturepixmapitem_p.cpp
+++ src/mtexturepixmapitem_p.cpp
@@ -22,6 +22,8 @@
#endif
#include "mtexturepixmapitem.h"
#include "texturepixmapshaders.h"
+#include "mcompositewindowshadereffect.h"
+#include "mcompositemanager.h"
#include <QX11Info>
#include <QRect>
@@ -41,6 +43,8 @@
#include "mtexturepixmapitem_p.h"
bool MTexturePixmapPrivate::inverted_texture = true;
+QGLWidget *MTexturePixmapPrivate::glwidget = 0;
+QGLContext *MTexturePixmapPrivate::ctx = 0;
MGLResourceManager *MTexturePixmapPrivate::glresource = 0;
static const GLuint D_VERTEX_COORDS = 0;
@@ -53,12 +57,63 @@
p->bindAttributeLocation(attrib, location);
}
+class MShaderProgram : public QGLShaderProgram
+{
+public:
+ MShaderProgram(const QGLContext* context, QObject* parent)
+ : QGLShaderProgram(context, parent)
+ {
+ texture = -1;
+ opacity = -1;
+ blurstep = -1;
+ }
+ void setWorldMatrix(GLfloat m[4][4]) {
+ static bool init = true;
+ if (init || memcmp(m, worldMatrix, sizeof(worldMatrix))) {
+ setUniformValue("matWorld", m);
+ memcpy(worldMatrix, m, sizeof(worldMatrix));
+ init = false;
+ }
+ }
+
+ void setTexture(GLuint t) {
+ if (t != texture) {
+ setUniformValue("texture", t);
+ texture = t;
+ }
+ }
+
+ void setOpacity(GLfloat o) {
+ if (o != opacity) {
+ setUniformValue("opacity", o);
+ opacity = o;
+ }
+ }
+ void setBlurStep(GLfloat b) {
+ if (b != blurstep) {
+ setUniformValue("blurstep", b);
+ blurstep = b;
+ }
+ }
+
+private:
+ // static because this is set in the shared vertex shader
+ static GLfloat worldMatrix[4][4];
+ GLfloat opacity, blurstep;
+ GLuint texture;
+};
+
+GLfloat MShaderProgram::worldMatrix[4][4];
+
// OpenGL ES 2.0 / OpenGL 2.0 - compatible texture painter
class MGLResourceManager: public QObject
{
public:
- /* add more values here as we add more effects */
+ /*
+ * This is the default set of shaders.
+ * Use MCompositeWindowShaderEffect class to add more shader effects
+ */
enum ShaderType {
NormalShader = 0,
BlurShader,
@@ -67,20 +122,24 @@
MGLResourceManager(QGLWidget *glwidget)
: QObject(glwidget),
- currentShader(0) {
- QGLShader *sharedVertexShader = new QGLShader(QGLShader::Vertex,
+ glcontext(glwidget->context()),
+ currentShader(0)
+ {
+ sharedVertexShader = new QGLShader(QGLShader::Vertex,
glwidget->context(), this);
if (!sharedVertexShader->compileSourceCode(QLatin1String(TexpVertShaderSource)))
qWarning("vertex shader failed to compile");
- QGLShaderProgram *normalShader = new QGLShaderProgram(glwidget->context(), this);
- shader[NormalShader] = normalShader;
+ MShaderProgram *normalShader = new MShaderProgram(glwidget->context(),
+ this);
normalShader->addShader(sharedVertexShader);
if (!normalShader->addShaderFromSourceCode(QGLShader::Fragment,
QLatin1String(TexpFragShaderSource)))
qWarning("normal fragment shader failed to compile");
+ shader[NormalShader] = normalShader;
- QGLShaderProgram *blurShader = new QGLShaderProgram(glwidget->context(), this);
+ MShaderProgram *blurShader = new MShaderProgram(glwidget->context(),
+ this);
shader[BlurShader] = blurShader;
blurShader->addShader(sharedVertexShader);
if (!blurShader->addShaderFromSourceCode(QGLShader::Fragment,
@@ -132,10 +191,8 @@
}
}
- void updateVertices(const QTransform &t, ShaderType type) {
- if (shader[type] != currentShader)
- currentShader = shader[type];
-
+ void updateVertices(const QTransform &t)
+ {
worldMatrix[0][0] = t.m11();
worldMatrix[0][1] = t.m12();
worldMatrix[0][3] = t.m13();
@@ -145,33 +202,95 @@
worldMatrix[3][0] = t.dx();
worldMatrix[3][1] = t.dy();
worldMatrix[3][3] = t.m33();
+ }
+
+ void updateVertices(const QTransform &t, ShaderType type)
+ {
+ if (shader[type] != currentShader)
+ currentShader = shader[type];
+
+ updateVertices(t);
currentShader->bind();
- currentShader->setUniformValue("matWorld", worldMatrix);
+ currentShader->setWorldMatrix(worldMatrix);
+ }
+
+
+ void updateVertices(const QTransform &t, GLuint customShaderId)
+ {
+ if (!customShaderId)
+ return;
+ MShaderProgram* frag = customShaders.value(customShaderId, 0);
+ if (!frag)
+ return;
+ currentShader = frag;
+ updateVertices(t);
+ currentShader->bind();
+ currentShader->setWorldMatrix(worldMatrix);
+ }
+
+ GLuint installPixelShader(const QByteArray& code)
+ {
+ QByteArray source = code;
+ source.append(TexpCustomShaderSource);
+ MShaderProgram *p = new MShaderProgram(glcontext, this);
+ p->addShader(sharedVertexShader);
+ if (!p->addShaderFromSourceCode(QGLShader::Fragment,
+ QLatin1String(source)))
+ qWarning("custom fragment shader failed to compile");
+
+ bindAttribLocation(p, "inputVertex", D_VERTEX_COORDS);
+ bindAttribLocation(p, "textureCoord", D_TEXTURE_COORDS);
+
+ if (p->link()) {
+ customShaders[p->programId()] = p;
+ return p->programId();
+ }
+
+ qWarning() << "failed installing custom fragment shader:"
+ << p->log();
+ p->deleteLater();
+
+ return 0;
}
private:
- static QGLShaderProgram *shader[ShaderTotal];
+ static MShaderProgram *shader[ShaderTotal];
+ QHash<GLuint, MShaderProgram *> customShaders;
+ QGLShader *sharedVertexShader;
+ const QGLContext* glcontext;
+
GLfloat projMatrix[4][4];
GLfloat worldMatrix[4][4];
GLfloat vertCoords[8];
GLfloat texCoords[8];
GLfloat texCoordsInv[8];
- QGLShaderProgram *currentShader;
+ MShaderProgram *currentShader;
int width;
int height;
friend class MTexturePixmapPrivate;
};
-QGLShaderProgram *MGLResourceManager::shader[ShaderTotal];
+MShaderProgram *MGLResourceManager::shader[ShaderTotal];
#endif
+
void MTexturePixmapPrivate::drawTexture(const QTransform &transform, const QRectF &drawRect, qreal opacity)
{
- // TODO only update if matrix is dirty
- glresource->updateVertices(transform, item->blurred() ?
- MGLResourceManager::BlurShader :
- MGLResourceManager::NormalShader);
+ if (current_effect) {
+ current_effect->d->drawTexture(this, transform, drawRect, opacity);
+ } else
+ q_drawTexture(transform, drawRect, opacity);
+}
+
+void MTexturePixmapPrivate::q_drawTexture(const QTransform &transform, const QRectF &drawRect, qreal opacity)
+{
+ if (current_effect)
+ glresource->updateVertices(transform, current_effect->activeShaderFragment());
+ else
+ glresource->updateVertices(transform, item->blurred() ?
+ MGLResourceManager::BlurShader :
+ MGLResourceManager::NormalShader);
GLfloat vertexCoords[] = {
drawRect.left(), drawRect.top(),
drawRect.left(), drawRect.bottom(),
@@ -187,10 +306,13 @@
else
glVertexAttribPointer(D_TEXTURE_COORDS, 2, GL_FLOAT, GL_FALSE, 0,
glresource->texCoords);
- if (item->blurred())
- glresource->currentShader->setUniformValue("blurstep", (GLfloat) 0.5);
- glresource->currentShader->setUniformValue("opacity", (GLfloat) opacity);
- glresource->currentShader->setUniformValue("texture", 0);
+
+ if (current_effect)
+ current_effect->setUniforms(glresource->currentShader);
+ else if (item->blurred())
+ glresource->currentShader->setBlurStep((GLfloat) 0.5);
+ glresource->currentShader->setOpacity((GLfloat) opacity);
+ glresource->currentShader->setTexture(0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(D_VERTEX_COORDS);
@@ -200,6 +322,65 @@
glActiveTexture(GL_TEXTURE0);
}
+void MTexturePixmapPrivate::installEffect(MCompositeWindowShaderEffect* effect)
+{
+ if (effect == prev_effect)
+ return;
+
+ if (prev_effect) {
+ disconnect(prev_effect, SIGNAL(enabledChanged(bool)), this,
+ SLOT(activateEffect(bool)));
+ disconnect(prev_effect, SIGNAL(destroyed()), this,
+ SLOT(removeEffect()));
+ }
+ current_effect = effect;
+ prev_effect = effect;
+ if (effect) {
+ connect(effect, SIGNAL(enabledChanged(bool)),
+ SLOT(activateEffect(bool)), Qt::UniqueConnection);
+ connect(effect, SIGNAL(destroyed()), SLOT(removeEffect()),
+ Qt::UniqueConnection);
+ }
+}
+
+void MTexturePixmapPrivate::removeEffect()
+{
+ MCompositeWindowShaderEffect* e= (MCompositeWindowShaderEffect* ) sender();
+ if (e == prev_effect)
+ prev_effect = 0;
+ for (int i=0; i < e->fragmentIds().size(); ++i) {
+ GLuint id = e->fragmentIds()[i];
+ QGLShaderProgram* frag = glresource->customShaders.value(id, 0);
+ if (frag)
+ delete frag;
+ glresource->customShaders.remove(id);
+ }
+}
+
+GLuint MTexturePixmapPrivate::installPixelShader(const QByteArray& code)
+{
+ if (!glwidget) {
+ MCompositeManager *m = (MCompositeManager*)qApp;
+ glwidget = m->glWidget();
+ }
+ if (!glresource) {
+ glresource = new MGLResourceManager(glwidget);
+ glresource->initVertices(glwidget);
+ }
+ if (glresource)
+ return glresource->installPixelShader(code);
+
+ return 0;
+}
+
+void MTexturePixmapPrivate::activateEffect(bool enabled)
+{
+ if (enabled)
+ current_effect = (MCompositeWindowShaderEffect* ) sender();
+ else
+ current_effect = 0;
+}
+
void MTexturePixmapPrivate::init()
{
if (!item->is_valid)
@@ -216,66 +397,67 @@
item->propertyCache()->realGeometry().y());
}
-MTexturePixmapPrivate::MTexturePixmapPrivate(Qt::HANDLE window, QGLWidget *w, MTexturePixmapItem *p)
- : ctx(0),
- glwidget(w),
- window(window),
+MTexturePixmapPrivate::MTexturePixmapPrivate(Qt::HANDLE window,
+ MTexturePixmapItem *p)
+ : window(window),
windowp(0),
-#ifdef DESKTOP_VERSION
+#ifdef GLES2_VERSION
+ egl_image(EGL_NO_IMAGE_KHR),
+#else
glpixmap(0),
#endif
textureId(0),
ctextureId(0),
custom_tfp(false),
- direct_fb_render(false),
+ direct_fb_render(false), // root's children start redirected
angle(0),
- damage_object(0),
- item(p)
+ item(p),
+ prev_effect(0),
+ pastDamages(0)
{
- damageTracking(true);
+ if (!glwidget) {
+ MCompositeManager *m = (MCompositeManager*)qApp;
+ glwidget = m->glWidget();
+ }
+ if (!ctx)
+ ctx = const_cast<QGLContext *>(glwidget->context());
+ if (item->propertyCache())
+ item->propertyCache()->damageTracking(true);
init();
}
MTexturePixmapPrivate::~MTexturePixmapPrivate()
{
- damageTracking(false);
-}
+ if (item->propertyCache())
+ item->propertyCache()->damageTracking(false);
-void MTexturePixmapPrivate::damageTracking(bool enabled)
-{
- if (damage_object) {
- XDamageDestroy(QX11Info::display(), damage_object);
- damage_object = NULL;
- }
+ if (windowp)
+ XFreePixmap(QX11Info::display(), windowp);
- if (enabled && !damage_object && !item->propertyCache()->isInputOnly())
- damage_object = XDamageCreate(QX11Info::display(), window,
- XDamageReportNonEmpty);
+ if (pastDamages)
+ delete pastDamages;
}
-void MTexturePixmapPrivate::saveBackingStore(bool renew)
+void MTexturePixmapPrivate::saveBackingStore()
{
if ((item->propertyCache()->is_valid && !item->propertyCache()->isMapped())
- || item->propertyCache()->isInputOnly())
+ || item->propertyCache()->isInputOnly()
+ || !window)
return;
if (windowp)
XFreePixmap(QX11Info::display(), windowp);
- Pixmap px = XCompositeNameWindowPixmap(QX11Info::display(), item->window());
- windowp = px;
- if (renew)
- item->rebindPixmap();
-}
-
-void MTexturePixmapPrivate::windowRaised()
-{
- XRaiseWindow(QX11Info::display(), item->window());
+ windowp = XCompositeNameWindowPixmap(QX11Info::display(), item->window());
+ item->rebindPixmap(); // windowp == 0 is also handled here
}
void MTexturePixmapPrivate::resize(int w, int h)
{
+ if (!window)
+ return;
+
if (!brect.isEmpty() && !item->isDirectRendered() && (brect.width() != w || brect.height() != h)) {
- item->saveBackingStore(true);
+ item->saveBackingStore();
item->updateWindowPixmap();
}
brect.setWidth(w);
--- src/mtexturepixmapitem_p.h
+++ src/mtexturepixmapitem_p.h
@@ -20,10 +20,11 @@
#ifndef DUITEXTUREPIXMAPITEM_P_H
#define DUITEXTUREPIXMAPITEM_P_H
+#include <QObject>
#include <QRect>
#include <QRegion>
-
-#include <X11/extensions/Xdamage.h>
+#include <QPointer>
+#include <X11/Xlib.h>
#ifdef GLES2_VERSION
#include <EGL/egl.h>
@@ -39,28 +40,34 @@
class QGraphicsItem;
class MTexturePixmapItem;
class QGLContext;
+class QTransform;
class MGLResourceManager;
+class MCompositeWindowShaderEffect;
+class MCompositeWindowGroup;
/*! Internal private implementation of MTexturePixmapItem
Warning! Interface here may change at any time!
*/
-class MTexturePixmapPrivate
+class MTexturePixmapPrivate: QObject
{
+ Q_OBJECT
public:
- MTexturePixmapPrivate(Window window, QGLWidget *w, MTexturePixmapItem *item);
+ MTexturePixmapPrivate(Window window, MTexturePixmapItem *item);
~MTexturePixmapPrivate();
void init();
void updateWindowPixmap(XRectangle *rects = 0, int num = 0);
- void saveBackingStore(bool renew = false);
+ void saveBackingStore();
void clearTexture();
bool isDirectRendered() const;
void resize(int w, int h);
- void windowRaised();
void drawTexture(const QTransform& transform, const QRectF& drawRect, qreal opacity);
- void damageTracking(bool enabled);
+
+ void q_drawTexture(const QTransform& transform, const QRectF& drawRect, qreal opacity);
+ void installEffect(MCompositeWindowShaderEffect* effect);
+ static GLuint installPixelShader(const QByteArray& code);
- QGLContext *ctx;
- QGLWidget *glwidget;
+ static QGLContext *ctx;
+ static QGLWidget *glwidget;
Window window;
Pixmap windowp;
#ifdef GLES2_VERSION
@@ -78,13 +85,26 @@
QRegion damageRegion;
qreal angle;
- Damage damage_object;
MTexturePixmapItem *item;
+ QPointer<MCompositeWindowShaderEffect> current_effect;
+#ifdef GLES2_VERSION
+ QPointer<MCompositeWindowGroup> current_window_group;
+#endif
+ const MCompositeWindowShaderEffect *prev_effect;
+
+ // Contains a limited number of server times we received damage
+ // notifications for this window. Only used by the EGL variant
+ // to throttle repairs if the window is transitioning.
+ QList<Time> *pastDamages;
#ifdef GLES2_VERSION
static EglResourceManager *eglresource;
#endif
static MGLResourceManager* glresource;
+
+private slots:
+ void activateEffect(bool enabled);
+ void removeEffect();
};
#endif //DUITEXTUREPIXMAPITEM_P_H
--- src/mwindowpropertycache.cpp
+++ src/mwindowpropertycache.cpp
@@ -17,6 +17,7 @@
**
****************************************************************************/
+#include <QtGui>
#include <stdlib.h>
#include <QX11Info>
#include <QRect>
@@ -25,65 +26,86 @@
#include <X11/extensions/Xrender.h>
#include <X11/extensions/shape.h>
#include <X11/Xmd.h>
+#include "mcompositemanager.h"
#include "mwindowpropertycache.h"
+#include "mcompositemanager_p.h"
#define MAX_TYPES 10
xcb_connection_t *MWindowPropertyCache::xcb_conn;
-MWindowPropertyCache::MWindowPropertyCache(Window w,
- xcb_get_window_attributes_reply_t *wa,
- xcb_get_geometry_reply_t *geom)
- : transient_for((Window)-1),
- wm_protocols_valid(false),
- icon_geometry_valid(false),
- decor_buttons_valid(false),
- shape_rects_valid(false),
- real_geom_valid(false),
- net_wm_state_valid(false),
- wm_state_query(true),
- has_alpha(-1),
- global_alpha(-1),
- video_global_alpha(-1),
- is_decorator(-1),
- wmhints(0),
- attrs(0),
- meego_layer(-1),
- window_state(-1),
- window_type(MCompAtoms::INVALID),
- window(w),
- parent_window(RootWindow(QX11Info::display(), 0)),
- being_mapped(false),
- xcb_real_geom(0)
+void MWindowPropertyCache::init()
{
+ transient_for = -1,
+ wm_protocols_valid = false;
+ icon_geometry_valid = false;
+ decor_buttons_valid = false;
+ shape_rects_valid = false;
+ real_geom_valid = false;
+ net_wm_state_valid = false;
+ wm_state_query = true;
+ has_alpha = -1;
+ global_alpha = -1;
+ video_global_alpha = -1;
+ is_decorator = -1;
+ wmhints = 0;
+ attrs = 0;
+ meego_layer = -1;
+ window_state = -1;
+ window_type = MCompAtoms::INVALID;
+ parent_window = QX11Info::appRootWindow();
+ always_mapped = -1;
+ cannot_minimize = -1;
+ desktop_view = -1;
+ being_mapped = false;
+ dont_iconify = false;
+ custom_region = 0;
+ custom_region_request_fired = false;
+ xcb_real_geom = 0;
+ damage_object = 0;
+
memset(&req_geom, 0, sizeof(req_geom));
memset(&home_button_geom, 0, sizeof(home_button_geom));
memset(&close_button_geom, 0, sizeof(close_button_geom));
window_type_atom = 0;
memset(&xcb_real_geom_cookie, 0, sizeof(xcb_real_geom_cookie));
+}
+void MWindowPropertyCache::init_invalid()
+{
+ is_valid = false;
+ memset(&xcb_transient_for_cookie, 0,
+ sizeof(xcb_transient_for_cookie));
+ memset(&xcb_meego_layer_cookie, 0, sizeof(xcb_meego_layer_cookie));
+ memset(&xcb_is_decorator_cookie, 0, sizeof(xcb_is_decorator_cookie));
+ memset(&xcb_window_type_cookie, 0, sizeof(xcb_window_type_cookie));
+ memset(&xcb_decor_buttons_cookie, 0, sizeof(xcb_decor_buttons_cookie));
+ memset(&xcb_wm_protocols_cookie, 0, sizeof(xcb_wm_protocols_cookie));
+ memset(&xcb_wm_state_cookie, 0, sizeof(xcb_wm_state_cookie));
+ memset(&xcb_wm_hints_cookie, 0, sizeof(xcb_wm_hints_cookie));
+ memset(&xcb_icon_geom_cookie, 0, sizeof(xcb_icon_geom_cookie));
+ memset(&xcb_global_alpha_cookie, 0, sizeof(xcb_global_alpha_cookie));
+ memset(&xcb_video_global_alpha_cookie, 0,
+ sizeof(xcb_video_global_alpha_cookie));
+ memset(&xcb_net_wm_state_cookie, 0, sizeof(xcb_net_wm_state_cookie));
+ memset(&xcb_always_mapped_cookie, 0, sizeof(xcb_always_mapped_cookie));
+ memset(&xcb_cannot_minimize_cookie, 0, sizeof(xcb_always_mapped_cookie));
+ memset(&xcb_pict_formats_cookie, 0, sizeof(xcb_pict_formats_cookie));
+ memset(&xcb_shape_rects_cookie, 0, sizeof(xcb_shape_rects_cookie));
+}
+
+MWindowPropertyCache::MWindowPropertyCache(Window w,
+ xcb_get_window_attributes_reply_t *wa,
+ xcb_get_geometry_reply_t *geom)
+ : window(w)
+{
+ init();
if (!wa) {
attrs = xcb_get_window_attributes_reply(xcb_conn,
xcb_get_window_attributes(xcb_conn, window), 0);
if (!attrs) {
qWarning("%s: invalid window 0x%lx", __func__, window);
- is_valid = false;
- memset(&xcb_transient_for_cookie, 0,
- sizeof(xcb_transient_for_cookie));
- memset(&xcb_meego_layer_cookie, 0, sizeof(xcb_meego_layer_cookie));
- memset(&xcb_is_decorator_cookie, 0, sizeof(xcb_is_decorator_cookie));
- memset(&xcb_window_type_cookie, 0, sizeof(xcb_window_type_cookie));
- memset(&xcb_decor_buttons_cookie, 0, sizeof(xcb_decor_buttons_cookie));
- memset(&xcb_wm_protocols_cookie, 0, sizeof(xcb_wm_protocols_cookie));
- memset(&xcb_wm_state_cookie, 0, sizeof(xcb_wm_state_cookie));
- memset(&xcb_wm_hints_cookie, 0, sizeof(xcb_wm_hints_cookie));
- memset(&xcb_icon_geom_cookie, 0, sizeof(xcb_icon_geom_cookie));
- memset(&xcb_global_alpha_cookie, 0, sizeof(xcb_global_alpha_cookie));
- memset(&xcb_video_global_alpha_cookie, 0,
- sizeof(xcb_video_global_alpha_cookie));
- memset(&xcb_net_wm_state_cookie, 0, sizeof(xcb_net_wm_state_cookie));
- memset(&xcb_pict_formats_cookie, 0, sizeof(xcb_pict_formats_cookie));
- memset(&xcb_shape_rects_cookie, 0, sizeof(xcb_shape_rects_cookie));
+ init_invalid();
return;
}
} else
@@ -141,6 +163,29 @@
xcb_net_wm_state_cookie = xcb_get_property(xcb_conn, 0, window,
ATOM(_NET_WM_STATE),
XCB_ATOM_ATOM, 0, 100);
+ xcb_always_mapped_cookie = xcb_get_property(xcb_conn, 0, window,
+ ATOM(_MEEGOTOUCH_ALWAYS_MAPPED),
+ XCB_ATOM_CARDINAL, 0, 1);
+ xcb_cannot_minimize_cookie = xcb_get_property(xcb_conn, 0, window,
+ ATOM(_MEEGOTOUCH_CANNOT_MINIMIZE),
+ XCB_ATOM_CARDINAL, 0, 1);
+ // add any transients to the transients list
+ MCompositeManager *m = (MCompositeManager*)qApp;
+ for (QList<Window>::const_iterator it = m->d->stacking_list.begin();
+ it != m->d->stacking_list.end(); ++it) {
+ MWindowPropertyCache *p = m->d->prop_caches.value(*it, 0);
+ if (p && p != this && p->transientFor() == window)
+ transients.append(*it);
+ }
+ connect(this, SIGNAL(meegoDecoratorButtonsChanged(Window)),
+ m->d, SLOT(setupButtonWindows(Window)));
+}
+
+MWindowPropertyCache::MWindowPropertyCache()
+ : window(None)
+{
+ init();
+ init_invalid();
}
MWindowPropertyCache::~MWindowPropertyCache()
@@ -165,11 +210,26 @@
free(xcb_real_geom);
xcb_real_geom = 0;
}
+ if (transient_for && transient_for != (Window)-1) {
+ MCompositeManager *m = (MCompositeManager*)qApp;
+ // remove reference from the old "parent"
+ MWindowPropertyCache *p = m->d->prop_caches.value(transient_for, 0);
+ if (p) p->transients.removeAll(window);
+ }
xcb_get_property_reply_t *r;
if (transient_for == (Window)-1) {
r = xcb_get_property_reply(xcb_conn, xcb_transient_for_cookie, 0);
if (r) free(r);
}
+ if (always_mapped < 0) {
+ r = xcb_get_property_reply(xcb_conn, xcb_always_mapped_cookie, 0);
+ if (r) free(r);
+ }
+ if (cannot_minimize < 0) {
+ r = xcb_get_property_reply(xcb_conn, xcb_cannot_minimize_cookie, 0);
+ if (r) free(r);
+ }
+ desktopView(false); // free the reply if it has been requested
if (meego_layer < 0) {
r = xcb_get_property_reply(xcb_conn, xcb_meego_layer_cookie, 0);
if (r) free(r);
@@ -196,6 +256,11 @@
r = xcb_get_property_reply(xcb_conn, xcb_wm_protocols_cookie, 0);
if (r) free(r);
}
+ if (custom_region_request_fired) {
+ r = xcb_get_property_reply(xcb_conn, xcb_custom_region_cookie, 0);
+ if (r) free(r);
+ }
+ if (custom_region) delete custom_region;
if (wm_state_query)
windowState();
if (!icon_geometry_valid)
@@ -286,6 +351,44 @@
return shape_region;
}
+const QRegion &MWindowPropertyCache::customRegion(bool request_only)
+{
+ if (!is_valid) {
+ if (!custom_region) custom_region = new QRegion(0, 0, 0, 0);
+ return *custom_region;
+ }
+ if (request_only || (!custom_region && !custom_region_request_fired)) {
+ if (custom_region_request_fired)
+ customRegion(false); // free the old reply
+ xcb_custom_region_cookie = xcb_get_property(xcb_conn, 0, window,
+ ATOM(_MEEGOTOUCH_CUSTOM_REGION),
+ XCB_ATOM_CARDINAL, 0, 10 * 4);
+ custom_region_request_fired = true;
+ }
+ if (!request_only && custom_region_request_fired) {
+ xcb_get_property_reply_t *r;
+ r = xcb_get_property_reply(xcb_conn, xcb_custom_region_cookie, 0);
+ custom_region_request_fired = false;
+ if (custom_region)
+ delete custom_region;
+ custom_region = new QRegion(0, 0, 0, 0);
+ if (r) {
+ int len = xcb_get_property_value_length(r);
+ if (len >= (int)sizeof(CARD32) * 4) {
+ int n = len / sizeof(CARD32) / 4;
+ CARD32 *p = (CARD32*)xcb_get_property_value(r);
+ for (int i = 0; i < n; ++i) {
+ int j = i * 4;
+ QRect tmp(p[j], p[j + 1], p[j + 2], p[j + 3]);
+ *custom_region += tmp;
+ }
+ }
+ free(r);
+ }
+ }
+ return *custom_region;
+}
+
Window MWindowPropertyCache::transientFor()
{
if (is_valid && transient_for == (Window)-1) {
@@ -299,11 +402,84 @@
free(r);
if (transient_for == window)
transient_for = 0;
- }
+ if (transient_for) {
+ MCompositeManager *m = (MCompositeManager*)qApp;
+ // add reference to the "parent"
+ MWindowPropertyCache *p = m->d->prop_caches.value(
+ transient_for, 0);
+ if (p) p->transients.append(window);
+ // need to check stacking again to make sure the "parent" is
+ // stacked according to the changed transient window list
+ m->d->dirtyStacking(false);
+ }
+ } else
+ transient_for = 0;
}
return transient_for;
}
+int MWindowPropertyCache::cannotMinimize()
+{
+ if (is_valid && cannot_minimize < 0) {
+ xcb_get_property_reply_t *r;
+ r = xcb_get_property_reply(xcb_conn, xcb_cannot_minimize_cookie, 0);
+ if (r) {
+ if (xcb_get_property_value_length(r) == sizeof(CARD32))
+ cannot_minimize = *((CARD32*)xcb_get_property_value(r));
+ else
+ cannot_minimize = 0;
+ free(r);
+ } else
+ cannot_minimize = 0;
+ }
+ return cannot_minimize;
+}
+
+int MWindowPropertyCache::alwaysMapped()
+{
+ if (is_valid && always_mapped < 0) {
+ xcb_get_property_reply_t *r;
+ r = xcb_get_property_reply(xcb_conn, xcb_always_mapped_cookie, 0);
+ if (r) {
+ if (xcb_get_property_value_length(r) == sizeof(CARD32))
+ always_mapped = *((CARD32*)xcb_get_property_value(r));
+ else
+ always_mapped = 0;
+ free(r);
+ } else
+ always_mapped = 0;
+ }
+ return always_mapped;
+}
+
+int MWindowPropertyCache::desktopView(bool request_only)
+{
+ static bool request_fired = false;
+ static xcb_get_property_cookie_t c;
+ if (is_valid && request_only) {
+ if (request_fired)
+ // free the old reply
+ desktopView(false);
+ c = xcb_get_property(xcb_conn, 0, window,
+ ATOM(_MEEGOTOUCH_DESKTOP_VIEW),
+ XCB_ATOM_CARDINAL, 0, 1);
+ request_fired = true;
+ } else if (is_valid && request_fired) {
+ xcb_get_property_reply_t *r;
+ r = xcb_get_property_reply(xcb_conn, c, 0);
+ if (r) {
+ if (xcb_get_property_value_length(r) == sizeof(CARD32))
+ desktop_view = *((CARD32*)xcb_get_property_value(r));
+ else
+ desktop_view = -1;
+ free(r);
+ } else
+ desktop_view = -1;
+ request_fired = false;
+ }
+ return desktop_view;
+}
+
bool MWindowPropertyCache::isDecorator()
{
if (is_valid && is_decorator < 0) {
@@ -334,6 +510,9 @@
}
}
if (meego_layer > 6) meego_layer = 6;
+ if (meego_layer == 1 || meego_layer == 2)
+ // these (screen/device lock) cannot be iconified by default
+ dont_iconify = true;
return (unsigned)meego_layer;
}
@@ -389,11 +568,36 @@
if (transient_for == (Window)-1)
// collect the old reply
transientFor();
+ if (transient_for && transient_for != (Window)-1) {
+ MCompositeManager *m = (MCompositeManager*)qApp;
+ // remove reference from the old "parent"
+ MWindowPropertyCache *p = m->d->prop_caches.value(transient_for, 0);
+ if (p) p->transients.removeAll(window);
+ }
transient_for = (Window)-1;
xcb_transient_for_cookie = xcb_get_property(xcb_conn, 0, window,
XCB_ATOM_WM_TRANSIENT_FOR,
XCB_ATOM_WINDOW, 0, 1);
return true;
+ } else if (e->atom == ATOM(_MEEGOTOUCH_ALWAYS_MAPPED)) {
+ if (always_mapped < 0)
+ // collect the old reply
+ alwaysMapped();
+ always_mapped = -1;
+ xcb_always_mapped_cookie = xcb_get_property(xcb_conn, 0, window,
+ ATOM(_MEEGOTOUCH_ALWAYS_MAPPED),
+ XCB_ATOM_CARDINAL, 0, 1);
+ emit alwaysMappedChanged(this);
+ } else if (e->atom == ATOM(_MEEGOTOUCH_CANNOT_MINIMIZE)) {
+ if (cannot_minimize < 0)
+ // collect the old reply
+ cannotMinimize();
+ cannot_minimize = -1;
+ xcb_cannot_minimize_cookie = xcb_get_property(xcb_conn, 0, window,
+ ATOM(_MEEGOTOUCH_CANNOT_MINIMIZE),
+ XCB_ATOM_CARDINAL, 0, 1);
+ } else if (e->atom == ATOM(_MEEGOTOUCH_DESKTOP_VIEW)) {
+ emit desktopViewChanged(this);
} else if (e->atom == ATOM(WM_HINTS)) {
if (!wmhints)
// collect the old reply
@@ -419,6 +623,7 @@
xcb_icon_geom_cookie = xcb_get_property(xcb_conn, 0, window,
ATOM(_NET_WM_ICON_GEOMETRY),
XCB_ATOM_CARDINAL, 0, 4);
+ emit iconGeometryUpdated();
} else if (e->atom == ATOM(_MEEGOTOUCH_GLOBAL_ALPHA)) {
if (global_alpha < 0)
// collect the old reply
@@ -443,8 +648,7 @@
xcb_decor_buttons_cookie = xcb_get_property(xcb_conn, 0, window,
ATOM(_MEEGOTOUCH_DECORATOR_BUTTONS),
XCB_ATOM_CARDINAL, 0, 8);
- // TODO: could be based on a signal
- return true;
+ emit meegoDecoratorButtonsChanged(window);
} else if (e->atom == ATOM(WM_PROTOCOLS)) {
if (!wm_protocols_valid)
// collect the old reply
@@ -477,7 +681,12 @@
xcb_meego_layer_cookie = xcb_get_property(xcb_conn, 0, window,
ATOM(_MEEGO_STACKING_LAYER),
XCB_ATOM_CARDINAL, 0, 1);
+ // raise it so that it becomes on top of same-leveled windows
+ MCompositeManager *m = (MCompositeManager*)qApp;
+ m->d->positionWindow(window, true);
return true;
+ } else if (e->atom == ATOM(_MEEGOTOUCH_CUSTOM_REGION)) {
+ emit customRegionChanged(this);
}
return false;
}
@@ -490,7 +699,8 @@
if (r && (unsigned)xcb_get_property_value_length(r) >= sizeof(CARD32))
window_state = ((CARD32*)xcb_get_property_value(r))[0];
else {
- window_state = NormalState;
+ // mark it so that MCompositeManagerPrivate::setWindowState sets it
+ window_state = -1;
}
if (r) free(r);
wm_state_query = false;
@@ -613,7 +823,6 @@
return icon_geometry;
}
-#define OPAQUE 0xffffffff
int MWindowPropertyCache::alphaValue(xcb_get_property_cookie_t c)
{
xcb_get_property_reply_t *r;
@@ -626,9 +835,11 @@
free(r);
return 255;
}
- CARD32 i = *((CARD32*)xcb_get_property_value(r));
- double opacity = i * 1.0 / OPAQUE;
- return opacity * 255;
+
+ /* Map 0..0xFFFFFFFF -> 0..0xFF. */
+ int ret = *(CARD32*)xcb_get_property_value(r) >> 24;
+ free(r);
+ return ret;
}
int MWindowPropertyCache::globalAlpha()
@@ -637,7 +848,6 @@
return global_alpha;
global_alpha = alphaValue(xcb_global_alpha_cookie);
-
return global_alpha;
}
@@ -697,3 +907,19 @@
return window_type;
}
+// MWindowDummyPropertyCache
+MWindowDummyPropertyCache *MWindowDummyPropertyCache::singleton;
+
+MWindowDummyPropertyCache *MWindowDummyPropertyCache::get()
+{
+ if (!singleton)
+ singleton = new MWindowDummyPropertyCache();
+ return singleton;
+}
+
+bool MWindowDummyPropertyCache::event(QEvent *e)
+{
+ // Ignore deleteLater().
+ return e->type() == QEvent::DeferredDelete
+ ? true : MWindowPropertyCache::event(e);
+}
--- src/mwindowpropertycache.h
+++ src/mwindowpropertycache.h
@@ -20,24 +20,29 @@
#ifndef MWINDOWPROPERTYCACHE_H
#define MWINDOWPROPERTYCACHE_H
+#include <QRegion>
+#include <QX11Info>
#include <X11/Xutil.h>
#include <X11/Xlib-xcb.h>
#include <xcb/render.h>
#include <xcb/shape.h>
#include <X11/extensions/shape.h>
+#include <X11/extensions/Xdamage.h>
#include "mcompatoms_p.h"
-#include <QRegion>
/*!
* This is a class for caching window property values for a window.
*/
class MWindowPropertyCache: public QObject
{
+ Q_OBJECT
public:
/*! Construct a MWindowPropertyCache
* \param window id to the window whose properties are cached
+ * Without one constructs a placeholder object.
*/
+ MWindowPropertyCache();
MWindowPropertyCache(Window window,
xcb_get_window_attributes_reply_t *attrs = 0,
xcb_get_geometry_reply_t *geom = 0);
@@ -93,6 +98,7 @@
ShapeBounding);
}
+ Window winId() const { return window; }
Window parentWindow() const { return parent_window; }
void setParentWindow(Window w) { parent_window = w; }
@@ -121,6 +127,11 @@
*/
const QList<Atom>& netWmState();
+ /*!
+ * Returns list of transients of the window.
+ */
+ const QList<Window>& transientWindows() const { return transients; }
+
// used to set the atom list now, for immediate effect in e.g. stacking
void setNetWmState(const QList<Atom>& s) {
if (!is_valid)
@@ -139,6 +150,14 @@
bool beingMapped() const { return being_mapped; }
void setBeingMapped(bool s) { being_mapped = s; }
+ /*!
+ * Used for special windows that should not be minimised/iconified.
+ */
+ bool dontIconify() {
+ return cannotMinimize() > 0 || dont_iconify;
+ }
+ void setDontIconify(bool s) { dont_iconify = s; }
+
bool isMapped() const {
if (!is_valid || !attrs)
return false;
@@ -152,7 +171,7 @@
if (s)
attrs->map_state = XCB_MAP_STATE_VIEWABLE;
else
- attrs->map_state = XCB_MAP_STATE_UNVIEWABLE;
+ attrs->map_state = XCB_MAP_STATE_UNMAPPED;
}
/*!
@@ -193,6 +212,26 @@
unsigned int meegoStackingLayer();
/*!
+ * Returns value of _MEEGOTOUCH_ALWAYS_MAPPED.
+ */
+ int alwaysMapped();
+
+ /*!
+ * Returns value of _MEEGOTOUCH_CANNOT_MINIMIZE.
+ */
+ int cannotMinimize();
+
+ /*!
+ * Returns value of _MEEGOTOUCH_DESKTOP_VIEW (makes sense for desktop only).
+ */
+ int desktopView(bool request_only = false);
+
+ /*!
+ * Returns value of _MEEGOTOUCH_CUSTOM_REGION.
+ */
+ const QRegion &customRegion(bool request_only = false);
+
+ /*!
* Called on PropertyNotify for this window.
* Returns true if we should re-check stacking order.
*/
@@ -210,12 +249,35 @@
MWindowPropertyCache::xcb_conn = c;
}
+ void damageTracking(bool enabled)
+ {
+ if (damage_object && enabled)
+ return;
+ if (damage_object && !enabled) {
+ XDamageDestroy(QX11Info::display(), damage_object);
+ damage_object = 0;
+ }
+ else if (enabled && !damage_object && !isInputOnly())
+ damage_object = XDamageCreate(QX11Info::display(), window,
+ XDamageReportNonEmpty);
+ }
+
+signals:
+ void iconGeometryUpdated();
+ void meegoDecoratorButtonsChanged(Window w);
+ void desktopViewChanged(MWindowPropertyCache *pc);
+ void alwaysMappedChanged(MWindowPropertyCache *pc);
+ void customRegionChanged(MWindowPropertyCache *pc);
+
private:
+ void init();
+ void init_invalid();
int alphaValue(xcb_get_property_cookie_t c);
void buttonGeometryHelper();
Atom window_type_atom;
Window transient_for;
+ QList<Window> transients;
QList<Atom> wm_protocols;
bool wm_protocols_valid;
bool icon_geometry_valid;
@@ -237,7 +299,10 @@
int meego_layer, window_state;
MCompAtoms::Type window_type;
Window window, parent_window;
- bool being_mapped;
+ int always_mapped, cannot_minimize, desktop_view;
+ bool being_mapped, dont_iconify;
+ QRegion *custom_region;
+ bool custom_region_request_fired;
// geometry is requested only once in the beginning, after that, we
// use ConfigureNotifys to update the size through setRealGeometry()
xcb_get_geometry_reply_t *xcb_real_geom;
@@ -254,11 +319,27 @@
xcb_get_property_cookie_t xcb_global_alpha_cookie;
xcb_get_property_cookie_t xcb_video_global_alpha_cookie;
xcb_get_property_cookie_t xcb_net_wm_state_cookie;
+ xcb_get_property_cookie_t xcb_always_mapped_cookie;
+ xcb_get_property_cookie_t xcb_cannot_minimize_cookie;
+ xcb_get_property_cookie_t xcb_custom_region_cookie;
xcb_render_query_pict_formats_cookie_t xcb_pict_formats_cookie;
xcb_shape_get_rectangles_cookie_t xcb_shape_rects_cookie;
QRegion shape_region;
static xcb_connection_t *xcb_conn;
+ Damage damage_object;
+};
+
+// Non-deletable dummy MWindowPropertyCache.
+class MWindowDummyPropertyCache: public MWindowPropertyCache
+{
+public:
+ static MWindowDummyPropertyCache *get();
+
+private:
+ virtual bool event(QEvent *e);
+
+ static MWindowDummyPropertyCache *singleton;
};
#endif
--- src/src.pro
+++ src/src.pro
@@ -2,26 +2,25 @@
contains(QT_CONFIG, opengles2) {
message("building Makefile for EGL/GLES2 version")
- DEFINES += GLES2_VERSION
- SOURCES += mtexturepixmapitem_egl.cpp
+ SOURCES += mtexturepixmapitem_egl.cpp mcompositewindowgroup.cpp
+ HEADERS += mcompositewindowgroup.h
+ publicHeaders.files += mcompositewindowgroup.h
} else {
# Qt wasn't built with EGL/GLES2 support but EGL is present
# ensure we still use the EGL back-end
exists($$QMAKE_INCDIR_OPENGL/EGL) {
message("building Makefile for EGL/GLES2 version")
- DEFINES += GLES2_VERSION
SOURCES += mtexturepixmapitem_egl.cpp
LIBS += -lEGL
}
# Otherwise use GLX backend
else {
message("building Makefile for GLX version")
- DEFINES += DESKTOP_VERSION
SOURCES += mtexturepixmapitem_glx.cpp
}
}
-TEMPLATE = app
+TEMPLATE = lib
TARGET = mcompositor
DEPENDPATH += .
QT += dbus
@@ -30,6 +29,7 @@
INCLUDEPATH += ../decorators/libdecorator/
HEADERS += \
mtexturepixmapitem.h \
+ mtexturepixmapitem_p.h \
mcompositescene.h \
mcompositewindow.h \
mwindowpropertycache.h \
@@ -39,10 +39,12 @@
mcompositemanager_p.h \
mdevicestate.h \
mcompatoms_p.h \
- mdecoratorframe.h
+ mdecoratorframe.h \
+ mcompositemanagerextension.h \
+ mcompositewindowshadereffect.h \
+ mcompmgrextensionfactory.h
SOURCES += \
- main.cpp \
mtexturepixmapitem_p.cpp \
mcompositescene.cpp \
mcompositewindow.cpp \
@@ -51,7 +53,9 @@
mcompositemanager.cpp \
msimplewindowframe.cpp \
mdevicestate.cpp \
- mdecoratorframe.cpp
+ mdecoratorframe.cpp \
+ mcompositemanagerextension.cpp \
+ mcompositewindowshadereffect.cpp
RESOURCES = tools.qrc
@@ -59,11 +63,22 @@
PKGCONFIG += contextsubscriber-1.0
QT += core gui opengl
-target.path += $$M_INSTALL_BIN
+# TODO: refactor the headers to exclude private stuff
+publicHeaders.files += mcompositewindow.h \
+ mcompositemanager.h \
+ mcompositewindowshadereffect.h \
+ mcompositemanagerextension.h \
+ mwindowpropertycache.h \
+ mcompatoms_p.h \
+ mcompmgrextensionfactory.h
+publicHeaders.path = $$M_INSTALL_HEADERS/mcompositor
+INSTALLS += publicHeaders
+
+target.path += /usr/lib
INSTALLS += target
LIBS += -lXdamage -lXcomposite -lXfixes -lX11-xcb -lxcb-render -lxcb-shape \
- ../decorators/libdecorator/libdecorator.so
+ -lXrandr ../decorators/libdecorator/libdecorator.so
QMAKE_EXTRA_TARGETS += check
check.depends = $$TARGET
--- src/texturepixmapshaders.h
+++ src/texturepixmapshaders.h
@@ -40,6 +40,15 @@
gl_FragColor = texture2D(texture, fragTexCoord) * opacity; \
}";
+static const char* TexpCustomShaderSource = "\
+ varying highp vec2 fragTexCoord;\n\
+ uniform lowp sampler2D texture;\n\
+ uniform lowp float opacity;\n\
+ void main(void) \n\
+ {\n\
+ gl_FragColor = customShader(texture, fragTexCoord) * opacity; \n\
+ }\n\n";
+
#if 0
static const char *AlphaTestFragShaderSource = "\
--- tests/functional/createTestXml
+++ tests/functional/createTestXml
@@ -60,7 +60,12 @@
BNAME=${CASEFILE}.testdata
if [ -e $BNAME ]; then
. ./$BNAME
- TESTCASE_TEMPLATE="\t\t\t<case name=\"$CaseName\" description=\"$CaseDescription\" requirement=\"$CaseRequirement\" timeout=\"$CaseTimeout\">\n\t\t\t\t<step expected_result=\"0\">$INSTALL_PATH/$CASEFILE</step>\n\t\t\t</case>\n"
+ if [ x$CaseInsignificant != x ]; then
+ TESTCASE_TEMPLATE="\t\t\t<case name=\"$CaseName\" description=\"$CaseDescription\" requirement=\"$CaseRequirement\" timeout=\"$CaseTimeout\" insignificant=\"$CaseInsignificant\">\n\t\t\t\t<step expected_result=\"0\">$INSTALL_PATH/$CASEFILE</step>\n\t\t\t</case>\n"
+ else
+ TESTCASE_TEMPLATE="\t\t\t<case name=\"$CaseName\" description=\"$CaseDescription\" requirement=\"$CaseRequirement\" timeout=\"$CaseTimeout\">\n\t\t\t\t<step expected_result=\"0\">$INSTALL_PATH/$CASEFILE</step>\n\t\t\t</case>\n"
+ fi
+ unset CaseInsignificant
TESTCASES="${TESTCASES}${TESTCASE_TEMPLATE}"
fi
done
--- tests/functional/functional.pro
+++ tests/functional/functional.pro
@@ -1,6 +1,6 @@
TEMPLATE=subdirs
-QMAKE_EXTRA_TARGETS += metadata
+QMAKE_EXTRA_TARGETS += metadata initScript
XMLLINT= xmllint --noout --schema /usr/share/test-definition/testdefinition-syntax.xsd --schema /usr/share/test-definition/testdefinition-tm_terms.xsd
SUITENAME=mcompositor-functional-tests
@@ -20,5 +20,7 @@
metadata.CONFIG += no_check_exist
-INSTALLS += metadata scripts
+initScript.files = mcompositor-test-init.py
+initScript.path = /usr/bin
+INSTALLS += metadata scripts initScript
--- tests/functional/mcompositor-test-init.py
+++ tests/functional/mcompositor-test-init.py
+#!/usr/bin/python
+# Common initialisation commands for each test.
+
+import os, sys
+
+if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
+ print 'mcetool is missing!'
+
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled false'):
+ print 'cannot disable notifications'
+
+if os.system('pidof mcompositor'):
+ print 'mcompositor is not running'
+ sys.exit(1)
+
+os.system('aegis-su -o com.nokia.maemo -n /usr/bin/windowstack')
+os.system('aegis-su -o com.nokia.maemo -n /usr/bin/windowctl')
+
+if os.system('windowstack m | grep mdecorator'):
+ print 'mdecorator is not mapped!'
+ sys.exit(1)
+
+os.system('uptime')
--- tests/functional/test0.py
+++ tests/functional/test0.py
@@ -10,15 +10,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
- sys.exit(1)
-
-if os.system('windowstack m | grep mdecorator'):
- print 'mdecorator is not mapped!'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowctl kn')
@@ -51,4 +43,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test1.py
+++ tests/functional/test1.py
@@ -10,11 +10,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowctl kn')
@@ -47,4 +43,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test10.py
+++ tests/functional/test10.py
@@ -14,11 +14,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowstack m')
@@ -94,4 +90,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test11.py
+++ tests/functional/test11.py
@@ -13,11 +13,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowstack m')
@@ -71,4 +67,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test12.py
+++ tests/functional/test12.py
@@ -15,11 +15,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowctl kn')
@@ -106,4 +102,8 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
+
sys.exit(ret)
--- tests/functional/test13.py
+++ tests/functional/test13.py
@@ -10,11 +10,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
def get_window_size(w):
@@ -29,7 +25,8 @@
break
return ret
-(fs_w, fs_h) = (864, 480)
+(fs_w, fs_h) = [ int(x) for x in os.popen("windowctl D").readline().split() ]
+
# map non-fullscreen application window
fd = os.popen('windowctl n')
@@ -53,4 +50,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test14.py
+++ tests/functional/test14.py
@@ -11,11 +11,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
def get_window_size(w):
@@ -30,7 +26,7 @@
break
return ret
-(fs_w, fs_h) = (864, 480)
+(fs_w, fs_h) = [ int(x) for x in os.popen("windowctl D").readline().split() ]
# map fullscreen application window
fd = os.popen('windowctl fn')
@@ -54,4 +50,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test15.py
+++ tests/functional/test15.py
@@ -17,11 +17,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
ret = 0
@@ -74,4 +70,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test16.py
+++ tests/functional/test16.py
@@ -9,11 +9,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowstack m')
@@ -51,4 +47,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test17.py
+++ tests/functional/test17.py
@@ -38,11 +38,7 @@
print fd.read(5000)
ret = 1
-if os.system('/sbin/mcetool --unblank-screen --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowstack m')
@@ -94,4 +90,7 @@
time.sleep(1)
check_focus(home_win)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test18.py
+++ tests/functional/test18.py
@@ -18,11 +18,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
ret = 0
@@ -103,4 +99,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test19.py
+++ tests/functional/test19.py
@@ -14,11 +14,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowstack m')
@@ -84,4 +80,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test2.py
+++ tests/functional/test2.py
@@ -10,11 +10,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
# create two dialogs
@@ -41,4 +37,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test20.py
+++ tests/functional/test20.py
@@ -14,11 +14,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowstack m')
@@ -57,7 +53,7 @@
# simulate a phone call
fd = os.popen('windowctl P')
-time.sleep(3)
+time.sleep(15)
# create a fullscreen application window
fd = os.popen('windowctl fn')
@@ -81,4 +77,7 @@
os.popen('pkill context-provide')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test20.py.testdata
+++ tests/functional/test20.py.testdata
@@ -1,6 +1,7 @@
CaseName="jammed_and_fullscreen_app_during_call_are_decorated"
CaseRequirement="NONE"
CaseTimeout="360"
+CaseInsignificant=true
CaseDescription="Check that jammed and (during call) fullscreen apps are decorated.\n
\n
- Test steps\n
--- tests/functional/test21.py
+++ tests/functional/test21.py
+#!/usr/bin/python
+
+# Some tests for Meego stacking layer support.
+
+#* Test steps
+# * show an application window
+# * show an application window transient to the first one
+# * show an application window transient to the first transient
+# * show an application window transient to the second transient
+# * show an application window
+# * set the first non-transient application window to Meego level 1
+# * set the second non-transient application window to Meego level 1
+# * check correct stacking
+# * set the first non-transient application window to Meego level 2
+#* Post-conditions
+# * check correct stacking
+
+import os, re, sys, time
+
+if os.system('mcompositor-test-init.py'):
+ sys.exit(1)
+
+# create application window
+fd = os.popen('windowctl kn')
+app1 = fd.readline().strip()
+time.sleep(1)
+
+# create transient application windows
+fd = os.popen('windowctl kn %s' % app1)
+trans1 = fd.readline().strip()
+fd = os.popen('windowctl kn %s' % trans1)
+trans2 = fd.readline().strip()
+fd = os.popen('windowctl kn %s' % trans2)
+trans3 = fd.readline().strip()
+time.sleep(1)
+
+# create application window
+fd = os.popen('windowctl kn')
+app2 = fd.readline().strip()
+time.sleep(1)
+
+# set the non-transient application windows to Meego level 1
+os.popen('windowctl E %s 1' % app1)
+os.popen('windowctl E %s 1' % app2)
+time.sleep(1)
+
+ret = app2_found = trans1_found = trans2_found = trans3_found = 0
+fd = os.popen('windowstack m')
+s = fd.read(5000)
+for l in s.splitlines():
+ if re.search("%s " % app2, l.strip()):
+ print app2, 'found'
+ app2_found = 1
+ elif re.search("%s " % trans3, l.strip()) and app2_found:
+ print trans3, 'found'
+ trans3_found = 1
+ elif re.search("%s " % trans2, l.strip()) and app2_found and trans3_found:
+ print trans2, 'found'
+ trans2_found = 1
+ elif re.search("%s " % trans1, l.strip()) and app2_found and trans3_found \
+ and trans2_found:
+ print trans1, 'found'
+ trans1_found = 1
+ elif re.search("%s " % app1, l.strip()) and app2_found and trans1_found \
+ and trans2_found and trans3_found:
+ print app1, 'found'
+ break
+ elif not re.search(" no-TYPE ", l.strip()):
+ print 'FAIL: stacking order is wrong'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+
+# set the first application window to Meego level 2
+os.popen('windowctl E %s 2' % app1)
+time.sleep(1)
+
+app2_found = trans1_found = trans2_found = trans3_found = 0
+fd = os.popen('windowstack m')
+s = fd.read(5000)
+for l in s.splitlines():
+ if re.search("%s " % trans3, l.strip()):
+ print trans3, 'found'
+ trans3_found = 1
+ elif re.search("%s " % trans2, l.strip()) and trans3_found:
+ print trans2, 'found'
+ trans2_found = 1
+ elif re.search("%s " % trans1, l.strip()) and trans3_found and trans2_found:
+ print trans1, 'found'
+ trans1_found = 1
+ elif re.search("%s " % app1, l.strip()) and trans1_found \
+ and trans2_found and trans3_found:
+ print app1, 'found'
+ break
+ elif re.search("%s " % app2, l.strip()):
+ print app2, 'found'
+ elif not re.search(" no-TYPE ", l.strip()):
+ print 'FAIL: stacking order is wrong'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+
+# cleanup
+os.popen('pkill windowctl')
+time.sleep(1)
+
+sys.exit(ret)
--- tests/functional/test21.py.testdata
+++ tests/functional/test21.py.testdata
+CaseName="Meego_stacking_layer_support"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Some tests for Meego stacking layer support.\n
+\n
+- Test steps\n
+\t- show an application window\n
+\t- show an application window transient to the first one\n
+\t- show an application window transient to the first transient\n
+\t- show an application window transient to the second transient\n
+\t- show an application window\n
+\t- set the first non-transient application window to Meego level 1\n
+\t- set the second non-transient application window to Meego level 1\n
+\t- check correct stacking\n
+\t- set the first non-transient application window to Meego level 2\n
+- Post-conditions\n
+\t- check correct stacking\n"
--- tests/functional/test22.py
+++ tests/functional/test22.py
+#!/usr/bin/python
+
+# Some tests for correct WM_STATE setting (Qt is very fond of it).
+
+#* Test steps
+# * map an application window
+# * check that it is in Normal state
+# * minimise the application window
+# * check that it is in Iconic state
+# * maximise the application window
+# * check that it is in Normal state
+# * unmap the application window
+# * check that it is in Withdrawn state
+# * map the application window
+#* Post-conditions
+# * check that it is in Normal state and stacked on top
+
+import os, re, sys, time
+
+if os.system('mcompositor-test-init.py'):
+ sys.exit(1)
+
+fd = os.popen('windowstack m')
+s = fd.read(5000)
+win_re = re.compile('^0x[0-9a-f]+')
+home_win = 0
+for l in s.splitlines():
+ if re.search(' DESKTOP viewable ', l.strip()):
+ home_win = win_re.match(l.strip()).group()
+
+if home_win == 0:
+ print 'FAIL: desktop not found'
+ sys.exit(1)
+
+# create an application window
+fd = os.popen('windowctl kne')
+app = fd.readline().strip()
+time.sleep(1)
+
+ret = 0
+fd = os.popen("xprop -id %s | grep 'window state' | awk '{print $3}'" % app)
+if fd.readline().strip() != 'Normal':
+ print 'FAIL: app is not in Normal state after mapping it'
+ ret = 1
+
+# minimise the application window
+os.popen("windowctl A %s" % home_win)
+time.sleep(1)
+
+fd = os.popen("xprop -id %s | grep 'window state' | awk '{print $3}'" % app)
+if fd.readline().strip() != 'Iconic':
+ print 'FAIL: app is not in Iconic state'
+ ret = 1
+
+# maximise the application window
+os.popen("windowctl A %s" % app)
+time.sleep(1)
+
+fd = os.popen("xprop -id %s | grep 'window state' | awk '{print $3}'" % app)
+if fd.readline().strip() != 'Normal':
+ print 'FAIL: app is not in Normal state after maximising it'
+ ret = 1
+
+# unmap the application window
+os.popen("windowctl U %s" % app)
+time.sleep(1)
+
+fd = os.popen("xprop -id %s | grep 'window state' | awk '{print $3}'" % app)
+if fd.readline().strip() != 'Withdrawn':
+ print 'FAIL: app is not in Withdrawn state after unmapping it'
+ ret = 1
+
+# map the application window
+os.popen("windowctl M %s" % app)
+time.sleep(1)
+
+fd = os.popen("xprop -id %s | grep 'window state' | awk '{print $3}'" % app)
+if fd.readline().strip() != 'Normal':
+ print 'FAIL: app is not in Normal state after re-mapping it'
+ ret = 1
+
+# check the application window is on top
+fd = os.popen('windowstack m')
+s = fd.read(5000)
+for l in s.splitlines():
+ if re.search('%s ' % home_win, l.strip()):
+ print 'FAIL: app is not on top'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+ elif re.search("%s " % app, l.strip()):
+ print app, 'found'
+ break
+
+# cleanup
+os.popen('pkill windowctl')
+time.sleep(1)
+
+sys.exit(ret)
--- tests/functional/test22.py.testdata
+++ tests/functional/test22.py.testdata
+CaseName="WM_STATE_setting"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Some tests for correct WM_STATE setting.\n
+\n
+- Test steps\n
+\t- map an application window\n
+\t- check that it is in Normal state\n
+\t- minimise the application window\n
+\t- check that it is in Iconic state\n
+\t- maximise the application window\n
+\t- check that it is in Normal state\n
+\t- unmap the application window\n
+\t- check that it is in Withdrawn state\n
+\t- map the application window\n
+- Post-conditions\n
+\t- check that it is in Normal state and stacked on top\n"
--- tests/functional/test3.py
+++ tests/functional/test3.py
@@ -10,11 +10,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowctl kn')
@@ -70,4 +66,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test4.py
+++ tests/functional/test4.py
@@ -10,11 +10,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowctl an')
@@ -41,4 +37,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test5.py
+++ tests/functional/test5.py
@@ -11,11 +11,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
# create two dialogs
@@ -51,4 +47,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test6.py
+++ tests/functional/test6.py
@@ -12,11 +12,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
def rotate_screen(top_edge):
@@ -80,4 +76,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test7.py
+++ tests/functional/test7.py
@@ -14,11 +14,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
# create notification, app, dialog, and input windows
@@ -62,4 +58,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test8.py
+++ tests/functional/test8.py
@@ -11,16 +11,18 @@
# * check that the transient is above the application window
# * iconify the application window
# * activate the transient window
-#* Post-conditions
# * check that the transient is above the application window
+# * create and show a dialog window that is transient for the previous dialog
+# * iconify the application window
+# * activate the application window
+# * check that both transients are above the application window and in order
+# * swap the transiencies of the dialogs
+#* Post-conditions
+# * check that both transients are above the application window and in order
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
fd = os.popen('windowstack m')
@@ -101,8 +103,90 @@
ret = 1
break
+# create a dialog that is transient to the first dialog
+fd = os.popen("windowctl kd %s" % new_win)
+new_dialog = fd.readline().strip()
+time.sleep(1)
+
+# iconify the application
+os.popen("windowctl O %s" % old_win)
+time.sleep(2)
+
+# activate the application (this should raise the dialogs too)
+os.popen("windowctl A %s" % old_win)
+time.sleep(1)
+
+new_dialog_found = new_win_found = 0
+fd = os.popen('windowstack m')
+s = fd.read(5000)
+for l in s.splitlines():
+ if re.search("%s " % new_dialog, l.strip()):
+ print new_dialog, 'found'
+ new_dialog_found = 1
+ elif re.search("%s " % new_win, l.strip()) and new_dialog_found:
+ print new_win, 'found'
+ new_win_found = 1
+ elif re.search("%s " % new_win, l.strip()):
+ print 'FAIL: old dialog is stacked before new dialog'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+ elif re.search("%s " % home_win, l.strip()):
+ print 'FAIL: home is stacked before app'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+ elif re.search("%s " % old_win, l.strip()) and new_dialog_found \
+ and new_win_found:
+ print old_win, 'found'
+ break
+ elif re.search("%s " % old_win, l.strip()):
+ print 'FAIL: app is stacked before a transient dialog'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+
+# swap the transiencies of the dialogs
+# (this introduces a temporary transiency loop)
+os.popen("windowctl t %s %s" % (new_win, new_dialog))
+os.popen("windowctl t %s %s" % (new_dialog, old_win))
+time.sleep(1)
+
+new_dialog_found = new_win_found = 0
+fd = os.popen('windowstack m')
+s = fd.read(5000)
+for l in s.splitlines():
+ if re.search("%s " % new_win, l.strip()):
+ print new_win, 'found'
+ new_win_found = 1
+ elif re.search("%s " % new_dialog, l.strip()) and new_win_found:
+ print new_dialog, 'found'
+ new_dialog_found = 1
+ elif re.search("%s " % new_dialog, l.strip()):
+ print 'FAIL: lower dialog is stacked before the upper dialog'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+ elif re.search("%s " % home_win, l.strip()):
+ print 'FAIL: home is stacked before app'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+ elif re.search("%s " % old_win, l.strip()) and new_dialog_found \
+ and new_win_found:
+ print old_win, 'found'
+ break
+ elif re.search("%s " % old_win, l.strip()):
+ print 'FAIL: app is stacked before a transient dialog'
+ print 'Failed stack:\n', s
+ ret = 1
+ break
+
# cleanup
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/functional/test8.py.testdata
+++ tests/functional/test8.py.testdata
@@ -12,5 +12,11 @@
\t- check that the transient is above the application window\n
\t- iconify the application window\n
\t- activate the transient window\n
+\t- check that the transient is above the application window\n
+\t- create and show a dialog window that is transient for the previous dialog\n
+\t- iconify the application window\n
+\t- activate the application window\n
+\t- check that both transients are above the application window and in order\n
+\t- swap the transiencies of the dialogs\n
- Post-conditions\n
-\t- check that the transient is above the application window\n"
+\t- check that both transients are above the application window and in order\n"
--- tests/functional/test9.py
+++ tests/functional/test9.py
@@ -13,11 +13,7 @@
import os, re, sys, time
-if os.system('/sbin/mcetool --unblank-screen --set-tklock-mode=unlocked --set-inhibit-mode=stay-on'):
- print 'mcetool is missing!'
-
-if os.system('pidof mcompositor'):
- print 'mcompositor is not running'
+if os.system('mcompositor-test-init.py'):
sys.exit(1)
# create two application windows
@@ -81,4 +77,7 @@
os.popen('pkill windowctl')
time.sleep(1)
+if os.system('/usr/bin/gconftool-2 --type bool --set /desktop/meego/notifications/previews_enabled true'):
+ print 'cannot re-enable notifications'
+
sys.exit(ret)
--- tests/windowctl/windowctl.cpp
+++ tests/windowctl/windowctl.cpp
@@ -22,8 +22,7 @@
#include <signal.h>
#include <sys/prctl.h>
-#define WIN_W 864
-#define WIN_H 480
+static int WIN_W, WIN_H;
static Atom win_type_atom, trans_for_atom, workarea_atom;
static int xerror_happened;
@@ -169,6 +168,15 @@
XSync(dpy, False);
}
+static void set_always_mapped (Display *dpy, Window w, int value)
+{
+ long data = value;
+ Atom a = XInternAtom (dpy, "_MEEGOTOUCH_ALWAYS_MAPPED", False);
+ XChangeProperty (dpy, w, a, XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char*)&data, 1);
+ XSync(dpy, False);
+}
+
static void set_decorator_buttons (Display *dpy, Window w)
{
unsigned int data[8] = {0, 0, 100, 100,
@@ -179,6 +187,16 @@
XSync(dpy, False);
}
+static void set_mstatusbar_geom (Display *dpy, Window win, unsigned x,
+ unsigned y, unsigned w, unsigned h)
+{
+ unsigned int data[4] = {x, y, w, h};
+ Atom a = XInternAtom (dpy, "_MEEGOTOUCH_MSTATUSBAR_GEOMETRY", False);
+ XChangeProperty (dpy, win, a, XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char*)&data, 4);
+ XSync(dpy, False);
+}
+
static void set_shaped (Display *dpy, Window w)
{
XRectangle rect = {250, 120, 300, 200};
@@ -288,7 +306,11 @@
visual,
attr_mask | CWColormap | CWBorderPixel, &attrs);
} else {
- attrs.background_pixel = BlackPixel (dpy, 0);
+ XColor color;
+ if (!XParseColor(dpy, DefaultColormap(dpy, 0), "red", &color))
+ attrs.background_pixel = BlackPixel(dpy, 0);
+ else
+ attrs.background_pixel = color.pixel;
w = XCreateWindow(dpy, DefaultRootWindow (dpy), 0, 0,
width, height, 0, input_only ? 0 : CopyFromParent,
input_only ? InputOnly : InputOutput,
@@ -337,7 +359,7 @@
static void print_usage_and_exit(QString& stdOut)
{
#define PROG "windowctl"
- stdOut = "Usage 1: " PROG " [afoemksIchpl](n|d|i|b) [transient for <XID>]\n"
+ stdOut = "Usage 1: " PROG " [afoemksIchpl(j[12])](n|d|i|b) [transient for <XID>]\n"
"a - ARGB (32-bit) window, otherwise 16-bit is used\n"
"f - fullscreen window\n"
"o - override-redirect window\n"
@@ -350,6 +372,8 @@
"h - set _MEEGOTOUCH_DECORATOR_BUTTONS for home and close buttons\n"
"p - claim to support the _NET_WM_PING protocol (but don't)\n"
"l - use InputOnly window class\n"
+ "j1 - set _MEEGOTOUCH_ALWAYS_MAPPED property to 1\n"
+ "j2 - set _MEEGOTOUCH_ALWAYS_MAPPED property to 2\n"
"n - WM_TYPE_NORMAL window (if 'k' is given, that is the first type)\n"
"d - WM_TYPE_DIALOG window\n"
"i - WM_TYPE_INPUT window\n"
@@ -379,7 +403,17 @@
"Usage 5: " PROG " K <name>\n"
"K - run 'pkill <name>'\n"
"Usage 6: " PROG " E [<XID>] (0-10)\n"
- "E - set _MEEGO_STACKING_LAYER of new window / window <XID> to 0-10\n";
+ "E - set _MEEGO_STACKING_LAYER of new window / window <XID> to 0-10\n"
+ "Usage 7: " PROG " J <XID> N\n"
+ "J - set _MEEGOTOUCH_ALWAYS_MAPPED of window <XID> to N (>= 0)\n"
+ "Usage 8: " PROG " X <XID> x y w h\n"
+ "X - set _MEEGOTOUCH_MSTATUSBAR_GEOMETRY of window <XID> to (x y w h)\n"
+ "Usage 9: " PROG " CM <XID> <ClientMessage type name> <window>\n"
+ "CM - send a ClientMessage (where window=<window>) to window <XID> "
+ "(0 assumed that = the root window)\n"
+ "Usage 10: " PROG " D\n"
+ "D - print the display resolution\n"
+ ;
}
static void configure (Display *dpy, char *first, char *second, bool above)
@@ -608,10 +642,10 @@
int argb = 0, fullscreen = 0, override_redirect = 0, decor_buttons = 0,
exit_on_unmap = 1, modal = 0, kde_override = 0, meego_layer = -1,
shaped = 0, initial_iconic = 0, no_focus = 0, support_ping = 0,
- input_only = 0;
+ input_only = 0, always_mapped = -1;
WindowType windowtype = TYPE_INVALID;
- if (args.count() < 1 || args.count() > 4) {
+ if (args.count() < 1 || args.count() > 6) {
print_usage_and_exit(stdOut);
return false;
}
@@ -624,6 +658,9 @@
/* catch X errors */
XSetErrorHandler (error_handler);
+ WIN_W = DisplayWidth(dpy, DefaultScreen(dpy));
+ WIN_H = DisplayHeight(dpy, DefaultScreen(dpy));
+
win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
/*trans_for_atom = XInternAtom(dpy, "WM_TRANSIENT_FOR", False);*/
workarea_atom = XInternAtom(dpy, "_NET_WORKAREA", False);
@@ -761,6 +798,27 @@
stdOut.append("execve() failed!");
return false;
}
+ if (*p == 'j') {
+ if (*(p + 1) == '2') {
+ always_mapped = 2;
+ ++p;
+ } else if (*(p + 1) == '1') {
+ always_mapped = 1;
+ ++p;
+ } else {
+ print_usage_and_exit(stdOut);
+ return false;
+ }
+ continue;
+ }
+ if (*p == 'J') {
+ if (args.count() == 3) {
+ set_always_mapped(dpy,
+ strtol(args.at(1).toAscii().data(), 0, 16),
+ atoi(args.at(2).toAscii().data()));
+ return true;
+ }
+ }
if (*p == 'E') {
if (args.count() == 3) {
set_meego_layer(dpy,
@@ -770,11 +828,45 @@
} else if (args.count() == 2) {
meego_layer = atoi(args.at(1).toAscii().data());
break;
- } else {
- print_usage_and_exit(stdOut);
- return false;
}
}
+ if (*p == 'X') {
+ if (args.count() == 6) {
+ set_mstatusbar_geom(dpy,
+ strtol(args.at(1).toAscii().data(), 0, 16),
+ atoi(args.at(2).toAscii().data()),
+ atoi(args.at(3).toAscii().data()),
+ atoi(args.at(4).toAscii().data()),
+ atoi(args.at(5).toAscii().data()));
+ return true;
+ }
+ }
+ if (*p == 'C' && *(p + 1) == 'M') {
+ if (args.count() != 4) {
+ print_usage_and_exit(stdOut); return false;
+ }
+ Atom a = XInternAtom(dpy, args.at(2).toAscii().data(),
+ False);
+ XEvent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.xclient.type = ClientMessage;
+ ev.xclient.message_type = a;
+ ev.xclient.window = strtol(args.at(3).toAscii().data(),
+ NULL, 16);
+ ev.xclient.format = 32;
+ Window w = strtol(args.at(1).toAscii().data(), NULL, 16);
+ if (w == 0) {
+ w = DefaultRootWindow(dpy);
+ XSendEvent(dpy, w, False, SubstructureRedirectMask, &ev);
+ } else
+ XSendEvent(dpy, w, False, NoEventMask, &ev);
+ XSync(dpy, False);
+ return true;
+ }
+ if (*p == 'D') {
+ printf("%u %u\n", WIN_W, WIN_H);
+ return true;
+ }
if ((command = strchr("NUFCMTAWHSO", *p))) {
if (args.count() != 2) {
print_usage_and_exit(stdOut);
@@ -811,6 +903,8 @@
if (meego_layer >= 0)
set_meego_layer(dpy, w, meego_layer);
+ if (always_mapped >= 0)
+ set_always_mapped(dpy, w, always_mapped);
if (decor_buttons) set_decorator_buttons(dpy, w);
if (shaped) set_shaped(dpy, w);
@@ -922,6 +1016,14 @@
/* our window was unmapped */
exit(0);
}
+#if 0
+ else if (xev.type == ClientMessage) {
+ XClientMessageEvent *e = (XClientMessageEvent*)&xev;
+ printf("0x%lx: ClientMessage %d %ld %ld\n", w,
+ e->message_type,
+ e->data.l[0], e->data.l[1]);
+ }
+#endif
else if (xev.type == ConfigureNotify) {
/*XConfigureEvent *e = (XConfigureEvent*)&xev;*/
}
++++++ meegotouch-compositor.yaml
--- meegotouch-compositor.yaml
+++ meegotouch-compositor.yaml
@@ -1,20 +1,17 @@
Name: meegotouch-compositor
Summary: MeeGo UI Compositing Window Manager
-Version: 0.5.8
+Version: 0.8.0
Release: 1
Group: System/Desktop
License: LGPLv2.1
URL: http://meego.gitorious.org/meegotouch/meegotouch-compositor
Sources:
- - "%{name}-%{version}.tar.bz2"
- - mdecorator.desktop
+ - "%{name}-%{version}.tar.gz"
Patches:
- add_Xext_lib_to_windowctl.patch
- fix_test_compile_issues.patch
- fix_build_on_ARM.patch
- initialize_EGL_library.patch
- - mcompositor-black-screen-gtk.patch
- - re-add_fallback_when_Image_extension_is_not_available.patch
Description: |
This package contains the Direct UI compositing window manager.
PkgConfigBR:
@@ -22,13 +19,13 @@
- QtNetwork
- QtOpenGL
- contextprovider-1.0
- - gl
- meegotouch
- x11
- xcomposite
- xdamage
- xext
- xrender
+ - xrandr
Provides:
- duicompositor >= 0.3.9
- mcompositor >= 0.4.6
@@ -37,6 +34,8 @@
- mcompositor < 0.4.6
Configure: none
Builder: qmake
+ExtraSources:
+ - mdecorator.desktop;%{_sysconfdir}/xdg/autostart
SubPackages:
- Name: devel
Summary: Development files for building mcompositor decorators
++++++ deleted files:
--- mcompositor-black-screen-gtk.patch
--- re-add_fallback_when_Image_extension_is_not_available.patch
More information about the MeeGo-commits
mailing list