summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbdiel Janulgue <abdiel.janulgue@nokia.com>2010-06-30 14:40:05 +0300
committerAbdiel Janulgue <abdiel.janulgue@nokia.com>2010-06-30 14:40:05 +0300
commitccc240d5f2319bfac94bd0226865b3a0f341db3d (patch)
treecf97bf7238d1660ffca7780314f8801537a84532
parentcc89a1b7b363e222f7085cefb4afd078dfee289e (diff)
parent611687a49ab8b61a09cf9026e965ef18eb44d586 (diff)
Merge commit 'refs/merge-requests/17' of git@gitorious.org:maemo-6-ui-framework/duicompositor into integration0.4.10rc1
-rw-r--r--.gitignore7
-rw-r--r--debian/compat2
-rw-r--r--debian/control10
-rw-r--r--debian/mcompositor-functional-tests.dirs2
-rw-r--r--debian/mcompositor-functional-tests.install4
-rw-r--r--mcompositor.pro1
-rw-r--r--tests/functional/createTestXml80
-rw-r--r--tests/functional/functional.pro22
-rw-r--r--tests/functional/setandsuite.testdata14
-rw-r--r--tests/functional/test0.py.testdata10
-rw-r--r--tests/functional/test1.py.testdata10
-rw-r--r--tests/functional/test10.py.testdata14
-rw-r--r--tests/functional/test11.py.testdata13
-rw-r--r--tests/functional/test12.py.testdata15
-rw-r--r--tests/functional/test13.py.testdata10
-rw-r--r--tests/functional/test14.py.testdata11
-rw-r--r--tests/functional/test2.py.testdata10
-rw-r--r--tests/functional/test3.py.testdata10
-rw-r--r--tests/functional/test4.py.testdata10
-rw-r--r--tests/functional/test5.py.testdata11
-rw-r--r--tests/functional/test6.py.testdata12
-rw-r--r--tests/functional/test7.py.testdata14
-rw-r--r--tests/functional/test8.py.testdata13
-rw-r--r--tests/functional/test9.py.testdata13
-rw-r--r--tests/tests.pro5
-rw-r--r--tests/windowctl/windowctl.cpp821
-rw-r--r--tests/windowctl/windowctl.pro18
-rw-r--r--tests/windowstack/windowstack.cpp387
-rw-r--r--tests/windowstack/windowstack.h23
-rw-r--r--tests/windowstack/windowstack.pro13
30 files changed, 1583 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 912b356..5d84894 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,9 +11,16 @@ debian/tmp
debian/duicompositor
debian/duicompositor-dbg
debian/duicompositor-tests
+debian/mcompositor-dbg/
+debian/mcompositor-functional-tests/
+debian/mcompositor/
*.debhelper
+*.debhelper.log
debian/files
debian/*.substvars
tests/ut_duicompositescene/ut_duicompositescene
tests/ut_duisimplewindowframe/ut_duisimplewindowframe
*-stamp
+tests.xml
+decorators/mdecorator/mdecorator
+src/mcompositor
diff --git a/debian/compat b/debian/compat
index b8626c4..7ed6ff8 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-4
+5
diff --git a/debian/control b/debian/control
index 09801c0..0727942 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: mcompositor
Section: x11
Priority: extra
Maintainer: Abdiel Janulgue <abdiel.janulgue@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)
+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
Standards-Version: 3.7.2
Package: mcompositor
@@ -18,3 +18,11 @@ Architecture: any
Depends: mcompositor (=${Source-Version})
Description: MeeGo Touch UI Compositing Window Manager debug symbols
MeeGo Touch UI Compositor debug symbols
+
+Package: mcompositor-functional-tests
+Architecture: any
+Depends: libmeegotouchcore0, ci-testing, meego-env-dimming, meego-env-behave, python, ${shlibs:Depends}
+XB-Maemo-CI-Packages: mcompositor
+XB-Maemo-CI-Stage: fast
+Description: mcompositor functional testcases
+ .
diff --git a/debian/mcompositor-functional-tests.dirs b/debian/mcompositor-functional-tests.dirs
new file mode 100644
index 0000000..2318d6e
--- /dev/null
+++ b/debian/mcompositor-functional-tests.dirs
@@ -0,0 +1,2 @@
+usr/share/mcompositor-functional-tests
+usr/share/meegotouch/testscripts
diff --git a/debian/mcompositor-functional-tests.install b/debian/mcompositor-functional-tests.install
new file mode 100644
index 0000000..9108c7c
--- /dev/null
+++ b/debian/mcompositor-functional-tests.install
@@ -0,0 +1,4 @@
+usr/share/mcompositor-functional-tests
+usr/share/meegotouch/testscripts
+usr/bin/windowctl
+usr/bin/windowstack
diff --git a/mcompositor.pro b/mcompositor.pro
index 129c089..d357095 100644
--- a/mcompositor.pro
+++ b/mcompositor.pro
@@ -3,6 +3,7 @@ CONFIG+=ordered
SUBDIRS = \
decorators \
src \
+ tests \
src.depends=decorators
diff --git a/tests/functional/createTestXml b/tests/functional/createTestXml
new file mode 100644
index 0000000..e54e6e2
--- /dev/null
+++ b/tests/functional/createTestXml
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+INSTALL_PATH=$1
+shift
+TESTCASES=""
+
+. setandsuite.testdata
+
+# remove old version
+if [ -e tests.xml ]; then
+ rm tests.xml
+fi
+
+. setandsuite.testdata
+
+
+#####################################################
+# We'll build up a blocks that are not necessery to
+# the tests.xml to work but sometimes very hand to
+# have them around ..
+#
+# SetPreSteps parser
+#
+if [ -n "${SetPreSteps+x}" ]; then
+ PRESTEPS="\t\t\t<pre_steps>\n\t\t\t\t<step>$SetPreSteps</step>\n\t\t\t</pre_steps>"
+else
+ PRESTEPS=""
+fi
+
+#
+# SetPostSteps parser
+#
+
+if [ -n "${SetPostSteps+x}" ]; then
+ POSTSTEPS="\t\t\t<post_steps>\n\t\t\t\t<step>$SetPostSteps</step>\n\t\t\t</post_steps>"
+else
+ POSTSTEPS=""
+fi
+
+#
+# SetLogFiles parser
+#
+
+if [ -n "${SetLogFiles+x}" ]; then
+ LOGFILES="\t\t\t<get>\n"
+ for LOGFILE in ${SetLogFiles}; do
+ LOGFILES="${LOGFILES}\t\t\t\t<file>$LOGFILE</file>\n"
+ done
+ LOGFILES="${LOGFILES}\t\t\t</get>"
+else
+ LOGFILES=""
+fi
+
+#
+# and then we iterate the testcases and build up
+# xml block for each of them ..
+#
+
+for CASEFILE in $*; do
+ 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"
+ TESTCASES="${TESTCASES}${TESTCASE_TEMPLATE}"
+ fi
+done
+
+#
+# And create the actual whole xml template
+#
+
+TESTSUITE_TEMPLATE="<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<testdefinition version=\"0.1\">\n\t<suite name=\"${SuiteName}\" domain=\"$SuiteDomain\" type=\"$SuiteType\" level=\"$SuiteLevel\">\n\t\t<set name=\"$SetName\" description=\"$SetDescription\" feature=\"$SetFeature\" timeout=\"$SetTimeout\">\n$PRESTEPS\n$TESTCASES\n\t\t\t<environments>\n\t\t\t\t<scratchbox>$SetScratchbox</scratchbox>\n\t\t\t\t<hardware>$SetHardware</hardware>\n\t\t\t</environments>\n$LOGFILES\n$POSTSTEPS\n\t\t</set>\n\t</suite>\n</testdefinition>\n"
+
+#
+# And output it !
+#
+
+echo -e $TESTSUITE_TEMPLATE > tests.xml
+
+exit 0
diff --git a/tests/functional/functional.pro b/tests/functional/functional.pro
new file mode 100644
index 0000000..cd99245
--- /dev/null
+++ b/tests/functional/functional.pro
@@ -0,0 +1,22 @@
+TEMPLATE=subdirs
+
+QMAKE_EXTRA_TARGETS += metadata
+XMLLINT= xmllint --noout --schema /usr/share/test-definition/testdefinition-syntax.xsd --schema /usr/share/test-definition/testdefinition-tm_terms.xsd
+
+SUITENAME=mcompositor-functional-tests
+scripts.path=/usr/share/meegotouch/testscripts/$$SUITENAME
+scripts.files += \
+ test*.py \
+
+
+metadata.commands = ./createTestXml $${scripts.path} $${scripts.files};
+metadata.commands += $$XMLLINT tests.xml
+
+metadata.target=tests.xml
+
+metadata.path=/usr/share/$$SUITENAME
+
+metadata.files += tests.xml
+
+INSTALLS += metadata scripts
+
diff --git a/tests/functional/setandsuite.testdata b/tests/functional/setandsuite.testdata
new file mode 100644
index 0000000..268ab5f
--- /dev/null
+++ b/tests/functional/setandsuite.testdata
@@ -0,0 +1,14 @@
+SuiteDomain="ApplicationFramework"
+SuiteName="MCompositor_Functional_Tests"
+SuiteTimeout="3600"
+SuiteType="Functional"
+SuiteLevel="Component"
+SuiteDescription="Sometime goes here"
+
+SetName="MCompositor_Functional_Tests"
+SetDescription="Bunch of low level tests written in python"
+SetHardware="True"
+SetScratchbox="False"
+SetFeature="NONE"
+SetTimeout="3600"
+SetLogFiles="Eka Toka Kolmas Neljas"
diff --git a/tests/functional/test0.py.testdata b/tests/functional/test0.py.testdata
new file mode 100644
index 0000000..c27349e
--- /dev/null
+++ b/tests/functional/test0.py.testdata
@@ -0,0 +1,10 @@
+CaseName="activate_background_app_and_raise_to_top"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Background application raises on top when activated.\n
+\n
+- Test steps\n
+\t- create new application window\n
+\t- activate a background application\n
+- Post-conditions\n
+\t- background application is stacked above the new application\n"
diff --git a/tests/functional/test1.py.testdata b/tests/functional/test1.py.testdata
new file mode 100644
index 0000000..e7a81b5
--- /dev/null
+++ b/tests/functional/test1.py.testdata
@@ -0,0 +1,10 @@
+CaseName="nothing_can_come_over_modal_dialog"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that we can't raise app over a modal, non-transient dialog.\n
+\n
+- Test steps\n
+\t- create a modal, non-transient dialog\n
+\t- activate application in background\n
+- Post-conditions\n
+\t- dialog should be stacked above the application\n"
diff --git a/tests/functional/test10.py.testdata b/tests/functional/test10.py.testdata
new file mode 100644
index 0000000..f074ac9
--- /dev/null
+++ b/tests/functional/test10.py.testdata
@@ -0,0 +1,14 @@
+CaseName="group_windows_that_are_stacked_together"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that windows belonging to the same group are stacked together.\n
+\n
+- Test steps\n
+\t- show two application windows in the same group\n
+\t- show another application window that is not in the group\n
+\t- raise the desktop\n
+\t- raise the first grouped application window\n
+\t- raise the non-grouped application window\n
+\t- raise the second grouped application window\n
+- Post-conditions\n
+\t- grouped application windows are stacked together above the desktop\n"
diff --git a/tests/functional/test11.py.testdata b/tests/functional/test11.py.testdata
new file mode 100644
index 0000000..3f69dfb
--- /dev/null
+++ b/tests/functional/test11.py.testdata
@@ -0,0 +1,13 @@
+CaseName="unmap_background_window_not_causing_changes_in_stack"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that unmapping a background application does not cause other\n
+changes in the window stack\n
+\n
+- Test steps\n
+\t- show an application window\n
+\t- show another application window\n
+\t- raise the desktop\n
+\t- unmap the application that was opened last\n
+- Post-conditions\n
+\t- remaining application is still stacked below the desktop\n"
diff --git a/tests/functional/test12.py.testdata b/tests/functional/test12.py.testdata
new file mode 100644
index 0000000..7d8a190
--- /dev/null
+++ b/tests/functional/test12.py.testdata
@@ -0,0 +1,15 @@
+CaseName="verify_selective_compositing_usecases"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that selective compositing works in most important cases.\n
+\n
+- Test steps\n
+\t- start test app
+\t- check that test app is not composited
+\t- show a normal (non-fullscreen) application window\n
+\t- check that application is composited (due to the decorator)\n
+\t- iconify the application by raising desktop\n
+\t- check that desktop is not composited\n
+\t- show a non-decorated RGBA application window\n
+- Post-conditions\n
+\t- check that compositing is on (because the topmost window is RGBA)\n"
diff --git a/tests/functional/test13.py.testdata b/tests/functional/test13.py.testdata
new file mode 100644
index 0000000..94f7482
--- /dev/null
+++ b/tests/functional/test13.py.testdata
@@ -0,0 +1,10 @@
+CaseName="verify_window_attributes_are_respected"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE is respected.\n
+\n
+- Test steps\n
+\t- show a non-fullscreen application window\n
+\t- request _NET_WM_STATE _NET_WM_STATE_FULLSCREEN for the window\n
+- Post-conditions\n
+\t- verify that the window now has fullscreen size\n"
diff --git a/tests/functional/test14.py.testdata b/tests/functional/test14.py.testdata
new file mode 100644
index 0000000..bfb41f8
--- /dev/null
+++ b/tests/functional/test14.py.testdata
@@ -0,0 +1,11 @@
+CaseName="verify_removal_of_window_attributes"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that removal of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE is respected.\n
+\n
+- Test steps\n
+\t- show a fullscreen application window\n
+\t- verify that the window has fullscreen size\n
+\t request removal of _NET_WM_STATE_FULLSCREEN for the window\n
+- Post-conditions\n
+\t- verify that the window now has non-fullscreen size (due to decorator)\n"
diff --git a/tests/functional/test2.py.testdata b/tests/functional/test2.py.testdata
new file mode 100644
index 0000000..3bfa16a
--- /dev/null
+++ b/tests/functional/test2.py.testdata
@@ -0,0 +1,10 @@
+CaseName="check_modal_dialog_is_coming_to_top"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that new dialog is stacked above old one.\n
+\n
+- Test steps\n
+\t- create a non-transient dialog\n
+\t- create another non-transient dialog\n
+- Post-conditions\n
+\t- the new dialog should be stacked above the old dialog\n"
diff --git a/tests/functional/test3.py.testdata b/tests/functional/test3.py.testdata
new file mode 100644
index 0000000..9a8f879
--- /dev/null
+++ b/tests/functional/test3.py.testdata
@@ -0,0 +1,10 @@
+CaseName="verify_duihome_activation_and_deactivation"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that duihome can be activated and deactivated\n
+\n
+- Test steps\n
+\t- start test app
+\t- activate duihome
+- Post-conditions
+\t- duihome should be above test app"
diff --git a/tests/functional/test4.py.testdata b/tests/functional/test4.py.testdata
new file mode 100644
index 0000000..d54a8ac
--- /dev/null
+++ b/tests/functional/test4.py.testdata
@@ -0,0 +1,10 @@
+CaseName="verify_support_for_argb_windows"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that ARGB windows can be shown.\n
+\n
+- Test steps\n
+\t- create and show ARGB application window\n
+\t- create and show ARGB dialog window\n
+- Post-conditions\n
+\t- both windows are mapped\n"
diff --git a/tests/functional/test5.py.testdata b/tests/functional/test5.py.testdata
new file mode 100644
index 0000000..a63f92a
--- /dev/null
+++ b/tests/functional/test5.py.testdata
@@ -0,0 +1,11 @@
+CaseName="verify_input_window_is_always_topmost"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that TYPE_INPUT window is stacked above applications and dialogs.\n
+\n
+- Test steps\n
+\t- create and show TYPE_INPUT window\n
+\t- create and show application window\n
+\t- create and show dialog window\n
+- Post-conditions\n
+\t- TYPE_INPUT window is stacked above application and dialog windows.\n"
diff --git a/tests/functional/test6.py.testdata b/tests/functional/test6.py.testdata
new file mode 100644
index 0000000..f77dc9d
--- /dev/null
+++ b/tests/functional/test6.py.testdata
@@ -0,0 +1,12 @@
+CaseName="verify_window_stack_while_rotation"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that window stack stays static while rotating the screen.\n
+\n
+- Test steps\n
+\t- create and show TYPE_INPUT window\n
+\t- create and show application window\n
+\t- create and show dialog window\n
+\t- rotate screen step-by-step and check that window stack says the same\n
+- Post-conditions\n
+\t- None\n"
diff --git a/tests/functional/test7.py.testdata b/tests/functional/test7.py.testdata
new file mode 100644
index 0000000..da16b77
--- /dev/null
+++ b/tests/functional/test7.py.testdata
@@ -0,0 +1,14 @@
+CaseName="verify_notifications_stacking"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that TYPE_NOTIFICATION window is stacked above applications,\n
+dialogs and input windows.\n
+\n
+- Test steps\n
+\t- create and show notification window\n
+\t- create and show application window\n
+\t- create and show dialog window\n
+\t- create and show TYPE_INPUT window\n
+- Post-conditions\n
+\t- Notification window is stacked above application, dialog and input\n
+\t window\n"
diff --git a/tests/functional/test8.py.testdata b/tests/functional/test8.py.testdata
new file mode 100644
index 0000000..01ad1ff
--- /dev/null
+++ b/tests/functional/test8.py.testdata
@@ -0,0 +1,13 @@
+CaseName="verify_transient_dialog_functionality"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that a transient dialog is raised with the application it is\n
+transient for.\n
+\n
+- Test steps\n
+\t- show an application window\n
+\t- create and show a dialog window that is transient for the application\n
+\t- activate the duihome window\n
+\t- activate the application window\n
+- Post-conditions\n
+\t- dialog is above the application window\n"
diff --git a/tests/functional/test9.py.testdata b/tests/functional/test9.py.testdata
new file mode 100644
index 0000000..0a08f7d
--- /dev/null
+++ b/tests/functional/test9.py.testdata
@@ -0,0 +1,13 @@
+CaseName="verify_configure_changes_for_stack_order"
+CaseRequirement="NONE"
+CaseTimeout="360"
+CaseDescription="Check that configure requests for changing stacking order are supported.\n
+\n
+- Test steps\n
+\t- show an application window\n
+\t- show another application window\n
+\t- configure topmost application below the other\n
+\t- configure bottommost application above the other\n
+\t- configure bottommost application to the top (sibling None)\n
+- Post-conditions\n
+\t- stacking order is changed according to the configure requests\n"
diff --git a/tests/tests.pro b/tests/tests.pro
new file mode 100644
index 0000000..241ce81
--- /dev/null
+++ b/tests/tests.pro
@@ -0,0 +1,5 @@
+TEMPLATE=subdirs
+
+SUBDIRS = windowctl \
+ windowstack \
+ functional
diff --git a/tests/windowctl/windowctl.cpp b/tests/windowctl/windowctl.cpp
new file mode 100644
index 0000000..1c9203e
--- /dev/null
+++ b/tests/windowctl/windowctl.cpp
@@ -0,0 +1,821 @@
+/* Simple test program to show and manipulate windows.
+ *
+ * Compiling standalone:
+ * g++ -lQtCore -lX11 -lXrender -lXdamage -Wall -I/usr/include/qt4/QtCore/ -I/usr/include/qt4/ windowctl.cpp -o windowctl
+ *
+ * */
+
+#include <QtCore>
+#include <iostream>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <X11/extensions/Xrender.h>
+#include <X11/extensions/Xdamage.h>
+#include <signal.h>
+#include <sys/prctl.h>
+
+static Atom win_type_atom, trans_for_atom, workarea_atom;
+static int xerror_happened;
+
+static void set_fullscreen (Display *dpy, Window w)
+{
+ Atom wm_state, state_fs;
+
+ wm_state = XInternAtom (dpy, "_NET_WM_STATE", False);
+ state_fs = XInternAtom (dpy, "_NET_WM_STATE_FULLSCREEN", False);
+
+ XChangeProperty (dpy, w, wm_state,
+ XA_ATOM, 32, PropModeAppend,
+ (unsigned char *) &state_fs, 1);
+}
+
+static void set_modal (Display *dpy, Window w)
+{
+ Atom wm_state, state_modal;
+
+ wm_state = XInternAtom (dpy, "_NET_WM_STATE", False);
+ state_modal = XInternAtom (dpy, "_NET_WM_STATE_MODAL", False);
+
+ XChangeProperty (dpy, w, wm_state,
+ XA_ATOM, 32, PropModeAppend,
+ (unsigned char *) &state_modal, 1);
+}
+
+static void set_kde_override (Display *dpy, Window w)
+{
+ Atom kde_override;
+ kde_override = XInternAtom (dpy, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", False);
+ XChangeProperty (dpy, w, win_type_atom,
+ XA_ATOM, 32, PropModeAppend,
+ (unsigned char *) &kde_override, 1);
+}
+
+typedef enum {
+ TYPE_INVALID = 0,
+ TYPE_NORMAL,
+ TYPE_DIALOG,
+ TYPE_INPUT,
+ TYPE_NOTIFICATION
+} WindowType;
+
+static const char *win_type_str[] = {"INVALID",
+ "TYPE_NORMAL", "TYPE_DIALOG", "TYPE_INPUT"};
+
+static void set_window_type (Display *dpy, Window w, WindowType type)
+{
+ Atom type_atom;
+
+ switch (type) {
+ case TYPE_NORMAL:
+ type_atom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+ break;
+ case TYPE_DIALOG:
+ type_atom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
+ break;
+ case TYPE_INPUT:
+ type_atom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_INPUT", False);
+ break;
+ case TYPE_NOTIFICATION:
+ type_atom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False);
+ break;
+ default:
+ return;
+ }
+
+ XChangeProperty (dpy, w, win_type_atom,
+ XA_ATOM, 32, PropModeAppend,
+ (unsigned char *) &type_atom, 1);
+}
+
+#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
+#define _NET_WM_STATE_ADD 1 /* add/set property */
+#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
+
+/* this can be used to set fullscreenness after mapping the window */
+static void toggle_fullscreen (Display *display, Window xwindow, int mode)
+{
+ XClientMessageEvent xclient;
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = xwindow;
+ xclient.message_type = XInternAtom (display, "_NET_WM_STATE", False);
+ xclient.format = 32;
+ xclient.data.l[0] = mode;
+ xclient.data.l[1] = XInternAtom (display, "_NET_WM_STATE_FULLSCREEN", False);
+ xclient.data.l[2] = None;
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (display, DefaultRootWindow (display), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+}
+
+static Bool
+timestamp_predicate (Display *display,
+ XEvent *xevent,
+ XPointer arg)
+{
+ Q_UNUSED(arg);
+ if (xevent->type == PropertyNotify &&
+ xevent->xproperty.window == DefaultRootWindow (display) &&
+ xevent->xproperty.atom == workarea_atom)
+ return True;
+
+ return False;
+}
+
+static Time get_server_time (Display *dpy)
+{
+ XEvent xevent;
+ long data = 0;
+
+ /* zero-length append to get timestamp in the PropertyNotify */
+ XChangeProperty (dpy, DefaultRootWindow (dpy), workarea_atom,
+ XA_CARDINAL, 32, PropModeAppend,
+ (unsigned char*)&data, 0);
+
+ XIfEvent (dpy, &xevent, timestamp_predicate, NULL);
+
+ return xevent.xproperty.time;
+}
+
+static void set_meego_layer (Display *dpy, Window w, int layer)
+{
+ long data = layer;
+ Atom a = XInternAtom (dpy, "_MEEGO_STACKING_LAYER", False);
+ XChangeProperty (dpy, w, a, XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char*)&data, 1);
+ XSync(dpy, False);
+}
+
+static void activate_window (Display *dpy, Window window)
+{
+ XClientMessageEvent xclient;
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = window;
+ xclient.message_type = XInternAtom (dpy, "_NET_ACTIVE_WINDOW", False);
+ xclient.format = 32;
+ xclient.data.l[0] = 1; /* requestor type; we're an app */
+ xclient.data.l[1] = get_server_time (dpy);
+ xclient.data.l[2] = None; /* currently active window */
+ xclient.data.l[3] = 0;
+ xclient.data.l[4] = 0;
+
+ XSendEvent (dpy, DefaultRootWindow (dpy), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+}
+
+static Visual*
+get_argb32_visual (Display *dpy)
+{
+ XVisualInfo * xvi;
+ XVisualInfo templ;
+ int nvi;
+ int i;
+ XRenderPictFormat * format;
+ Visual * visual = NULL;
+
+ templ.depth = 32;
+ templ.c_class = TrueColor;
+
+ if ((xvi = XGetVisualInfo (dpy,
+ VisualDepthMask|VisualClassMask,
+ &templ,
+ &nvi)) == NULL)
+ return NULL;
+
+ for (i = 0; i < nvi; i++)
+ {
+ format = XRenderFindVisualFormat (dpy, xvi[i].visual);
+ if (format->type == PictTypeDirect && format->direct.alphaMask)
+ {
+ /*printf("%s: ARGB visual found\n", __func__);*/
+ visual = xvi[i].visual;
+ break;
+ }
+ }
+
+ XFree (xvi);
+ return visual;
+}
+
+static int error_handler (Display *dpy, XErrorEvent *err)
+{
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+ xerror_happened = 1;
+ return 0;
+}
+
+static Window create_window(Display *dpy, int width, int height,
+ int argb, int fullscreen, Colormap *colormap,
+ int override)
+{
+ Window w;
+ XSetWindowAttributes attrs;
+ int attr_mask = CWOverrideRedirect;
+ attrs.override_redirect = override ? True : False;
+ if (argb) {
+ /* use ARGB window */
+ Visual *visual;
+
+ visual = get_argb32_visual (dpy);
+ *colormap = XCreateColormap (dpy, DefaultRootWindow (dpy),
+ visual, AllocNone);
+ attrs.colormap = *colormap;
+ attrs.border_pixel = BlackPixel (dpy, 0);
+ w = XCreateWindow(dpy, DefaultRootWindow (dpy), 0, 0,
+ width, height, 0, 32,
+ InputOutput,
+ visual,
+ attr_mask | CWColormap | CWBorderPixel, &attrs);
+ } else {
+ attrs.background_pixel = BlackPixel (dpy, 0);
+ w = XCreateWindow(dpy, DefaultRootWindow (dpy), 0, 0,
+ width, height, 0, CopyFromParent, InputOutput,
+ CopyFromParent,
+ attr_mask | CWBackPixel, &attrs);
+ *colormap = DefaultColormap (dpy, 0);
+ }
+ if (fullscreen)
+ set_fullscreen(dpy, w);
+ return w;
+}
+
+static Bool
+wait_predicate (Display *display,
+ XEvent *xevent,
+ XPointer arg)
+{
+ Q_UNUSED(display);
+ if ((xevent->type == MapNotify || xevent->type == UnmapNotify) &&
+ xevent->xmap.window == (Window)arg)
+ return True;
+
+ return False;
+}
+
+static void wait_for_mapnotify(Display *dpy, Window w)
+{
+ XWindowAttributes attrs;
+ XEvent xevent;
+
+ /* first check if it's already mapped */
+ if (!XGetWindowAttributes (dpy, w, &attrs))
+ /* window is not valid */
+ exit(1);
+ if (attrs.map_state == IsViewable)
+ return;
+
+ XIfEvent (dpy, &xevent, wait_predicate, (XPointer)w);
+ if (xevent.type == UnmapNotify)
+ exit(1);
+}
+
+static void print_usage_and_exit(QString& stdOut)
+{
+#define PROG "windowctl"
+ stdOut = "Usage 1: " PROG " [afoemk](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"
+ "e - do not exit on unmapping of the window\n"
+ "m - set _NET_WM_STATE_MODAL (makes sense for dialogs only)\n"
+ "k - set _KDE_NET_WM_WINDOW_TYPE_OVERRIDE\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"
+ "b - WM_TYPE_NOTIFICATION window ('b' is for banner)\n\n"
+ "Usage 2: " PROG " N|U|F|C|M|T|A|W <XID>\n"
+ "N - unfullscreen the window with <XID>\n"
+ "U - unmap the window with <XID>\n"
+ "F - fullscreen the window with <XID>\n"
+ "C - toggle fullscreenness of the window with <XID>\n"
+ "M - map the window with <XID>\n"
+ "T - make the window with <XID> non-transient\n"
+ "A - activate (_NET_ACTIVE_WINDOW) the window with <XID>\n"
+ "W - wait for mapping of the window with <XID>\n\n"
+ "Usage 3: " PROG " t|L|V|G <XID> (<XID>|'None')\n"
+ "t - make the first window transient for the second one\n"
+ "L - configure the first window beLow the second one\n"
+ "V - configure the first window aboVe the second one\n"
+ "G - set the window to window group of the second XID\n"
+ "Usage 4: " PROG " R t|b|l|r\n"
+ "R - rotate Screen.TopEdge to top, bottom, left, or right\n"
+ " (requires context-provide)\n"
+ "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";
+}
+
+static void configure (Display *dpy, char *first, char *second, bool above)
+{
+ Window w = strtol(first, NULL, 16);
+ Window sibling = None;
+ XWindowChanges wc;
+ int mask = CWStackMode;
+
+ if (strcmp(second, "None")) {
+ sibling = strtol(second, NULL, 16);
+ mask |= CWSibling;
+ }
+
+ memset(&wc, 0, sizeof(wc));
+ wc.sibling = sibling;
+ wc.stack_mode = above ? Above : Below;
+ XConfigureWindow(dpy, w, mask, &wc);
+ XSync(dpy, False);
+}
+
+static void set_group (Display *dpy, char *first, char *second)
+{
+ Window w = strtol(first, NULL, 16);
+ XID group = strtol(second, NULL, 16);
+ XWMHints h;
+ memset(&h, 0, sizeof(h));
+
+ h.flags = WindowGroupHint;
+ h.window_group = group;
+ XSetWMHints(dpy, w, &h);
+ XSync(dpy, False);
+}
+
+static void do_command (Display *dpy, char command, Window window,
+ Window target, QString& stdOut)
+{
+ switch (command) {
+ case 'N':
+ toggle_fullscreen(dpy, window, _NET_WM_STATE_REMOVE);
+ break;
+ case 'U':
+ XSync(dpy, False);
+ xerror_happened = 0;
+ XUnmapWindow(dpy, window);
+ XSync(dpy, False);
+ if (!xerror_happened)
+ stdOut = "Unmap succeeded\n";
+ else
+ stdOut = "Unmap failed\n";
+ break;
+ case 'F':
+ toggle_fullscreen(dpy, window, _NET_WM_STATE_ADD);
+ break;
+ case 'C':
+ toggle_fullscreen(dpy, window, _NET_WM_STATE_TOGGLE);
+ break;
+ case 'M':
+ XMapWindow(dpy, window);
+ break;
+ case 'T':
+ XDeleteProperty(dpy, window, trans_for_atom);
+ break;
+ case 'A':
+ activate_window(dpy, window);
+ break;
+ case 'W':
+ wait_for_mapnotify(dpy, window);
+ break;
+ case 't':
+ XSetTransientForHint(dpy, window, target);
+ /*
+ XChangeProperty(dpy, window, trans_for_atom, XA_WINDOW,
+ 32, PropModeReplace,
+ (unsigned char*)&target, 1);
+ */
+ break;
+ default:
+ printf ("unknown command %c\n", command);
+ exit(1);
+ }
+ XFlush(dpy);
+}
+
+#if 0
+static int child_exit_status, child_exit_signal;
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static void sig_child(int n)
+{
+ int status;
+ wait(&status);
+ if (WIFEXITED(status))
+ child_exit_status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ child_exit_signal = WTERMSIG(status);
+}
+#endif
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+extern char** environ;
+
+static pid_t run_pkill(char *arg, QString& stdOut)
+{
+ char buf[30];
+ pid_t pid = -1;
+
+ snprintf(buf, 30, "'%s'", arg);
+ stdOut = buf;
+ if ((pid = fork())) {
+ int status;
+ if (pid == -1)
+ stdOut = "fork() error\n";
+ else
+ wait(&status);
+ } else {
+ const char *args[] = {"/usr/bin/pkill", NULL, NULL};
+ args[1] = arg;
+ execve(args[0], (char**)args, environ);
+ exit(1);
+ }
+ return pid;
+}
+
+static pid_t rotate_screen(char *o, QString& stdOut)
+{
+ pid_t pid;
+ char *orientation;
+ int pipefd[2];
+
+ switch (*o) {
+ case 't':
+ orientation = (char*)"top";
+ break;
+ case 'b':
+ orientation = (char*)"bottom";
+ break;
+ case 'l':
+ orientation = (char*)"left";
+ break;
+ case 'r':
+ orientation = (char*)"right";
+ break;
+ default:
+ return -1;
+ }
+
+ pipe(pipefd);
+
+ if ((pid = fork())) {
+ if (pid == -1) {
+ stdOut = "fork() error\n";
+ return -1;
+ }
+ close(pipefd[0]);
+ sleep(5);
+ kill(pid, SIGKILL);
+ return pid;
+ } else {
+ /*
+ context-provide org.freedesktop.ContextKit.Commander \
+ string Screen.TopEdge left
+ */
+ char *args[] = {(char*)"/usr/bin/context-provide",
+ (char*)"org.freedesktop.ContextKit.Commander",
+ (char*)"string", (char*)"Screen.TopEdge",
+ NULL, NULL};
+ args[4] = orientation;
+ close(pipefd[1]);
+ dup2(pipefd[0], STDIN_FILENO);
+ close(pipefd[0]);
+
+ execve(args[0], args, environ);
+ exit(1);
+ }
+ return -1;
+}
+
+static bool old_main(QStringList& args, QString& stdOut)
+{
+ Display *dpy;
+ Window w;
+ GC green_gc;
+ XColor green_col;
+ Colormap colormap;
+ char green[] = "#00ff00";
+ time_t last_time;
+ int argb = 0, fullscreen = 0, override_redirect = 0,
+ exit_on_unmap = 1, modal = 0, kde_override = 0, meego_layer = -1;
+ WindowType windowtype = TYPE_INVALID;
+
+ if (args.count() < 1 || args.count() > 4) {
+ print_usage_and_exit(stdOut);
+ return false;
+ }
+
+ if (!(dpy = XOpenDisplay(NULL))) {
+ stdOut = "Can't open X display\n";
+ return false;
+ }
+
+ /* catch X errors */
+ XSetErrorHandler (error_handler);
+
+ 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);
+
+ /* receive MapNotifys and UnmapNotifys for root's children,
+ * PropertyChangeMask is needed for get_server_time() */
+ XSelectInput (dpy, DefaultRootWindow (dpy),
+ SubstructureNotifyMask | PropertyChangeMask);
+
+ /*printf ("argc == %d, argv[1] = '%s'\n", argc, argv[1]);*/
+ for (char *p = args.at(0).toAscii().data(); *p; ++p) {
+ char *command;
+ if (*p == 'a') {
+ argb = 1;
+ continue;
+ }
+ if (*p == 'f') {
+ fullscreen = 1;
+ continue;
+ }
+ if (*p == 'm') {
+ modal = 1;
+ continue;
+ }
+ if (*p == 'k') {
+ kde_override = 1;
+ continue;
+ }
+ if (*p == 'd') {
+ windowtype = TYPE_DIALOG;
+ continue;
+ }
+ if (*p == 'n') {
+ windowtype = TYPE_NORMAL;
+ continue;
+ }
+ if (*p == 'i') {
+ windowtype = TYPE_INPUT;
+ continue;
+ }
+ if (*p == 'b') {
+ windowtype = TYPE_NOTIFICATION;
+ continue;
+ }
+ if (*p == 'o') {
+ override_redirect = 1;
+ continue;
+ }
+ if (*p == 't') {
+ if (args.count() != 3) {
+ print_usage_and_exit(stdOut);
+ return false;
+ } else
+ do_command(dpy, *p,
+ strtol(args.at(1).toAscii().data(),
+ NULL, 16),
+ strtol(args.at(2).toAscii().data(),
+ NULL, 16), stdOut);
+ return true;
+ }
+ if (*p == 'L' || *p == 'V') {
+ if (args.count() != 3) {
+ print_usage_and_exit(stdOut);
+ return false;
+ } else {
+ bool mode = (*p == 'V');
+ configure(dpy, args.at(1).toAscii().data(),
+ args.at(2).toAscii().data(), mode);
+ }
+ return true;
+ }
+ if (*p == 'G') {
+ if (args.count() != 3) {
+ print_usage_and_exit(stdOut);
+ return false;
+ } else
+ set_group(dpy, args.at(1).toAscii().data(),
+ args.at(2).toAscii().data());
+ return true;
+ }
+ if (*p == 'e') {
+ exit_on_unmap = 0;
+ continue;
+ }
+ if (*p == 'K') {
+ if (args.count() != 2) {
+ print_usage_and_exit(stdOut);
+ return false;
+ }
+ signal (SIGCHLD, SIG_IGN);
+ run_pkill(args.at(1).toAscii().data(), stdOut);
+ return true;
+ }
+ if (*p == 'R') {
+ if (args.count() != 2) {
+ print_usage_and_exit(stdOut);
+ return false;
+ }
+ signal (SIGCHLD, SIG_IGN);
+ rotate_screen(args.at(1).toAscii().data(), stdOut);
+ return true;
+ }
+ if (*p == 'E') {
+ if (args.count() == 3) {
+ set_meego_layer(dpy,
+ strtol(args.at(1).toAscii().data(), 0, 16),
+ atoi(args.at(2).toAscii().data()));
+ return true;
+ } else if (args.count() == 2) {
+ meego_layer = atoi(args.at(1).toAscii().data());
+ break;
+ } else {
+ print_usage_and_exit(stdOut);
+ return false;
+ }
+ }
+ if ((command = strchr("NUFCMTAW", *p))) {
+ if (args.count() != 2) {
+ print_usage_and_exit(stdOut);
+ return false;
+ } else
+ do_command(dpy, *command,
+ strtol(args.at(1).toAscii().data(),
+ NULL, 16), 0, stdOut);
+ return true;
+ }
+ print_usage_and_exit(stdOut);
+ return false;
+ }
+
+ w = create_window (dpy, 400, 400, argb, fullscreen, &colormap,
+ override_redirect);
+ /* print XID of the window */
+ {
+ char buf[20];
+ snprintf(buf, 19, "0x%lx\n", w);
+ stdOut.append(buf);
+ }
+ signal (SIGCHLD, SIG_IGN);
+ if (fork())
+ return true;
+
+ if (args.count() == 2 && strcmp(args.at(0).toAscii().data(), "E"))
+ XSetTransientForHint(dpy, w,
+ strtol(args.at(1).toAscii().data(),
+ NULL, 16));
+
+ if (modal) set_modal(dpy, w);
+
+ if (meego_layer >= 0)
+ set_meego_layer(dpy, w, meego_layer);
+
+ green_gc = XCreateGC (dpy, w, 0, NULL);
+ XParseColor (dpy, colormap, green, &green_col);
+ XAllocColor (dpy, colormap, &green_col);
+ XSetForeground (dpy, green_gc, green_col.pixel);
+
+ if (kde_override && windowtype == TYPE_NORMAL)
+ set_kde_override(dpy, w);
+ set_window_type(dpy, w, windowtype);
+ if (kde_override && windowtype != TYPE_NORMAL)
+ set_kde_override(dpy, w);
+
+ XSelectInput (dpy, w,
+ ExposureMask | ButtonReleaseMask | ButtonPressMask);
+
+ /* set WM_NAME */
+ {
+ char *wmname[] = {(char*)"windowctl"};
+ XTextProperty text_prop;
+ XStringListToTextProperty (wmname, 1, &text_prop);
+ XSetWMName (dpy, w, &text_prop);
+ }
+ /* set _NET_WM_NAME */
+ {
+ char name[] = "windowctl";
+ Atom a = XInternAtom(dpy, "_NET_WM_NAME", False);
+ Atom utf8_a = XInternAtom(dpy, "UTF8_STRING", False);
+ XChangeProperty(dpy, w, a, utf8_a,
+ 8, PropModeReplace, (unsigned char*)name,
+ sizeof(name));
+ }
+
+ int damage_event, damage_error;
+ XDamageQueryExtension(dpy, &damage_event, &damage_error);
+ static const int damage_ev = damage_event + XDamageNotify;
+ // listen to root window damage
+ XDamageCreate(dpy, DefaultRootWindow(dpy), XDamageReportNonEmpty);
+
+ struct timeval map_time;
+ gettimeofday(&map_time, NULL);
+
+ XMapWindow(dpy, w); /* map the window */
+
+ last_time = time(NULL);
+
+ for (;;) {
+ XEvent xev;
+
+ /*if (XEventsQueued (dpy, QueuedAfterFlush))*/
+ XNextEvent(dpy, &xev);
+
+ if (xev.type == Expose) {
+ char str[200];
+ XTextItem item;
+ int len = snprintf(str, 199,
+ "This is %s%s window 0x%lx",
+ win_type_str[windowtype],
+ override_redirect ? " (OR)" : "",
+ w);
+ item.chars = str;
+ item.nchars = len > 0 ? len : 0;
+ item.delta = 0;
+ item.font = None;
+
+#if 0
+ if (argb) {
+ /* draw background with transparent colour */
+ XImage ximage;
+ ximage.width = 800;
+ ximage.height = 480;
+ ximage.format = ZPixmap;
+ ximage.byte_order = LSBFirst;
+ ximage.bitmap_unit = 32;
+ ximage.bitmap_bit_order = LSBFirst;
+ ximage.bitmap_pad = 32;
+ ximage.depth = 32;
+ ximage.red_mask = 0;
+ ximage.green_mask = 0;
+ ximage.blue_mask = 0;
+ ximage.xoffset = 0;
+ ximage.bits_per_pixel = 32;
+ ximage.bytes_per_line = 800 * 4;
+ ximage.data = calloc (1, 800 * 480 * 4);
+
+ XInitImage (&ximage);
+
+ XPutImage (dpy, w, green_gc, &ximage, 0, 0, 0, 0,
+ 800, 480);
+ free (ximage.data);
+ }
+#endif
+ //draw_rect (dpy, w, green_gc, &green_col, 100, 100);
+ XDrawText (dpy, w, green_gc, 300, 200, &item, 1);
+ }
+ else if (xev.type == PropertyNotify) {
+ }
+ else if (xev.type == ButtonRelease) {
+ /*XButtonEvent *e = (XButtonEvent*)&xev;*/
+ }
+ else if (xev.type == MapNotify) {
+ /*XMapEvent *e = (XMapEvent*)&xev;*/
+ }
+ else if (xev.type == UnmapNotify) {
+ XUnmapEvent *e = (XUnmapEvent*)&xev;
+ if (e->window == w && exit_on_unmap)
+ /* our window was unmapped */
+ exit(0);
+ }
+ else if (xev.type == ConfigureNotify) {
+ /*XConfigureEvent *e = (XConfigureEvent*)&xev;*/
+ }
+ else if (xev.type == damage_ev) {
+ struct timeval dtime;
+ gettimeofday(&dtime, NULL);
+ unsigned int secs = dtime.tv_sec - map_time.tv_sec;
+ unsigned int usecs = dtime.tv_usec - map_time.tv_usec;
+ printf("Damage received, %us and %uus from mapping\n",
+ secs, usecs);
+ /*
+ XDamageSubtract(dpy, ((XDamageNotifyEvent*)&xev)->damage,
+ None, None);
+ */
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ QStringList args;
+ QString stdOut;
+ int ret;
+ for (int i = 1; i < argc; ++i)
+ args.append(QString(argv[i]));
+ ret = old_main(args, stdOut);
+ std::cout << stdOut.toStdString();
+ return ret;
+}
diff --git a/tests/windowctl/windowctl.pro b/tests/windowctl/windowctl.pro
new file mode 100644
index 0000000..0b08e86
--- /dev/null
+++ b/tests/windowctl/windowctl.pro
@@ -0,0 +1,18 @@
+TEMPLATE = app
+TARGET = windowctl
+
+target.path=/usr/bin
+
+QMAKE_CXXFLAGS+= -Wall
+QMAKE_CFLAGS+= -Wall
+
+LIBS+=-lX11 -lXrender -lXdamage
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += .
+
+SOURCES += windowctl.cpp
+
+INSTALLS += \
+ target
diff --git a/tests/windowstack/windowstack.cpp b/tests/windowstack/windowstack.cpp
new file mode 100644
index 0000000..92ef3b5
--- /dev/null
+++ b/tests/windowstack/windowstack.cpp
@@ -0,0 +1,387 @@
+/* utility to print the window stack for top-levels and useful
+ * information for each top-level
+ *
+ * Compiling standalone:
+ * g++ -lQtCore -lX11 -lXcomposite -Wall -DSTANDALONE=1 -I/usr/include/qt4/QtCore/ -I/usr/include/qt4/ windowstack.cpp -o windowstack
+ *
+ * Author: Kimmo H. <kimmo.hamalainen@nokia.com> */
+
+#include <QtCore>
+#include <QDebug>
+#include <QHash>
+#include <iostream>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xcomposite.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int verbose, list_mapped_only;
+
+static Atom class_atom, name_atom, name_atom2, xembed_atom, pid_atom,
+ trans_atom,
+ utf8_string_atom,
+ current_app_atom, win_type_atom, wm_state_atom;
+
+static char *get_atom_prop(Display *dpy, Window w, Atom atom)
+{
+ Atom type;
+ int format, rc;
+ unsigned long items;
+ unsigned long left;
+ Atom *value = 0;
+ char *copy;
+
+ rc = XGetWindowProperty (dpy, w, atom, 0, 1, False,
+ XA_ATOM, &type, &format,
+ &items, &left, (unsigned char**)&value);
+ if (!value || type != XA_ATOM || format == 0 || rc != Success) {
+ copy = strdup("");
+ } else {
+ char *s = XGetAtomName(dpy, *value);
+ if (s) {
+ copy = strdup(s);
+ XFree(s);
+ } else
+ copy = strdup("");
+ }
+ return copy;
+}
+
+static Window get_win_prop(Display *dpy, Window w, Atom atom)
+{
+ Atom type;
+ int format, rc;
+ unsigned long items;
+ unsigned long left;
+ Window *value;
+
+ rc = XGetWindowProperty (dpy, w, atom, 0, 1, False,
+ XA_WINDOW, &type, &format,
+ &items, &left, (unsigned char**)&value);
+ if (type == None || rc != Success)
+ return 0;
+ else
+ {
+ return *value;
+ }
+}
+
+#if 0
+static unsigned long get_card_prop(Display *dpy, Window w, Atom atom)
+{
+ Atom type;
+ int format, rc;
+ unsigned long items;
+ unsigned long left;
+ unsigned long *value;
+
+ rc = XGetWindowProperty (dpy, w, atom, 0, 1, False,
+ XA_CARDINAL, &type, &format,
+ &items, &left, (unsigned char**)&value);
+ if (type == None || rc != Success)
+ return 0;
+ else
+ {
+ return *value;
+ }
+}
+
+static long get_int_prop(Display *dpy, Window w, Atom atom)
+{
+ Atom type;
+ int format, rc;
+ unsigned long items;
+ unsigned long left;
+ unsigned long *value;
+
+ rc = XGetWindowProperty (dpy, w, atom, 0, 1, False,
+ XA_INTEGER, &type, &format,
+ &items, &left, (unsigned char**)&value);
+ if (type == None || rc != Success)
+ return -1;
+ else
+ {
+ return *value;
+ }
+}
+
+static long get_xembed_prop(Display *dpy, Window w)
+{
+ Atom type;
+ int format, rc;
+ unsigned long items;
+ unsigned long left;
+ unsigned long *value;
+
+ rc = XGetWindowProperty (dpy, w, xembed_atom, 0, 2, False,
+ XA_CARDINAL, &type, &format,
+ &items, &left, (unsigned char**)&value);
+ if (type == None || rc != Success)
+ return -1;
+ else
+ {
+ long ret;
+ ret = value[1] & (1 << 0);
+ XFree(value);
+ return ret;
+ }
+}
+#endif
+
+static char *get_str_prop(Display *dpy, Window w, Atom atom)
+{
+ Atom type;
+ int format, rc;
+ unsigned long items;
+ unsigned long left;
+ char *value;
+
+ rc = XGetWindowProperty (dpy, w, atom, 0, 200, False,
+ XA_STRING, &type, &format,
+ &items, &left, (unsigned char**)&value);
+ if (type == None || rc != Success)
+ return NULL;
+ else
+ {
+ char *s = strdup((const char*)value);
+ XFree(value);
+ return s;
+ }
+}
+
+static char *get_utf8_prop(Display *dpy, Window w, Atom atom)
+{
+ Atom type;
+ int format, rc;
+ unsigned long items;
+ unsigned long left;
+ char *value;
+
+ rc = XGetWindowProperty (dpy, w, atom, 0, 200, False,
+ utf8_string_atom, &type, &format,
+ &items, &left, (unsigned char**)&value);
+ if (type == None || rc != Success)
+ return NULL;
+ else
+ {
+ char *s = strdup((const char*)value);
+ XFree(value);
+ return s;
+ }
+}
+
+static const char *get_map_state(int state)
+{
+ switch (state) {
+ case IsUnmapped:
+ return "unmapped";
+ case IsUnviewable:
+ return "unviewable";
+ case IsViewable:
+ return "viewable";
+ default:
+ return "error";
+ }
+}
+
+static int xerror;
+static int ignoring_error_handler (Display *dpy, XErrorEvent *err)
+{
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+ xerror = 1;
+ return 0;
+}
+
+static void print_children(Display *dpy, Window w, int level, QString& stdOut)
+{
+ unsigned int n_children = 0;
+ Window *child_l = NULL;
+ Window root_ret, parent_ret;
+ int i;
+ char *wmclass, *wmname, *wmname2, *wmtype, *wmstate;
+ Window trans_for;
+ char buf[100];
+ XWindowAttributes attrs;
+ /*
+ char xembed_buf[50];
+ long xembed;
+ */
+
+ if (!XQueryTree(dpy, w, &root_ret, &parent_ret,
+ &child_l, &n_children))
+ return;
+
+ wmclass = get_str_prop(dpy, w, class_atom);
+ wmname = get_utf8_prop(dpy, w, name_atom);
+ wmname2 = get_str_prop(dpy, w, name_atom2);
+ wmtype = get_atom_prop(dpy, w, win_type_atom);
+ wmstate = get_atom_prop(dpy, w, wm_state_atom);
+ trans_for = get_win_prop(dpy, w, trans_atom);
+ if (!XGetWindowAttributes(dpy, w, &attrs))
+ return;
+
+ if (trans_for)
+ snprintf(buf, 100, "TR_FOR:0x%lx", trans_for);
+ else
+ buf[0] = '\0';
+
+#if 0
+ xembed = get_xembed_prop(dpy, w);
+ if (xembed != -1)
+ snprintf(xembed_buf, 50, "(XEmbed %ld)", xembed);
+ else
+ xembed_buf[0] = '\0';
+#endif
+
+ /* print children in reverse order to get the top of the
+ * stack first */
+ for (i = n_children - 1; i >= 0; --i) {
+ print_children(dpy, child_l[i], level + 1, stdOut);
+ }
+ XFree(child_l);
+ if (level > 0
+ && (!list_mapped_only || attrs.map_state == IsViewable)) {
+ int type_i;
+ char outbuf[200], indent[10];
+ indent[0] = '\0';
+
+ if (wmtype[0] &&
+ strcmp(wmtype, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE") == 0)
+ type_i = sizeof ("_KDE_NET_WM_WINDOW_TYPE");
+ else
+ type_i = sizeof ("_NET_WM_WINDOW_TYPE");
+ const int state_i = sizeof ("_NET_WM_STATE");
+ for (i = 1; i < level; ++i)
+ strcat(indent, " ");
+ if (verbose) {
+ int x2, y2;
+ unsigned int w2, h2, b2, d2;
+ Window r2;
+ Pixmap pixmap;
+ XSetErrorHandler(ignoring_error_handler);
+ XSync(dpy, False);
+ xerror = 0;
+ pixmap = XCompositeNameWindowPixmap(dpy, w);
+ /* generate X error for invalid pixmap */
+ XGetGeometry(dpy, pixmap, &r2, &x2, &y2, &w2, &h2, &b2, &d2);
+ snprintf(outbuf, 200,
+ "%s0x%lx %s %s %s %s '%s' '%s' %s %s %s %d,%d %dx%dx%d\n",
+ indent, w,
+ wmtype[0] ? wmtype + type_i : "no-TYPE",
+ wmstate[0] ? wmstate + state_i : "no-STATE",
+ get_map_state(attrs.map_state),
+ wmclass ? wmclass : "no-CLASS",
+ wmname2 ? wmname2 : "no-NAME",
+ wmname ? wmname : "no-NNAME",
+ buf[0] ? buf : "no-TR",
+ xerror == 0 ? "redir." : "dir.",
+ attrs.override_redirect ? "OR" : "no-OR",
+ attrs.x, attrs.y,
+ attrs.width, attrs.height, attrs.depth);
+ if (pixmap != None)
+ XFreePixmap(dpy, pixmap);
+ }
+ else
+ snprintf(outbuf, 200,
+ "%s0x%lx %s %s %s '%s' %s %s %d,%d %dx%dx%d\n",
+ indent, w,
+ wmtype[0] ? wmtype + type_i : "no-TYPE",
+ /*wmstate[0] ? wmstate + state_i : "no-STATE",*/
+ get_map_state(attrs.map_state),
+ wmclass ? wmclass : "no-CLASS",
+ wmname2 ? wmname2 : "no-NAME",
+ /*wmname ? wmname : "no-NNAME"*/
+ buf[0] ? buf : "no-TR",
+ attrs.override_redirect ? "OR" : "no-OR",
+ attrs.x, attrs.y,
+ attrs.width, attrs.height, attrs.depth);
+ stdOut.append(outbuf);
+ }
+ if (level == 0) {
+ char buf[100];
+ snprintf(buf, 100,
+ "root 0x%lx, _NET_ACTIVE_WINDOW = 0x%lx\n", w,
+ get_win_prop(dpy, w, current_app_atom));
+ stdOut.append(buf);
+ }
+
+ free(wmclass);
+ free(wmname);
+ free(wmtype);
+ free(wmstate);
+}
+
+static int error_handler (Display *dpy, XErrorEvent *err)
+{
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+ return 0;
+}
+
+static bool old_main(QStringList& args, QString& stdOut)
+{
+ Display *dpy;
+ Window root;
+
+ if (args.count() > 0) {
+ int i;
+ for (i = 0; i < args.count(); ++i) {
+ int match = 0;
+ if (args.at(i).contains(QChar('v'))) {
+ verbose = 1;
+ list_mapped_only = 1;
+ match = 1;
+ }
+ if (args.at(i).contains(QChar('m'))) {
+ list_mapped_only = 1;
+ match = 1;
+ }
+ if (!match) {
+ stdOut = "Usage: windowstack [v][m]\n"
+ "v verbose\n"
+ "m list mapped only\n";
+ return false;
+ }
+ }
+ }
+
+ dpy = XOpenDisplay(NULL);
+ if (dpy == NULL) {
+ stdOut = "couldn't open display\n";
+ return false;
+ }
+ XSetErrorHandler(error_handler);
+
+ class_atom = XInternAtom(dpy, "WM_CLASS", False);
+ name_atom = XInternAtom(dpy, "_NET_WM_NAME", False);
+ name_atom2 = XInternAtom(dpy, "WM_NAME", False);
+ xembed_atom = XInternAtom(dpy, "_XEMBED_INFO", False);
+ pid_atom = XInternAtom(dpy, "_NET_WM_PID", False);
+ trans_atom = XInternAtom(dpy, "WM_TRANSIENT_FOR", False);
+ utf8_string_atom = XInternAtom(dpy, "UTF8_STRING", False);
+ current_app_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
+ wm_state_atom = XInternAtom(dpy, "_NET_WM_STATE", False);
+
+ root = XDefaultRootWindow(dpy);
+
+ print_children(dpy, root, 0, stdOut);
+
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ QStringList args;
+ QString stdOut;
+ int ret;
+ for (int i = 1; i < argc; ++i)
+ args.append(QString(argv[i]));
+ ret = old_main(args, stdOut);
+ std::cout << stdOut.toStdString();
+ return ret;
+}
diff --git a/tests/windowstack/windowstack.h b/tests/windowstack/windowstack.h
new file mode 100644
index 0000000..e9ca28c
--- /dev/null
+++ b/tests/windowstack/windowstack.h
@@ -0,0 +1,23 @@
+#ifndef WINDOWSTACK_H
+#define WINDOWSTACK_H
+
+#include <QObject>
+#include <tasfixtureplugininterface.h>
+#include <QtGui>
+#include <MSceneManager>
+#include <MApplication>
+
+class FixtureWindowstack : public QObject, public TasFixturePluginInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(TasFixturePluginInterface)
+
+public:
+
+ FixtureWindowstack(QObject* parent=0);
+ ~FixtureWindowstack();
+ bool execute(void* objectInstance, QString actionName,
+ QHash<QString, QString> parameters, QString & stdOut);
+};
+
+#endif
diff --git a/tests/windowstack/windowstack.pro b/tests/windowstack/windowstack.pro
new file mode 100644
index 0000000..a31785c
--- /dev/null
+++ b/tests/windowstack/windowstack.pro
@@ -0,0 +1,13 @@
+TEMPLATE = app
+TARGET = windowstack
+
+target.path=/usr/bin
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += .
+
+LIBS += -lXcomposite
+SOURCES += windowstack.cpp
+
+INSTALLS += target