diff options
author | Abdiel Janulgue <abdiel.janulgue@nokia.com> | 2010-06-30 14:40:05 +0300 |
---|---|---|
committer | Abdiel Janulgue <abdiel.janulgue@nokia.com> | 2010-06-30 14:40:05 +0300 |
commit | ccc240d5f2319bfac94bd0226865b3a0f341db3d (patch) | |
tree | cf97bf7238d1660ffca7780314f8801537a84532 | |
parent | cc89a1b7b363e222f7085cefb4afd078dfee289e (diff) | |
parent | 611687a49ab8b61a09cf9026e965ef18eb44d586 (diff) |
Merge commit 'refs/merge-requests/17' of git@gitorious.org:maemo-6-ui-framework/duicompositor into integration0.4.10rc1
30 files changed, 1583 insertions, 2 deletions
@@ -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 |