Merge lp:~lukas-kde/unity8/activateWindows into lp:unity8

Proposed by Lukáš Tinkl
Status: Superseded
Proposed branch: lp:~lukas-kde/unity8/activateWindows
Merge into: lp:unity8
Diff against target: 2494 lines (+1893/-154)
33 files modified
CMakeLists.txt (+1/-1)
debian/control (+6/-4)
debian/copyright (+21/-0)
debian/unity8-private.install (+1/-0)
plugins/CMakeLists.txt (+1/-0)
plugins/Cursor/3rd_party/CMakeLists.txt (+1/-0)
plugins/Cursor/3rd_party/xcursor/CMakeLists.txt (+15/-0)
plugins/Cursor/3rd_party/xcursor/xcursor.c (+968/-0)
plugins/Cursor/3rd_party/xcursor/xcursor.h (+65/-0)
plugins/Cursor/CMakeLists.txt (+28/-0)
plugins/Cursor/Cursor.qml (+12/-0)
plugins/Cursor/CursorImageProvider.cpp (+191/-0)
plugins/Cursor/CursorImageProvider.h (+76/-0)
plugins/Cursor/MousePointer.cpp (+125/-0)
plugins/Cursor/MousePointer.h (+59/-0)
plugins/Cursor/plugin.cpp (+39/-0)
plugins/Cursor/plugin.h (+33/-0)
plugins/Cursor/qmldir (+3/-0)
qml/Greeter/Greeter.qml (+1/-1)
qml/Launcher/Launcher.qml (+1/-0)
qml/Panel/Panel.qml (+1/-0)
qml/Shell.qml (+14/-7)
qml/Stages/DecoratedWindow.qml (+6/-3)
qml/Stages/DesktopStage.qml (+10/-10)
qml/Stages/WindowDecoration.qml (+33/-4)
qml/Stages/WindowResizeArea.qml (+135/-62)
tests/mocks/CMakeLists.txt (+1/-0)
tests/mocks/Cursor/CMakeLists.txt (+1/-0)
tests/mocks/Cursor/Cursor.qml (+20/-0)
tests/mocks/Cursor/qmldir (+2/-0)
tests/qmltests/CMakeLists.txt (+1/-1)
tests/qmltests/Stages/tst_WindowResizeArea.qml (+18/-61)
tests/qmltests/tst_OrientedShell.qml (+4/-0)
To merge this branch: bzr merge lp:~lukas-kde/unity8/activateWindows
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Daniel d'Andrada (community) Needs Fixing
Review via email: mp+274878@code.launchpad.net

This proposal has been superseded by a proposal from 2015-10-19.

Commit message

Correctly activate windows from spread and when clicking their decoration

Description of the change

Activate/focus windows when clicking on their decoration and raise/restore them
when activating from the spread.

* Are there any related MPs required for this MP to build/function as expected? Please list.

No

* Did you perform an exploratory manual test run of your code change and any related functionality?

Yes

* Did you make sure that your branch does not contain spurious tags?

Yes

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

Yes

* If you changed the UI, has there been a design review?

N/A

To post a comment you must log in.
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Please follow the commit message format as explained here: https://wiki.ubuntu.com/Process/Merges/Checklists/Unity8

Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

> Please follow the commit message format as explained here:
> https://wiki.ubuntu.com/Process/Merges/Checklists/Unity8

Should be fine now

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Could you please add qml tests to cover those use cases you mention (ie active focus when clicking on decoration and raise/restore when activation from spread)?

I believe we already have a test for the first one (focus when clicking decoration). We had similar problems in the past before. Should investigate why it passes now even though there's a bug there (maybe it tests only with touches and not with mouse clicks, don't know).

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Oh, and it's worth making lp:~unity-team/unity8/mousePointer from silo 022 a prerequisite as it makes a lot of changes in this code.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> > Please follow the commit message format as explained here:
> > https://wiki.ubuntu.com/Process/Merges/Checklists/Unity8
>
> Should be fine now

Yes, thanks!

lp:~lukas-kde/unity8/activateWindows updated
2006. By Lukáš Tinkl

merge lp:~unity-team/unity8/mousePointer, fix conflicts, move the logic

2007. By Lukáš Tinkl

fix warning

2008. By Lukáš Tinkl

fix immediate grab cursor when just clicking

2009. By Lukáš Tinkl

merge lp:~unity-team/unity8/mousePointer

2010. By Lukáš Tinkl

reaise the window on resize click too

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:2005
http://jenkins.qa.ubuntu.com/job/unity8-ci/6488/
Executed test runs:
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/4701
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-wily-touch/870
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1200
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-wily/516
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/1095
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/1096
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-wily-amd64-ci/727
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-wily-i386-ci/728
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-mako/3794
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/4698
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/4698/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/24339
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-wily-mako/514
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-wily-armhf/870
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-wily-armhf/870/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/24336

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6488/rebuild

review: Needs Fixing (continuous-integration)
lp:~lukas-kde/unity8/activateWindows updated
2011. By Lukáš Tinkl

rename unminimize() to restore()

maintan the focus stack to switch between the apps when an app
is minimized or restored

2012. By Lukáš Tinkl

update the focus stack when the focused app id changes

2013. By Lukáš Tinkl

add a test for clicking to get focus (instead of tap)

simplify the foreach loops

2014. By Lukáš Tinkl

provide shortcuts for common window operations

2015. By Lukáš Tinkl

don't special case for dash, clear and reset the focus stack when we minimizeAll()

2016. By Lukáš Tinkl

add tests for maximize and minimize an app

2017. By Lukáš Tinkl

always focus the first app in the stack, based on recency

2018. By Lukáš Tinkl

add tests for minimizeAll and close

2019. By Lukáš Tinkl

add tests for maximizeLeft/Right

2020. By Lukáš Tinkl

simplify code, minimize delta

2021. By Lukáš Tinkl

merge trunk

2022. By Lukáš Tinkl

merge lp:~mzanetti/unity8/panel-button-fixes

2023. By Lukáš Tinkl

display the maximized window's title in the panel, next to the buttons

2024. By Lukáš Tinkl

merge prereq to fix failing tests

2025. By Lukáš Tinkl

elide the window title in the title bar

2026. By Lukáš Tinkl

silence warnings

2027. By Lukáš Tinkl

fix issues found by mzanetti

additionally properly restore windows from minimized state to the correct
previous state (maximized, maximizedLeft/Right)

2028. By Lukáš Tinkl

fix restoring the apps from spread to the correct state and size

potential fix for the Alt+F4 shortcut problem

2029. By Lukáš Tinkl

fix click to focus

2030. By Lukáš Tinkl

take the panel height into account when semimaximizing windows

2031. By Lukáš Tinkl

add a test to prove smashing all the 4 cursor keys together does nothing :)

2032. By Lukáš Tinkl

make sure to play the minimized animation before switching focus to next

2033. By Lukáš Tinkl

merge trunk

2034. By Lukáš Tinkl

cleanup

2035. By Lukáš Tinkl

cleanup

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-09-30 16:49:52 +0000
+++ CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -57,7 +57,7 @@
57find_package(Qt5Concurrent 5.2 REQUIRED)57find_package(Qt5Concurrent 5.2 REQUIRED)
58find_package(Qt5Sql 5.2 REQUIRED)58find_package(Qt5Sql 5.2 REQUIRED)
5959
60pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application>=8)60pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=9)
6161
62# Standard install paths62# Standard install paths
63include(GNUInstallDirs)63include(GNUInstallDirs)
6464
=== modified file 'debian/control'
--- debian/control 2015-09-23 18:15:40 +0000
+++ debian/control 2015-10-19 14:53:23 +0000
@@ -25,10 +25,11 @@
25 libpay2-dev,25 libpay2-dev,
26 libpulse-dev,26 libpulse-dev,
27 libqmenumodel-dev (>= 0.2.9),27 libqmenumodel-dev (>= 0.2.9),
28 libqt5svg5-dev,
28 libqt5xmlpatterns5-dev,29 libqt5xmlpatterns5-dev,
29 libsystemsettings-dev,30 libsystemsettings-dev,
30 libudev-dev,31 libudev-dev,
31 libunity-api-dev (>= 7.100),32 libunity-api-dev (>= 7.101),
32 libusermetricsoutput1-dev,33 libusermetricsoutput1-dev,
33 libxcb1-dev,34 libxcb1-dev,
34 pkg-config,35 pkg-config,
@@ -87,7 +88,8 @@
87Package: unity888Package: unity8
88Architecture: any89Architecture: any
89Provides: indicator-renderer,90Provides: indicator-renderer,
90Depends: gsettings-desktop-schemas,91Depends: dmz-cursor-theme,
92 gsettings-desktop-schemas,
91 libcap2-bin,93 libcap2-bin,
92 libglib2.0-bin,94 libglib2.0-bin,
93 qmenumodel-qml (>= 0.2.9),95 qmenumodel-qml (>= 0.2.9),
@@ -125,7 +127,7 @@
125 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),127 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
126 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,128 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
127 ubuntu-thumbnailer-impl-0,129 ubuntu-thumbnailer-impl-0,
128 unity-application-impl-8,130 unity-application-impl-9,
129 unity-notifications-impl-3,131 unity-notifications-impl-3,
130 unity-plugin-scopes | unity-scopes-impl,132 unity-plugin-scopes | unity-scopes-impl,
131 unity-scopes-impl-7,133 unity-scopes-impl-7,
@@ -171,7 +173,7 @@
171Depends: ${misc:Depends},173Depends: ${misc:Depends},
172 ${shlibs:Depends},174 ${shlibs:Depends},
173Provides: unity-application-impl,175Provides: unity-application-impl,
174 unity-application-impl-8,176 unity-application-impl-9,
175Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)177Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
176Description: Fake environment for running Unity 8 shell178Description: Fake environment for running Unity 8 shell
177 Provides fake implementations of some QML modules used by Unity 8 shell179 Provides fake implementations of some QML modules used by Unity 8 shell
178180
=== modified file 'debian/copyright'
--- debian/copyright 2014-06-11 15:36:51 +0000
+++ debian/copyright 2015-10-19 14:53:23 +0000
@@ -60,3 +60,24 @@
60 packaging of this file. Please review the following information to60 packaging of this file. Please review the following information to
61 ensure the GNU General Public License version 3.0 requirements will be61 ensure the GNU General Public License version 3.0 requirements will be
62 met: http://www.gnu.org/copyleft/gpl.html.62 met: http://www.gnu.org/copyleft/gpl.html.
63
64Files: plugins/Cursor/3rd_party/xcursor/xcursor.*
65Copyright: 2002 Keith Packard
66License: Keith Packard
67 Permission to use, copy, modify, distribute, and sell this software and its
68 documentation for any purpose is hereby granted without fee, provided that
69 the above copyright notice appear in all copies and that both that
70 copyright notice and this permission notice appear in supporting
71 documentation, and that the name of Keith Packard not be used in
72 advertising or publicity pertaining to distribution of the software without
73 specific, written prior permission. Keith Packard makes no
74 representations about the suitability of this software for any purpose. It
75 is provided "as is" without express or implied warranty.
76 .
77 KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
78 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
79 EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
80 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
81 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
82 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
83 PERFORMANCE OF THIS SOFTWARE.
6384
=== modified file 'debian/unity8-private.install'
--- debian/unity8-private.install 2015-09-02 09:30:32 +0000
+++ debian/unity8-private.install 2015-10-19 14:53:23 +0000
@@ -1,6 +1,7 @@
1usr/lib/*/libunity8-private.*1usr/lib/*/libunity8-private.*
2usr/lib/*/unity8/libUbuntuGestures*2usr/lib/*/unity8/libUbuntuGestures*
3usr/lib/*/unity8/qml/AccountsService3usr/lib/*/unity8/qml/AccountsService
4usr/lib/*/unity8/qml/Cursor
4usr/lib/*/unity8/qml/Dash5usr/lib/*/unity8/qml/Dash
5usr/lib/*/unity8/qml/GlobalShortcut6usr/lib/*/unity8/qml/GlobalShortcut
6usr/lib/*/unity8/qml/Greeter7usr/lib/*/unity8/qml/Greeter
78
=== modified file 'plugins/CMakeLists.txt'
--- plugins/CMakeLists.txt 2015-09-02 09:30:32 +0000
+++ plugins/CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -12,6 +12,7 @@
12endmacro()12endmacro()
1313
14add_subdirectory(AccountsService)14add_subdirectory(AccountsService)
15add_subdirectory(Cursor)
15add_subdirectory(GlobalShortcut)16add_subdirectory(GlobalShortcut)
16add_subdirectory(Greeter)17add_subdirectory(Greeter)
17add_subdirectory(IntegratedLightDM)18add_subdirectory(IntegratedLightDM)
1819
=== added directory 'plugins/Cursor'
=== added directory 'plugins/Cursor/3rd_party'
=== added file 'plugins/Cursor/3rd_party/CMakeLists.txt'
--- plugins/Cursor/3rd_party/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/3rd_party/CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -0,0 +1,1 @@
1add_subdirectory(xcursor)
02
=== added directory 'plugins/Cursor/3rd_party/xcursor'
=== added file 'plugins/Cursor/3rd_party/xcursor/CMakeLists.txt'
--- plugins/Cursor/3rd_party/xcursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/3rd_party/xcursor/CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -0,0 +1,15 @@
1add_definitions(-D_DEFAULT_SOURCE=1)
2
3set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
4
5set(
6 XCURSOR_SOURCES
7
8 xcursor.c
9)
10
11add_library(
12 xcursorloader-static STATIC
13
14 ${XCURSOR_SOURCES}
15)
016
=== added file 'plugins/Cursor/3rd_party/xcursor/xcursor.c'
--- plugins/Cursor/3rd_party/xcursor/xcursor.c 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/3rd_party/xcursor/xcursor.c 2015-10-19 14:53:23 +0000
@@ -0,0 +1,968 @@
1/*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission. Keith Packard makes no
11 * representations about the suitability of this software for any purpose. It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include "xcursor.h"
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <dirent.h>
28
29/*
30 * From libXcursor/include/X11/extensions/Xcursor.h
31 */
32
33#define XcursorTrue 1
34#define XcursorFalse 0
35
36/*
37 * Cursor files start with a header. The header
38 * contains a magic number, a version number and a
39 * table of contents which has type and offset information
40 * for the remaining tables in the file.
41 *
42 * File minor versions increment for compatible changes
43 * File major versions increment for incompatible changes (never, we hope)
44 *
45 * Chunks of the same type are always upward compatible. Incompatible
46 * changes are made with new chunk types; the old data can remain under
47 * the old type. Upward compatible changes can add header data as the
48 * header lengths are specified in the file.
49 *
50 * File:
51 * FileHeader
52 * LISTofChunk
53 *
54 * FileHeader:
55 * CARD32 magic magic number
56 * CARD32 header bytes in file header
57 * CARD32 version file version
58 * CARD32 ntoc number of toc entries
59 * LISTofFileToc toc table of contents
60 *
61 * FileToc:
62 * CARD32 type entry type
63 * CARD32 subtype entry subtype (size for images)
64 * CARD32 position absolute file position
65 */
66
67#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
68
69/*
70 * Current Xcursor version number. Will be substituted by configure
71 * from the version in the libXcursor configure.ac file.
72 */
73
74#define XCURSOR_LIB_MAJOR 1
75#define XCURSOR_LIB_MINOR 1
76#define XCURSOR_LIB_REVISION 13
77#define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \
78 (XCURSOR_LIB_MINOR * 100) + \
79 (XCURSOR_LIB_REVISION))
80
81/*
82 * This version number is stored in cursor files; changes to the
83 * file format require updating this version number
84 */
85#define XCURSOR_FILE_MAJOR 1
86#define XCURSOR_FILE_MINOR 0
87#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
88#define XCURSOR_FILE_HEADER_LEN (4 * 4)
89#define XCURSOR_FILE_TOC_LEN (3 * 4)
90
91typedef struct _XcursorFileToc {
92 XcursorUInt type; /* chunk type */
93 XcursorUInt subtype; /* subtype (size for images) */
94 XcursorUInt position; /* absolute position in file */
95} XcursorFileToc;
96
97typedef struct _XcursorFileHeader {
98 XcursorUInt magic; /* magic number */
99 XcursorUInt header; /* byte length of header */
100 XcursorUInt version; /* file version number */
101 XcursorUInt ntoc; /* number of toc entries */
102 XcursorFileToc *tocs; /* table of contents */
103} XcursorFileHeader;
104
105/*
106 * The rest of the file is a list of chunks, each tagged by type
107 * and version.
108 *
109 * Chunk:
110 * ChunkHeader
111 * <extra type-specific header fields>
112 * <type-specific data>
113 *
114 * ChunkHeader:
115 * CARD32 header bytes in chunk header + type header
116 * CARD32 type chunk type
117 * CARD32 subtype chunk subtype
118 * CARD32 version chunk type version
119 */
120
121#define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
122
123typedef struct _XcursorChunkHeader {
124 XcursorUInt header; /* bytes in chunk header */
125 XcursorUInt type; /* chunk type */
126 XcursorUInt subtype; /* chunk subtype (size for images) */
127 XcursorUInt version; /* version of this type */
128} XcursorChunkHeader;
129
130/*
131 * Here's a list of the known chunk types
132 */
133
134/*
135 * Comments consist of a 4-byte length field followed by
136 * UTF-8 encoded text
137 *
138 * Comment:
139 * ChunkHeader header chunk header
140 * CARD32 length bytes in text
141 * LISTofCARD8 text UTF-8 encoded text
142 */
143
144#define XCURSOR_COMMENT_TYPE 0xfffe0001
145#define XCURSOR_COMMENT_VERSION 1
146#define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
147#define XCURSOR_COMMENT_COPYRIGHT 1
148#define XCURSOR_COMMENT_LICENSE 2
149#define XCURSOR_COMMENT_OTHER 3
150#define XCURSOR_COMMENT_MAX_LEN 0x100000
151
152typedef struct _XcursorComment {
153 XcursorUInt version;
154 XcursorUInt comment_type;
155 char *comment;
156} XcursorComment;
157
158/*
159 * Each cursor image occupies a separate image chunk.
160 * The length of the image header follows the chunk header
161 * so that future versions can extend the header without
162 * breaking older applications
163 *
164 * Image:
165 * ChunkHeader header chunk header
166 * CARD32 width actual width
167 * CARD32 height actual height
168 * CARD32 xhot hot spot x
169 * CARD32 yhot hot spot y
170 * CARD32 delay animation delay
171 * LISTofCARD32 pixels ARGB pixels
172 */
173
174#define XCURSOR_IMAGE_TYPE 0xfffd0002
175#define XCURSOR_IMAGE_VERSION 1
176#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
177#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
178
179typedef struct _XcursorFile XcursorFile;
180
181struct _XcursorFile {
182 void *closure;
183 int (*read) (XcursorFile *file, unsigned char *buf, int len);
184 int (*write) (XcursorFile *file, unsigned char *buf, int len);
185 int (*seek) (XcursorFile *file, long offset, int whence);
186};
187
188typedef struct _XcursorComments {
189 int ncomment; /* number of comments */
190 XcursorComment **comments; /* array of XcursorComment pointers */
191} XcursorComments;
192
193/*
194 * From libXcursor/src/file.c
195 */
196
197static XcursorImage *
198XcursorImageCreate (int width, int height)
199{
200 XcursorImage *image;
201
202 image = malloc (sizeof (XcursorImage) +
203 width * height * sizeof (XcursorPixel));
204 if (!image)
205 return NULL;
206 image->version = XCURSOR_IMAGE_VERSION;
207 image->pixels = (XcursorPixel *) (image + 1);
208 image->size = width > height ? width : height;
209 image->width = width;
210 image->height = height;
211 image->delay = 0;
212 return image;
213}
214
215static void
216XcursorImageDestroy (XcursorImage *image)
217{
218 free (image);
219}
220
221static XcursorImages *
222XcursorImagesCreate (int size)
223{
224 XcursorImages *images;
225
226 images = malloc (sizeof (XcursorImages) +
227 size * sizeof (XcursorImage *));
228 if (!images)
229 return NULL;
230 images->nimage = 0;
231 images->images = (XcursorImage **) (images + 1);
232 images->name = NULL;
233 return images;
234}
235
236void
237XcursorImagesDestroy (XcursorImages *images)
238{
239 int n;
240
241 if (!images)
242 return;
243
244 for (n = 0; n < images->nimage; n++)
245 XcursorImageDestroy (images->images[n]);
246 if (images->name)
247 free (images->name);
248 free (images);
249}
250
251static void
252XcursorImagesSetName (XcursorImages *images, const char *name)
253{
254 char *new;
255
256 if (!images || !name)
257 return;
258
259 new = malloc (strlen (name) + 1);
260
261 if (!new)
262 return;
263
264 strcpy (new, name);
265 if (images->name)
266 free (images->name);
267 images->name = new;
268}
269
270static XcursorBool
271_XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
272{
273 unsigned char bytes[4];
274
275 if (!file || !u)
276 return XcursorFalse;
277
278 if ((*file->read) (file, bytes, 4) != 4)
279 return XcursorFalse;
280 *u = ((bytes[0] << 0) |
281 (bytes[1] << 8) |
282 (bytes[2] << 16) |
283 (bytes[3] << 24));
284 return XcursorTrue;
285}
286
287static void
288_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
289{
290 free (fileHeader);
291}
292
293static XcursorFileHeader *
294_XcursorFileHeaderCreate (int ntoc)
295{
296 XcursorFileHeader *fileHeader;
297
298 if (ntoc > 0x10000)
299 return NULL;
300 fileHeader = malloc (sizeof (XcursorFileHeader) +
301 ntoc * sizeof (XcursorFileToc));
302 if (!fileHeader)
303 return NULL;
304 fileHeader->magic = XCURSOR_MAGIC;
305 fileHeader->header = XCURSOR_FILE_HEADER_LEN;
306 fileHeader->version = XCURSOR_FILE_VERSION;
307 fileHeader->ntoc = ntoc;
308 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
309 return fileHeader;
310}
311
312static XcursorFileHeader *
313_XcursorReadFileHeader (XcursorFile *file)
314{
315 XcursorFileHeader head, *fileHeader;
316 XcursorUInt skip;
317 unsigned int n;
318
319 if (!file)
320 return NULL;
321
322 if (!_XcursorReadUInt (file, &head.magic))
323 return NULL;
324 if (head.magic != XCURSOR_MAGIC)
325 return NULL;
326 if (!_XcursorReadUInt (file, &head.header))
327 return NULL;
328 if (!_XcursorReadUInt (file, &head.version))
329 return NULL;
330 if (!_XcursorReadUInt (file, &head.ntoc))
331 return NULL;
332 skip = head.header - XCURSOR_FILE_HEADER_LEN;
333 if (skip)
334 if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
335 return NULL;
336 fileHeader = _XcursorFileHeaderCreate (head.ntoc);
337 if (!fileHeader)
338 return NULL;
339 fileHeader->magic = head.magic;
340 fileHeader->header = head.header;
341 fileHeader->version = head.version;
342 fileHeader->ntoc = head.ntoc;
343 for (n = 0; n < fileHeader->ntoc; n++)
344 {
345 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
346 break;
347 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
348 break;
349 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
350 break;
351 }
352 if (n != fileHeader->ntoc)
353 {
354 _XcursorFileHeaderDestroy (fileHeader);
355 return NULL;
356 }
357 return fileHeader;
358}
359
360static XcursorBool
361_XcursorSeekToToc (XcursorFile *file,
362 XcursorFileHeader *fileHeader,
363 int toc)
364{
365 if (!file || !fileHeader || \
366 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
367 return XcursorFalse;
368 return XcursorTrue;
369}
370
371static XcursorBool
372_XcursorFileReadChunkHeader (XcursorFile *file,
373 XcursorFileHeader *fileHeader,
374 int toc,
375 XcursorChunkHeader *chunkHeader)
376{
377 if (!file || !fileHeader || !chunkHeader)
378 return XcursorFalse;
379 if (!_XcursorSeekToToc (file, fileHeader, toc))
380 return XcursorFalse;
381 if (!_XcursorReadUInt (file, &chunkHeader->header))
382 return XcursorFalse;
383 if (!_XcursorReadUInt (file, &chunkHeader->type))
384 return XcursorFalse;
385 if (!_XcursorReadUInt (file, &chunkHeader->subtype))
386 return XcursorFalse;
387 if (!_XcursorReadUInt (file, &chunkHeader->version))
388 return XcursorFalse;
389 /* sanity check */
390 if (chunkHeader->type != fileHeader->tocs[toc].type ||
391 chunkHeader->subtype != fileHeader->tocs[toc].subtype)
392 return XcursorFalse;
393 return XcursorTrue;
394}
395
396#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
397
398static XcursorDim
399_XcursorFindBestSize (XcursorFileHeader *fileHeader,
400 XcursorDim size,
401 int *nsizesp)
402{
403 unsigned int n;
404 int nsizes = 0;
405 XcursorDim bestSize = 0;
406 XcursorDim thisSize;
407
408 if (!fileHeader || !nsizesp)
409 return 0;
410
411 for (n = 0; n < fileHeader->ntoc; n++)
412 {
413 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
414 continue;
415 thisSize = fileHeader->tocs[n].subtype;
416 if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
417 {
418 bestSize = thisSize;
419 nsizes = 1;
420 }
421 else if (thisSize == bestSize)
422 nsizes++;
423 }
424 *nsizesp = nsizes;
425 return bestSize;
426}
427
428static int
429_XcursorFindImageToc (XcursorFileHeader *fileHeader,
430 XcursorDim size,
431 int count)
432{
433 unsigned int toc;
434 XcursorDim thisSize;
435
436 if (!fileHeader)
437 return 0;
438
439 for (toc = 0; toc < fileHeader->ntoc; toc++)
440 {
441 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
442 continue;
443 thisSize = fileHeader->tocs[toc].subtype;
444 if (thisSize != size)
445 continue;
446 if (!count)
447 break;
448 count--;
449 }
450 if (toc == fileHeader->ntoc)
451 return -1;
452 return toc;
453}
454
455static XcursorImage *
456_XcursorReadImage (XcursorFile *file,
457 XcursorFileHeader *fileHeader,
458 int toc)
459{
460 XcursorChunkHeader chunkHeader;
461 XcursorImage head;
462 XcursorImage *image;
463 int n;
464 XcursorPixel *p;
465
466 if (!file || !fileHeader)
467 return NULL;
468
469 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
470 return NULL;
471 if (!_XcursorReadUInt (file, &head.width))
472 return NULL;
473 if (!_XcursorReadUInt (file, &head.height))
474 return NULL;
475 if (!_XcursorReadUInt (file, &head.xhot))
476 return NULL;
477 if (!_XcursorReadUInt (file, &head.yhot))
478 return NULL;
479 if (!_XcursorReadUInt (file, &head.delay))
480 return NULL;
481 /* sanity check data */
482 if (head.width >= 0x10000 || head.height > 0x10000)
483 return NULL;
484 if (head.width == 0 || head.height == 0)
485 return NULL;
486 if (head.xhot > head.width || head.yhot > head.height)
487 return NULL;
488
489 /* Create the image and initialize it */
490 image = XcursorImageCreate (head.width, head.height);
491 if (image == NULL)
492 return NULL;
493 if (chunkHeader.version < image->version)
494 image->version = chunkHeader.version;
495 image->size = chunkHeader.subtype;
496 image->xhot = head.xhot;
497 image->yhot = head.yhot;
498 image->delay = head.delay;
499 n = image->width * image->height;
500 p = image->pixels;
501 while (n--)
502 {
503 if (!_XcursorReadUInt (file, p))
504 {
505 XcursorImageDestroy (image);
506 return NULL;
507 }
508 p++;
509 }
510 return image;
511}
512
513static XcursorImages *
514XcursorXcFileLoadImages (XcursorFile *file, int size)
515{
516 XcursorFileHeader *fileHeader;
517 XcursorDim bestSize;
518 int nsize;
519 XcursorImages *images;
520 int n;
521 int toc;
522
523 if (!file || size < 0)
524 return NULL;
525 fileHeader = _XcursorReadFileHeader (file);
526 if (!fileHeader)
527 return NULL;
528 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
529 if (!bestSize)
530 {
531 _XcursorFileHeaderDestroy (fileHeader);
532 return NULL;
533 }
534 images = XcursorImagesCreate (nsize);
535 if (!images)
536 {
537 _XcursorFileHeaderDestroy (fileHeader);
538 return NULL;
539 }
540 for (n = 0; n < nsize; n++)
541 {
542 toc = _XcursorFindImageToc (fileHeader, bestSize, n);
543 if (toc < 0)
544 break;
545 images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
546 toc);
547 if (!images->images[images->nimage])
548 break;
549 images->nimage++;
550 }
551 _XcursorFileHeaderDestroy (fileHeader);
552 if (images->nimage != nsize)
553 {
554 XcursorImagesDestroy (images);
555 images = NULL;
556 }
557 return images;
558}
559
560static int
561_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
562{
563 FILE *f = file->closure;
564 return fread (buf, 1, len, f);
565}
566
567static int
568_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
569{
570 FILE *f = file->closure;
571 return fwrite (buf, 1, len, f);
572}
573
574static int
575_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
576{
577 FILE *f = file->closure;
578 return fseek (f, offset, whence);
579}
580
581static void
582_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
583{
584 file->closure = stdfile;
585 file->read = _XcursorStdioFileRead;
586 file->write = _XcursorStdioFileWrite;
587 file->seek = _XcursorStdioFileSeek;
588}
589
590static XcursorImages *
591XcursorFileLoadImages (FILE *file, int size)
592{
593 XcursorFile f;
594
595 if (!file)
596 return NULL;
597
598 _XcursorStdioFileInitialize (file, &f);
599 return XcursorXcFileLoadImages (&f, size);
600}
601
602/*
603 * From libXcursor/src/library.c
604 */
605
606#ifndef ICONDIR
607#define ICONDIR "/usr/X11R6/lib/X11/icons"
608#endif
609
610#ifndef XCURSORPATH
611#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
612#endif
613
614static const char *
615XcursorLibraryPath (void)
616{
617 static const char *path;
618
619 if (!path)
620 {
621 path = getenv ("XCURSOR_PATH");
622 if (!path)
623 path = XCURSORPATH;
624 }
625 return path;
626}
627
628static void
629_XcursorAddPathElt (char *path, const char *elt, int len)
630{
631 int pathlen = strlen (path);
632
633 /* append / if the path doesn't currently have one */
634 if (path[0] == '\0' || path[pathlen - 1] != '/')
635 {
636 strcat (path, "/");
637 pathlen++;
638 }
639 if (len == -1)
640 len = strlen (elt);
641 /* strip leading slashes */
642 while (len && elt[0] == '/')
643 {
644 elt++;
645 len--;
646 }
647 strncpy (path + pathlen, elt, len);
648 path[pathlen + len] = '\0';
649}
650
651static char *
652_XcursorBuildThemeDir (const char *dir, const char *theme)
653{
654 const char *colon;
655 const char *tcolon;
656 char *full;
657 char *home;
658 int dirlen;
659 int homelen;
660 int themelen;
661 int len;
662
663 if (!dir || !theme)
664 return NULL;
665
666 colon = strchr (dir, ':');
667 if (!colon)
668 colon = dir + strlen (dir);
669
670 dirlen = colon - dir;
671
672 tcolon = strchr (theme, ':');
673 if (!tcolon)
674 tcolon = theme + strlen (theme);
675
676 themelen = tcolon - theme;
677
678 home = NULL;
679 homelen = 0;
680 if (*dir == '~')
681 {
682 home = getenv ("HOME");
683 if (!home)
684 return NULL;
685 homelen = strlen (home);
686 dir++;
687 dirlen--;
688 }
689
690 /*
691 * add space for any needed directory separators, one per component,
692 * and one for the trailing null
693 */
694 len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
695
696 full = malloc (len);
697 if (!full)
698 return NULL;
699 full[0] = '\0';
700
701 if (home)
702 _XcursorAddPathElt (full, home, -1);
703 _XcursorAddPathElt (full, dir, dirlen);
704 _XcursorAddPathElt (full, theme, themelen);
705 return full;
706}
707
708static char *
709_XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
710{
711 char *full;
712
713 if (!dir || !subdir || !file)
714 return NULL;
715
716 full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
717 if (!full)
718 return NULL;
719 full[0] = '\0';
720 _XcursorAddPathElt (full, dir, -1);
721 _XcursorAddPathElt (full, subdir, -1);
722 _XcursorAddPathElt (full, file, -1);
723 return full;
724}
725
726static const char *
727_XcursorNextPath (const char *path)
728{
729 char *colon = strchr (path, ':');
730
731 if (!colon)
732 return NULL;
733 return colon + 1;
734}
735
736#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
737#define XcursorSep(c) ((c) == ';' || (c) == ',')
738
739static char *
740_XcursorThemeInherits (const char *full)
741{
742 char line[8192];
743 char *result = NULL;
744 FILE *f;
745
746 if (!full)
747 return NULL;
748
749 f = fopen (full, "r");
750 if (f)
751 {
752 while (fgets (line, sizeof (line), f))
753 {
754 if (!strncmp (line, "Inherits", 8))
755 {
756 char *l = line + 8;
757 char *r;
758 while (*l == ' ') l++;
759 if (*l != '=') continue;
760 l++;
761 while (*l == ' ') l++;
762 result = malloc (strlen (l) + 1);
763 if (result)
764 {
765 r = result;
766 while (*l)
767 {
768 while (XcursorSep(*l) || XcursorWhite (*l)) l++;
769 if (!*l)
770 break;
771 if (r != result)
772 *r++ = ':';
773 while (*l && !XcursorWhite(*l) &&
774 !XcursorSep(*l))
775 *r++ = *l++;
776 }
777 *r++ = '\0';
778 }
779 break;
780 }
781 }
782 fclose (f);
783 }
784 return result;
785}
786
787static FILE *
788XcursorScanTheme (const char *theme, const char *name)
789{
790 FILE *f = NULL;
791 char *full;
792 char *dir;
793 const char *path;
794 char *inherits = NULL;
795 const char *i;
796
797 if (!theme || !name)
798 return NULL;
799
800 /*
801 * Scan this theme
802 */
803 for (path = XcursorLibraryPath ();
804 path && f == NULL;
805 path = _XcursorNextPath (path))
806 {
807 dir = _XcursorBuildThemeDir (path, theme);
808 if (dir)
809 {
810 full = _XcursorBuildFullname (dir, "cursors", name);
811 if (full)
812 {
813 f = fopen (full, "r");
814 free (full);
815 }
816 if (!f && !inherits)
817 {
818 full = _XcursorBuildFullname (dir, "", "index.theme");
819 if (full)
820 {
821 inherits = _XcursorThemeInherits (full);
822 free (full);
823 }
824 }
825 free (dir);
826 }
827 }
828 /*
829 * Recurse to scan inherited themes
830 */
831 for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
832 f = XcursorScanTheme (i, name);
833 if (inherits != NULL)
834 free (inherits);
835 return f;
836}
837
838XcursorImages *
839XcursorLibraryLoadImages (const char *file, const char *theme, int size)
840{
841 FILE *f = NULL;
842 XcursorImages *images = NULL;
843
844 if (!file)
845 return NULL;
846
847 if (theme)
848 f = XcursorScanTheme (theme, file);
849 if (!f)
850 f = XcursorScanTheme ("default", file);
851 if (f)
852 {
853 images = XcursorFileLoadImages (f, size);
854 if (images)
855 XcursorImagesSetName (images, file);
856 fclose (f);
857 }
858 return images;
859}
860
861static void
862load_all_cursors_from_dir(const char *path, int size,
863 void (*load_callback)(XcursorImages *, void *),
864 void *user_data)
865{
866 FILE *f;
867 DIR *dir = opendir(path);
868 struct dirent *ent;
869 char *full;
870 XcursorImages *images;
871
872 if (!dir)
873 return;
874
875 for(ent = readdir(dir); ent; ent = readdir(dir)) {
876#ifdef _DIRENT_HAVE_D_TYPE
877 if (ent->d_type != DT_UNKNOWN &&
878 (ent->d_type != DT_REG && ent->d_type != DT_LNK))
879 continue;
880#endif
881
882 full = _XcursorBuildFullname(path, "", ent->d_name);
883 if (!full)
884 continue;
885
886 f = fopen(full, "r");
887 if (!f) {
888 free(full);
889 continue;
890 }
891
892 images = XcursorFileLoadImages(f, size);
893
894 if (images) {
895 XcursorImagesSetName(images, ent->d_name);
896 load_callback(images, user_data);
897 }
898
899 fclose (f);
900 free(full);
901 }
902
903 closedir(dir);
904}
905
906/** Load all the cursor of a theme
907 *
908 * This function loads all the cursor images of a given theme and its
909 * inherited themes. Each cursor is loaded into an XcursorImages object
910 * which is passed to the caller's load callback. If a cursor appears
911 * more than once across all the inherited themes, the load callback
912 * will be called multiple times, with possibly different XcursorImages
913 * object which have the same name. The user is expected to destroy the
914 * XcursorImages objects passed to the callback with
915 * XcursorImagesDestroy().
916 *
917 * \param theme The name of theme that should be loaded
918 * \param size The desired size of the cursor images
919 * \param load_callback A callback function that will be called
920 * for each cursor loaded. The first parameter is the XcursorImages
921 * object representing the loaded cursor and the second is a pointer
922 * to data provided by the user.
923 * \param user_data The data that should be passed to the load callback
924 */
925void
926xcursor_load_theme(const char *theme, int size,
927 void (*load_callback)(XcursorImages *, void *),
928 void *user_data)
929{
930 char *full, *dir;
931 char *inherits = NULL;
932 const char *path, *i;
933
934 if (!theme)
935 theme = "default";
936
937 for (path = XcursorLibraryPath();
938 path;
939 path = _XcursorNextPath(path)) {
940 dir = _XcursorBuildThemeDir(path, theme);
941 if (!dir)
942 continue;
943
944 full = _XcursorBuildFullname(dir, "cursors", "");
945
946 if (full) {
947 load_all_cursors_from_dir(full, size, load_callback,
948 user_data);
949 free(full);
950 }
951
952 if (!inherits) {
953 full = _XcursorBuildFullname(dir, "", "index.theme");
954 if (full) {
955 inherits = _XcursorThemeInherits(full);
956 free(full);
957 }
958 }
959
960 free(dir);
961 }
962
963 for (i = inherits; i; i = _XcursorNextPath(i))
964 xcursor_load_theme(i, size, load_callback, user_data);
965
966 if (inherits)
967 free(inherits);
968}
0969
=== added file 'plugins/Cursor/3rd_party/xcursor/xcursor.h'
--- plugins/Cursor/3rd_party/xcursor/xcursor.h 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/3rd_party/xcursor/xcursor.h 2015-10-19 14:53:23 +0000
@@ -0,0 +1,65 @@
1/*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission. Keith Packard makes no
11 * representations about the suitability of this software for any purpose. It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifndef XCURSOR_H
24#define XCURSOR_H
25
26#include <stdint.h>
27
28
29typedef int XcursorBool;
30typedef uint32_t XcursorUInt;
31
32typedef XcursorUInt XcursorDim;
33typedef XcursorUInt XcursorPixel;
34
35typedef struct _XcursorImage {
36 XcursorUInt version; /* version of the image data */
37 XcursorDim size; /* nominal size for matching */
38 XcursorDim width; /* actual width */
39 XcursorDim height; /* actual height */
40 XcursorDim xhot; /* hot spot x (must be inside image) */
41 XcursorDim yhot; /* hot spot y (must be inside image) */
42 XcursorUInt delay; /* animation delay to next frame (ms) */
43 XcursorPixel *pixels; /* pointer to pixels */
44} XcursorImage;
45
46/*
47 * Other data structures exposed by the library API
48 */
49typedef struct _XcursorImages {
50 int nimage; /* number of images */
51 XcursorImage **images; /* array of XcursorImage pointers */
52 char *name; /* name used to load images */
53} XcursorImages;
54
55XcursorImages *
56XcursorLibraryLoadImages (const char *file, const char *theme, int size);
57
58void
59XcursorImagesDestroy (XcursorImages *images);
60
61void
62xcursor_load_theme(const char *theme, int size,
63 void (*load_callback)(XcursorImages *, void *),
64 void *user_data);
65#endif
066
=== added file 'plugins/Cursor/CMakeLists.txt'
--- plugins/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -0,0 +1,28 @@
1add_subdirectory(3rd_party)
2
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/xcursor
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7)
8
9set(QMLPLUGIN_SRC
10 plugin.cpp
11 MousePointer.cpp
12 CursorImageProvider.cpp
13 # We need to run moc on this header
14 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirMousePointerInterface.h
15 )
16
17add_library(Cursor-qml SHARED
18 ${QMLPLUGIN_SRC}
19 )
20
21target_link_libraries(Cursor-qml
22 xcursorloader-static
23 ${QT5PLATFORM_SUPPORT_LDFLAGS}
24)
25
26qt5_use_modules(Cursor-qml Qml Quick DBus Network Gui Sql Concurrent Svg)
27
28add_unity8_plugin(Cursor 1.0 Cursor TARGETS Cursor-qml)
029
=== added file 'plugins/Cursor/Cursor.qml'
--- plugins/Cursor/Cursor.qml 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/Cursor.qml 2015-10-19 14:53:23 +0000
@@ -0,0 +1,12 @@
1import QtQuick 2.4
2import Cursor 1.0 // For MousePointer
3
4MousePointer {
5 id: mousePointer
6
7 Image {
8 x: -mousePointer.hotspotX
9 y: -mousePointer.hotspotY
10 source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
11 }
12}
013
=== added file 'plugins/Cursor/CursorImageProvider.cpp'
--- plugins/Cursor/CursorImageProvider.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/CursorImageProvider.cpp 2015-10-19 14:53:23 +0000
@@ -0,0 +1,191 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "CursorImageProvider.h"
18
19#include <QDebug>
20#include <QFile>
21#include <QPainter>
22#include <QSvgRenderer>
23
24CursorImageProvider *CursorImageProvider::m_instance = nullptr;
25
26/////
27// BuiltInCursorImage
28
29BuiltInCursorImage::BuiltInCursorImage()
30{
31 const char *svgString =
32 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
33 "<svg"
34 " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
35 " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
36 " xmlns:svg=\"http://www.w3.org/2000/svg\""
37 " xmlns=\"http://www.w3.org/2000/svg\""
38 " version=\"1.1\">"
39 " <path"
40 " style=\"fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:40;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\""
41 " d=\"M 20.504,50.94931 460.42533,518.14486 266.47603,515.61948 366.48114,719.16522 274.05218,770.68296 172.53185,559.56112 20.504,716.13476 Z\" />"
42 "</svg>";
43
44 qimage = QImage(20, 32, QImage::Format_ARGB32);
45 QPainter imagePainter(&qimage);
46
47 QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
48 svgRenderer->render(&imagePainter);
49 delete svgRenderer;
50}
51
52/////
53// XCursorImage
54
55XCursorImage::XCursorImage(const QString &theme, const QString &file)
56 : xcursorImages(nullptr)
57{
58 xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme), 32);
59 if (!xcursorImages) {
60 return;
61 }
62
63 bool loaded = false;
64 for (int i = 0; i < xcursorImages->nimage && !loaded; ++i) {
65 XcursorImage *xcursorImage = xcursorImages->images[i];
66 if (xcursorImage->size == 32) {
67
68 qimage = QImage((uchar*)xcursorImage->pixels,
69 xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
70
71 hotspot.setX(xcursorImage->xhot);
72 hotspot.setY(xcursorImage->yhot);
73
74 loaded = true;
75 }
76 }
77}
78
79XCursorImage::~XCursorImage()
80{
81 XcursorImagesDestroy(xcursorImages);
82}
83
84/////
85// CursorImageProvider
86
87CursorImageProvider::CursorImageProvider()
88 : QQuickImageProvider(QQuickImageProvider::Image)
89{
90 if (m_instance) {
91 qFatal("Cannot have multiple CursorImageProvider instances");
92 }
93 m_instance = this;
94}
95
96CursorImageProvider::~CursorImageProvider()
97{
98 {
99 QList< QMap<QString, CursorImage*> > cursorList = m_cursors.values();
100
101 for (int i = 0; i < cursorList.count(); ++i) {
102 QList<CursorImage*> cursorImageList = cursorList[i].values();
103 for (int j = 0; j < cursorImageList.count(); ++j) {
104 delete cursorImageList[j];
105 }
106 }
107 }
108
109 m_cursors.clear();
110 m_instance = nullptr;
111}
112
113QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
114{
115 CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
116 size->setWidth(cursorImage->qimage.width());
117 size->setHeight(cursorImage->qimage.height());
118
119 return cursorImage->qimage;
120}
121
122QPoint CursorImageProvider::hotspot(const QString &themeName, const QString &cursorName)
123{
124 CursorImage *cursorImage = fetchCursor(themeName, cursorName);
125 if (cursorImage) {
126 return cursorImage->hotspot;
127 } else {
128 return QPoint(0,0);
129 }
130}
131
132CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
133{
134 QString themeName;
135 QString cursorName;
136 {
137 QStringList themeAndNameList = cursorThemeAndName.split("/");
138 if (themeAndNameList.size() != 2) {
139 return nullptr;
140 }
141 themeName = themeAndNameList[0];
142 cursorName = themeAndNameList[1];
143 }
144
145 return fetchCursor(themeName, cursorName);
146}
147
148CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
149{
150 CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
151
152 // Try some fallbacks
153 if (cursorImage->qimage.isNull()) {
154 if (cursorName == "ibeam") {
155 qDebug() << "CursorImageProvider: \"ibeam\" not found, falling back to \"xterm\"";
156 cursorImage = fetchCursorHelper(themeName, "xterm");
157 } else if (cursorName == "xterm") {
158 qDebug() << "CursorImageProvider: \"xterm\" not found, falling back to \"ibeam\"";
159 cursorImage = fetchCursorHelper(themeName, "ibeam");
160 }
161 }
162
163 // if it all fails, there must be at least a left_ptr
164 if (cursorImage->qimage.isNull() && cursorName != "left_ptr") {
165 qDebug() << "CursorImageProvider:" << cursorName
166 << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
167 cursorImage = fetchCursorHelper(themeName, "left_ptr");
168 }
169
170 if (cursorImage->qimage.isNull()) {
171 // finally, go for the built-in cursor
172 qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
173 if (!m_builtInCursorImage) {
174 m_builtInCursorImage.reset(new BuiltInCursorImage);
175 }
176 cursorImage = m_builtInCursorImage.data();
177 }
178
179 return cursorImage;
180}
181
182CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
183{
184 QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
185
186 if (!themeCursors.contains(cursorName)) {
187 themeCursors[cursorName] = new XCursorImage(themeName, cursorName);
188 }
189
190 return themeCursors[cursorName];
191}
0192
=== added file 'plugins/Cursor/CursorImageProvider.h'
--- plugins/Cursor/CursorImageProvider.h 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/CursorImageProvider.h 2015-10-19 14:53:23 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef CURSORIMAGEPROVIDER_H
18#define CURSORIMAGEPROVIDER_H
19
20#include <QQuickImageProvider>
21#include <QScopedPointer>
22
23// xcursor static lib
24extern "C"
25{
26#include <xcursor.h>
27}
28
29class CursorImage {
30public:
31 virtual ~CursorImage() {}
32
33 QImage qimage;
34 QPoint hotspot;
35};
36
37class XCursorImage : public CursorImage {
38public:
39 XCursorImage(const QString &theme, const QString &file);
40 virtual ~XCursorImage();
41
42 XcursorImages *xcursorImages;
43};
44
45class BuiltInCursorImage : public CursorImage {
46public:
47 BuiltInCursorImage();
48};
49
50class CursorImageProvider : public QQuickImageProvider
51{
52public:
53 CursorImageProvider();
54 virtual ~CursorImageProvider();
55
56 static CursorImageProvider *instance() { return m_instance; }
57
58
59 QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
60
61 QPoint hotspot(const QString &themeName, const QString &cursorName);
62
63private:
64 CursorImage *fetchCursor(const QString &cursorThemeAndName);
65 CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
66 CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
67
68 // themeName -> (cursorName -> cursorImage)
69 QMap<QString, QMap<QString, CursorImage*> > m_cursors;
70
71 QScopedPointer<CursorImage> m_builtInCursorImage;
72
73 static CursorImageProvider *m_instance;
74};
75
76#endif // CURSORIMAGEPROVIDER_H
077
=== added file 'plugins/Cursor/MousePointer.cpp'
--- plugins/Cursor/MousePointer.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/MousePointer.cpp 2015-10-19 14:53:23 +0000
@@ -0,0 +1,125 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "MousePointer.h"
18#include "CursorImageProvider.h"
19
20// Unity API
21#include <unity/shell/application/MirPlatformCursor.h>
22
23#include <QQuickWindow>
24#include <QGuiApplication>
25
26#include <qpa/qwindowsysteminterface.h>
27
28MousePointer::MousePointer(QQuickItem *parent)
29 : MirMousePointerInterface(parent)
30 , m_cursorName("left_ptr")
31 , m_themeName("default")
32 , m_hotspotX(0)
33 , m_hotspotY(0)
34{
35}
36
37void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
38 Qt::KeyboardModifiers modifiers)
39{
40 if (!parentItem()) {
41 return;
42 }
43
44 qreal newX = x() + movement.x();
45 if (newX < 0) {
46 newX = 0;
47 } else if (newX > parentItem()->width()) {
48 newX = parentItem()->width();
49 }
50 setX(newX);
51
52 qreal newY = y() + movement.y();
53 if (newY < 0) {
54 newY = 0;
55 } else if (newY > parentItem()->height()) {
56 newY = parentItem()->height();
57 }
58 setY(newY);
59
60 QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
61 QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
62 buttons, modifiers);
63}
64
65void MousePointer::itemChange(ItemChange change, const ItemChangeData &value)
66{
67 if (change == ItemSceneChange) {
68 registerWindow(value.window);
69 }
70}
71
72void MousePointer::registerWindow(QWindow *window)
73{
74 if (m_registeredWindow && window != m_registeredWindow) {
75 auto previousCursor = dynamic_cast<MirPlatformCursor*>(m_registeredWindow->screen()->handle()->cursor());
76 if (previousCursor) {
77 previousCursor->setMousePointer(nullptr);
78 } else {
79 qCritical("QPlatformCursor is not a MirPlatformCursor! Cursor module only works in a Mir server.");
80 }
81 }
82
83 m_registeredWindow = window;
84
85 if (m_registeredWindow) {
86 auto cursor = dynamic_cast<MirPlatformCursor*>(window->screen()->handle()->cursor());
87 if (cursor) {
88 cursor->setMousePointer(this);
89 } else {
90 qCritical("QPlaformCursor is not a MirPlatformCursor! Cursor module only works in Mir.");
91 }
92 }
93}
94
95void MousePointer::setCursorName(const QString &cursorName)
96{
97 if (cursorName != m_cursorName) {
98 m_cursorName = cursorName;
99 Q_EMIT cursorNameChanged(m_cursorName);
100 updateHotspot();
101 }
102}
103
104void MousePointer::updateHotspot()
105{
106 QPoint newHotspot = CursorImageProvider::instance()->hotspot(m_themeName, m_cursorName);
107
108 if (m_hotspotX != newHotspot.x()) {
109 m_hotspotX = newHotspot.x();
110 Q_EMIT hotspotXChanged(m_hotspotX);
111 }
112
113 if (m_hotspotY != newHotspot.y()) {
114 m_hotspotY = newHotspot.y();
115 Q_EMIT hotspotYChanged(m_hotspotY);
116 }
117}
118
119void MousePointer::setThemeName(const QString &themeName)
120{
121 if (m_themeName != themeName) {
122 m_themeName = themeName;
123 Q_EMIT themeNameChanged(m_themeName);
124 }
125}
0126
=== added file 'plugins/Cursor/MousePointer.h'
--- plugins/Cursor/MousePointer.h 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/MousePointer.h 2015-10-19 14:53:23 +0000
@@ -0,0 +1,59 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef MOUSEPOINTER_H
18#define MOUSEPOINTER_H
19
20// Qt
21#include <QPointer>
22#include <QWindow>
23
24// Unity API
25#include <unity/shell/application/MirMousePointerInterface.h>
26
27class MousePointer : public MirMousePointerInterface {
28 Q_OBJECT
29public:
30 MousePointer(QQuickItem *parent = nullptr);
31
32 void setCursorName(const QString &qtCursorName) override;
33 QString cursorName() const override { return m_cursorName; }
34
35 void setThemeName(const QString &themeName) override;
36 QString themeName() const override { return m_themeName; }
37
38 qreal hotspotX() const override { return m_hotspotX; }
39 qreal hotspotY() const override { return m_hotspotY; }
40
41public Q_SLOTS:
42 void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
43 Qt::KeyboardModifiers modifiers) override;
44
45protected:
46 void itemChange(ItemChange change, const ItemChangeData &value) override;
47
48private:
49 void registerWindow(QWindow *window);
50 void updateHotspot();
51
52 QPointer<QWindow> m_registeredWindow;
53 QString m_cursorName;
54 QString m_themeName;
55 int m_hotspotX;
56 int m_hotspotY;
57};
58
59#endif // MOUSEPOINTER_H
060
=== added file 'plugins/Cursor/plugin.cpp'
--- plugins/Cursor/plugin.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/plugin.cpp 2015-10-19 14:53:23 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17// Qt
18#include <QtQml/qqml.h>
19#include <QQmlContext>
20
21// self
22#include "plugin.h"
23
24// local
25#include "CursorImageProvider.h"
26#include "MousePointer.h"
27
28void CursorPlugin::registerTypes(const char *uri)
29{
30 Q_ASSERT(uri == QLatin1String("Cursor"));
31 qmlRegisterType<MousePointer>(uri, 1, 0, "MousePointer");
32}
33
34void CursorPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
35{
36 QQmlExtensionPlugin::initializeEngine(engine, uri);
37
38 engine->addImageProvider(QLatin1String("cursor"), new CursorImageProvider());
39}
040
=== added file 'plugins/Cursor/plugin.h'
--- plugins/Cursor/plugin.h 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/plugin.h 2015-10-19 14:53:23 +0000
@@ -0,0 +1,33 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef CURSOR_PLUGIN_H
18#define CURSOR_PLUGIN_H
19
20#include <QtQml/QQmlEngine>
21#include <QtQml/QQmlExtensionPlugin>
22
23class CursorPlugin : public QQmlExtensionPlugin
24{
25 Q_OBJECT
26 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27
28public:
29 void registerTypes(const char *uri) override;
30 void initializeEngine(QQmlEngine *engine, const char *uri) override;
31};
32
33#endif // CURSOR_PLUGIN_H
034
=== added file 'plugins/Cursor/qmldir'
--- plugins/Cursor/qmldir 1970-01-01 00:00:00 +0000
+++ plugins/Cursor/qmldir 2015-10-19 14:53:23 +0000
@@ -0,0 +1,3 @@
1module Cursor
2plugin Cursor-qml
3Cursor 1.0 Cursor.qml
04
=== modified file 'qml/Greeter/Greeter.qml'
--- qml/Greeter/Greeter.qml 2015-09-02 13:06:56 +0000
+++ qml/Greeter/Greeter.qml 2015-10-19 14:53:23 +0000
@@ -251,7 +251,7 @@
251251
252 // event eater252 // event eater
253 // Nothing should leak to items behind the greeter253 // Nothing should leak to items behind the greeter
254 MouseArea { anchors.fill: parent }254 MouseArea { anchors.fill: parent; hoverEnabled: true }
255255
256 Loader {256 Loader {
257 id: loader257 id: loader
258258
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2015-09-02 14:18:40 +0000
+++ qml/Launcher/Launcher.qml 2015-10-19 14:53:23 +0000
@@ -188,6 +188,7 @@
188 bottom: parent.bottom188 bottom: parent.bottom
189 }189 }
190 enabled: root.shadeBackground && root.state == "visible"190 enabled: root.shadeBackground && root.state == "visible"
191 visible: enabled // otherwise it will get in the way of cursor selection for some reason
191 onPressed: {192 onPressed: {
192 root.state = ""193 root.state = ""
193 }194 }
194195
=== modified file 'qml/Panel/Panel.qml'
--- qml/Panel/Panel.qml 2015-06-29 03:58:22 +0000
+++ qml/Panel/Panel.qml 2015-10-19 14:53:23 +0000
@@ -48,6 +48,7 @@
48 MouseArea {48 MouseArea {
49 anchors.fill: parent49 anchors.fill: parent
50 onClicked: if (indicators.fullyOpened) indicators.hide();50 onClicked: if (indicators.fullyOpened) indicators.hide();
51 hoverEnabled: true // should also eat hover events, otherwise they will pass through
51 }52 }
52 }53 }
5354
5455
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2015-09-22 14:23:44 +0000
+++ qml/Shell.qml 2015-10-19 14:53:23 +0000
@@ -41,6 +41,7 @@
41import Unity.Session 0.141import Unity.Session 0.1
42import Unity.DashCommunicator 0.142import Unity.DashCommunicator 0.1
43import Unity.Indicators 0.1 as Indicators43import Unity.Indicators 0.1 as Indicators
44import Cursor 1.0
4445
4546
46Item {47Item {
@@ -63,7 +64,7 @@
63 function updateFocusedAppOrientationAnimated() {64 function updateFocusedAppOrientationAnimated() {
64 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();65 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
65 }66 }
66 property bool hasMouse67 property bool hasMouse: false
6768
68 // to be read from outside69 // to be read from outside
69 readonly property int mainAppWindowOrientationAngle:70 readonly property int mainAppWindowOrientationAngle:
@@ -190,11 +191,6 @@
190 onScreenshotTriggered: screenGrabber.capture();191 onScreenshotTriggered: screenGrabber.capture();
191 }192 }
192193
193 ScreenGrabber {
194 id: screenGrabber
195 z: dialogs.z + 10
196 }
197
198 GlobalShortcut {194 GlobalShortcut {
199 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter195 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
200 }196 }
@@ -684,9 +680,20 @@
684 onShowHome: showHome()680 onShowHome: showHome()
685 }681 }
686682
683 ScreenGrabber {
684 id: screenGrabber
685 z: dialogs.z + 10
686 }
687
688 Cursor {
689 id: cursor
690 visible: shell.hasMouse
691 z: screenGrabber.z + 1
692 }
693
687 Rectangle {694 Rectangle {
688 id: shutdownFadeOutRectangle695 id: shutdownFadeOutRectangle
689 z: screenGrabber.z + 10696 z: cursor.z + 1
690 enabled: false697 enabled: false
691 visible: false698 visible: false
692 color: "black"699 color: "black"
693700
=== modified file 'qml/Stages/DecoratedWindow.qml'
--- qml/Stages/DecoratedWindow.qml 2015-09-08 10:32:28 +0000
+++ qml/Stages/DecoratedWindow.qml 2015-10-19 14:53:23 +0000
@@ -31,9 +31,10 @@
31 property bool highlightShown: false31 property bool highlightShown: false
32 property real shadowOpacity: 132 property real shadowOpacity: 1
3333
34 signal close();34 signal close()
35 signal maximize();35 signal maximize()
36 signal minimize();36 signal minimize()
37 signal decorationPressed()
3738
38 BorderImage {39 BorderImage {
39 anchors {40 anchors {
@@ -61,6 +62,7 @@
6162
62 WindowDecoration {63 WindowDecoration {
63 id: decoration64 id: decoration
65 target: root.parent
64 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"66 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
65 anchors { left: parent.left; top: parent.top; right: parent.right }67 anchors { left: parent.left; top: parent.top; right: parent.right }
66 height: units.gu(3)68 height: units.gu(3)
@@ -68,6 +70,7 @@
68 onClose: root.close();70 onClose: root.close();
69 onMaximize: root.maximize();71 onMaximize: root.maximize();
70 onMinimize: root.minimize();72 onMinimize: root.minimize();
73 onPressed: root.decorationPressed();
71 visible: decorationShown74 visible: decorationShown
72 }75 }
7376
7477
=== modified file 'qml/Stages/DesktopStage.qml'
--- qml/Stages/DesktopStage.qml 2015-09-18 15:28:07 +0000
+++ qml/Stages/DesktopStage.qml 2015-10-19 14:53:23 +0000
@@ -86,6 +86,11 @@
86 var index = indexOf(focusedAppId);86 var index = indexOf(focusedAppId);
87 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null87 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
88 }88 }
89 onFocusedAppDelegateChanged: { // restore the window from minimization when we focus it (e.g. using spread)
90 if (priv.focusedAppDelegate && priv.focusedAppDelegate.minimized) {
91 priv.focusedAppDelegate.unmaximize()
92 }
93 }
8994
90 function indexOf(appId) {95 function indexOf(appId) {
91 for (var i = 0; i < ApplicationManager.count; i++) {96 for (var i = 0; i < ApplicationManager.count; i++) {
@@ -138,9 +143,6 @@
138 height: units.gu(50)143 height: units.gu(50)
139 focus: model.appId === priv.focusedAppId144 focus: model.appId === priv.focusedAppId
140145
141 readonly property int minWidth: units.gu(10)
142 readonly property int minHeight: units.gu(10)
143
144 property bool maximized: false146 property bool maximized: false
145 property bool minimized: false147 property bool minimized: false
146148
@@ -215,15 +217,12 @@
215 when: index == spread.highlightedIndex && blurLayer.ready217 when: index == spread.highlightedIndex && blurLayer.ready
216 }218 }
217219
218 WindowMoveResizeArea {220 WindowResizeArea {
219 id: windowMoveResizeArea
220 target: appDelegate221 target: appDelegate
221 minWidth: appDelegate.minWidth222 minWidth: units.gu(10)
222 minHeight: appDelegate.minHeight223 minHeight: units.gu(10)
223 resizeHandleWidth: units.gu(2)224 borderThickness: units.gu(2)
224 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing225 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
225
226 onPressed: { ApplicationManager.focusApplication(model.appId) }
227 }226 }
228227
229 DecoratedWindow {228 DecoratedWindow {
@@ -240,6 +239,7 @@
240 onClose: ApplicationManager.stopApplication(model.appId)239 onClose: ApplicationManager.stopApplication(model.appId)
241 onMaximize: appDelegate.maximize()240 onMaximize: appDelegate.maximize()
242 onMinimize: appDelegate.minimize()241 onMinimize: appDelegate.minimize()
242 onDecorationPressed: ApplicationManager.focusApplication(model.appId)
243 }243 }
244 }244 }
245 }245 }
246246
=== modified file 'qml/Stages/WindowDecoration.qml'
--- qml/Stages/WindowDecoration.qml 2015-03-13 19:18:35 +0000
+++ qml/Stages/WindowDecoration.qml 2015-10-19 14:53:23 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -12,25 +12,54 @@
12 *12 *
13 * You should have received a copy of the GNU General Public License13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */15 */
1816
19import QtQuick 2.317import QtQuick 2.3
18import Unity.Application 0.1 // For Mir singleton
20import Ubuntu.Components 1.119import Ubuntu.Components 1.1
21import "../Components"20import "../Components"
2221
23Item {22MouseArea {
24 id: root23 id: root
25 clip: true24 clip: true
2625
26 property Item target
27 property alias title: titleLabel.text27 property alias title: titleLabel.text
28 property bool active: false28 property bool active: false
29 hoverEnabled: true
2930
30 signal close()31 signal close()
31 signal minimize()32 signal minimize()
32 signal maximize()33 signal maximize()
3334
35 QtObject {
36 id: priv
37 property real distanceX
38 property real distanceY
39 property bool dragging
40 }
41
42 onPressedChanged: {
43 if (pressed) {
44 var pos = mapToItem(root.target, mouseX, mouseY);
45 priv.distanceX = pos.x;
46 priv.distanceY = pos.y;
47 priv.dragging = true;
48 } else {
49 priv.dragging = false;
50 Mir.cursorName = "";
51 }
52 }
53
54 onPositionChanged: {
55 if (priv.dragging) {
56 Mir.cursorName = "grabbing";
57 var pos = mapToItem(root.target.parent, mouseX, mouseY);
58 root.target.x = pos.x - priv.distanceX;
59 root.target.y = pos.y - priv.distanceY;
60 }
61 }
62
34 Rectangle {63 Rectangle {
35 anchors.fill: parent64 anchors.fill: parent
36 anchors.bottomMargin: -radius65 anchors.bottomMargin: -radius
3766
=== renamed file 'qml/Stages/WindowMoveResizeArea.qml' => 'qml/Stages/WindowResizeArea.qml'
--- qml/Stages/WindowMoveResizeArea.qml 2015-09-08 10:32:28 +0000
+++ qml/Stages/WindowResizeArea.qml 2015-10-19 14:53:23 +0000
@@ -12,18 +12,19 @@
12 *12 *
13 * You should have received a copy of the GNU General Public License13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17 */15 */
1816
19import QtQuick 2.317import QtQuick 2.3
20import Ubuntu.Components 1.118import Ubuntu.Components 1.1
21import Utils 0.119import Utils 0.1
20import Unity.Application 0.1 // for Mir.cursorName
2221
23MouseArea {22MouseArea {
24 id: root23 id: root
25 anchors.fill: target24 anchors.fill: target
26 anchors.margins: -resizeHandleWidth25 anchors.margins: -borderThickness
26
27 hoverEnabled: true
2728
28 property var windowStateStorage: WindowStateStorage29 property var windowStateStorage: WindowStateStorage
2930
@@ -31,24 +32,10 @@
31 // The area will anchor to it and manage move and resize events32 // The area will anchor to it and manage move and resize events
32 property Item target: null33 property Item target: null
33 property string windowId: ""34 property string windowId: ""
34 property int resizeHandleWidth: 035 property int borderThickness: 0
35 property int minWidth: 036 property int minWidth: 0
36 property int minHeight: 037 property int minHeight: 0
3738
38 QtObject {
39 id: priv
40 readonly property int windowWidth: root.width - root.resizeHandleWidth * 2
41 readonly property int windowHeight: root.height - resizeHandleWidth * 2
42
43 property var startPoint
44
45 property bool resizeTop: false
46 property bool resizeBottom: false
47 property bool resizeLeft: false
48 property bool resizeRight: false
49
50 }
51
52 Component.onCompleted: {39 Component.onCompleted: {
53 var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))40 var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
54 if (windowState !== undefined) {41 if (windowState !== undefined) {
@@ -59,51 +46,137 @@
59 }46 }
60 }47 }
6148
62 onPressed: {
63 priv.startPoint = Qt.point(mouse.x, mouse.y);
64 priv.resizeTop = mouseY < root.resizeHandleWidth;
65 priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
66 priv.resizeLeft = mouseX < root.resizeHandleWidth;
67 priv.resizeRight = mouseX > (root.width - root.resizeHandleWidth);
68 }
69
70 onPositionChanged: {
71 var currentPoint = Qt.point(mouse.x, mouse.y);
72 var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
73 var moveDiff = Qt.point(0, 0);
74 var sizeDiff = Qt.point(0, 0);
75 var maxSizeDiff = Qt.point(root.minWidth - root.target.width, root.minHeight - root.target.height)
76
77 if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
78 if (priv.resizeTop) {
79 sizeDiff.y = Math.max(maxSizeDiff.y, -currentPoint.y + priv.startPoint.y)
80 moveDiff.y = -sizeDiff.y
81 }
82 if (priv.resizeBottom) {
83 sizeDiff.y = Math.max(maxSizeDiff.y, currentPoint.y - priv.startPoint.y)
84 priv.startPoint.y += sizeDiff.y
85 }
86 if (priv.resizeLeft) {
87 sizeDiff.x = Math.max(maxSizeDiff.x, -currentPoint.x + priv.startPoint.x)
88 moveDiff.x = -sizeDiff.x
89 }
90 if (priv.resizeRight) {
91 sizeDiff.x = Math.max(maxSizeDiff.x, currentPoint.x - priv.startPoint.x)
92 priv.startPoint.x += sizeDiff.x
93 }
94
95 target.x += moveDiff.x;
96 target.y += moveDiff.y;
97 target.width += sizeDiff.x;
98 target.height += sizeDiff.y;
99 } else {
100 target.x += mouseDiff.x;
101 target.y += mouseDiff.y;
102 }
103
104 }
105
106 Component.onDestruction: {49 Component.onDestruction: {
107 windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))50 windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
108 }51 }
52
53 QtObject {
54 id: d
55 property bool leftBorder: false
56 property bool rightBorder: false
57 property bool topBorder: false
58 property bool bottomBorder: false
59
60 property bool dragging: false
61 property real startMousePosX
62 property real startMousePosY
63 property real startX
64 property real startY
65 property real startWidth
66 property real startHeight
67
68 property string cursorName: {
69 if (root.containsMouse || root.pressed) {
70 if (leftBorder && !topBorder && !bottomBorder) {
71 return "left_side";
72 } else if (rightBorder && !topBorder && !bottomBorder) {
73 return "right_side";
74 } else if (topBorder && !leftBorder && !rightBorder) {
75 return "top_side";
76 } else if (bottomBorder && !leftBorder && !rightBorder) {
77 return "bottom_side";
78 } else if (leftBorder && topBorder) {
79 return "top_left_corner";
80 } else if (leftBorder && bottomBorder) {
81 return "bottom_left_corner";
82 } else if (rightBorder && topBorder) {
83 return "top_right_corner";
84 } else if (rightBorder && bottomBorder) {
85 return "bottom_right_corner";
86 } else {
87 return "";
88 }
89 } else {
90 return "";
91 }
92 }
93 onCursorNameChanged: {
94 Mir.cursorName = cursorName;
95 }
96
97 function updateBorders() {
98 leftBorder = mouseX <= borderThickness;
99 rightBorder = mouseX >= width - borderThickness;
100 topBorder = mouseY <= borderThickness;
101 bottomBorder = mouseY >= height - borderThickness;
102 }
103 }
104
105 onPressedChanged: {
106 var pos = mapToItem(target.parent, mouseX, mouseY);
107
108 if (pressed) {
109 d.updateBorders();
110 var pos = mapToItem(root.target.parent, mouseX, mouseY);
111 d.startMousePosX = pos.x;
112 d.startMousePosY = pos.y;
113 d.startX = target.x;
114 d.startY = target.y;
115 d.startWidth = target.width;
116 d.startHeight = target.height;
117 d.dragging = true;
118 } else {
119 d.dragging = false;
120 if (containsMouse) {
121 d.updateBorders();
122 }
123 }
124 }
125
126 onEntered: {
127 if (!pressed) {
128 d.updateBorders();
129 }
130 }
131
132 onPositionChanged: {
133 if (!pressed) {
134 d.updateBorders();
135 }
136
137 if (!d.dragging) {
138 return;
139 }
140
141 var pos = mapToItem(target.parent, mouse.x, mouse.y);
142
143 var deltaX = pos.x - d.startMousePosX;
144 var deltaY = pos.y - d.startMousePosY;
145
146 if (d.leftBorder) {
147 var newTargetX = d.startX + deltaX;
148 if (target.x + target.width > newTargetX + minWidth) {
149 target.width = target.x + target.width - newTargetX;
150 target.x = newTargetX;
151 } else {
152 target.x = target.x + target.width - minWidth;
153 target.width = minWidth;
154 }
155
156 } else if (d.rightBorder) {
157 if (d.startWidth + deltaX >= minWidth) {
158 target.width = d.startWidth + deltaX;
159 } else {
160 target.width = minWidth;
161 }
162 }
163
164 if (d.topBorder) {
165 var newTargetY = d.startY + deltaY;
166 if (target.y + target.height > newTargetY + minHeight) {
167 target.height = target.y + target.height - newTargetY;
168 target.y = newTargetY;
169 } else {
170 target.y = target.y + target.height - minHeight;
171 target.height = minHeight;
172 }
173
174 } else if (d.bottomBorder) {
175 if (d.startHeight + deltaY >= minHeight) {
176 target.height = d.startHeight + deltaY;
177 } else {
178 target.height = minHeight;
179 }
180 }
181 }
109}182}
110183
=== modified file 'tests/mocks/CMakeLists.txt'
--- tests/mocks/CMakeLists.txt 2015-08-06 22:45:56 +0000
+++ tests/mocks/CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -29,6 +29,7 @@
29endmacro()29endmacro()
3030
31add_subdirectory(AccountsService)31add_subdirectory(AccountsService)
32add_subdirectory(Cursor)
32add_subdirectory(GSettings.1.0)33add_subdirectory(GSettings.1.0)
33add_subdirectory(indicator-service)34add_subdirectory(indicator-service)
34add_subdirectory(libusermetrics)35add_subdirectory(libusermetrics)
3536
=== added directory 'tests/mocks/Cursor'
=== added file 'tests/mocks/Cursor/CMakeLists.txt'
--- tests/mocks/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/mocks/Cursor/CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -0,0 +1,1 @@
1add_unity8_mock(Cursor 1.0 Cursor PREFIX mocks)
02
=== added file 'tests/mocks/Cursor/Cursor.qml'
--- tests/mocks/Cursor/Cursor.qml 1970-01-01 00:00:00 +0000
+++ tests/mocks/Cursor/Cursor.qml 2015-10-19 14:53:23 +0000
@@ -0,0 +1,20 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18
19Item {
20}
021
=== added file 'tests/mocks/Cursor/qmldir'
--- tests/mocks/Cursor/qmldir 1970-01-01 00:00:00 +0000
+++ tests/mocks/Cursor/qmldir 2015-10-19 14:53:23 +0000
@@ -0,0 +1,2 @@
1module Cursor
2Cursor 1.0 Cursor.qml
03
=== modified file 'tests/qmltests/CMakeLists.txt'
--- tests/qmltests/CMakeLists.txt 2015-09-19 07:37:52 +0000
+++ tests/qmltests/CMakeLists.txt 2015-10-19 14:53:23 +0000
@@ -78,7 +78,7 @@
78add_unity8_qmltest(Stages SurfaceContainer)78add_unity8_qmltest(Stages SurfaceContainer)
79add_unity8_qmltest(Stages SessionContainer)79add_unity8_qmltest(Stages SessionContainer)
80add_unity8_qmltest(Stages TabletStage)80add_unity8_qmltest(Stages TabletStage)
81add_unity8_qmltest(Stages WindowMoveResizeArea)81add_unity8_qmltest(Stages WindowResizeArea)
82add_unity8_qmltest(Stages Splash)82add_unity8_qmltest(Stages Splash)
83add_unity8_qmltest(Tutorial Tutorial LIGHTDM)83add_unity8_qmltest(Tutorial Tutorial LIGHTDM)
84add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")84add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")
8585
=== renamed file 'tests/qmltests/Stages/tst_WindowMoveResizeArea.qml' => 'tests/qmltests/Stages/tst_WindowResizeArea.qml'
--- tests/qmltests/Stages/tst_WindowMoveResizeArea.qml 2015-09-07 13:45:50 +0000
+++ tests/qmltests/Stages/tst_WindowResizeArea.qml 2015-10-19 14:53:23 +0000
@@ -17,7 +17,7 @@
17import QtQuick 2.117import QtQuick 2.1
18import QtQuick.Layouts 1.118import QtQuick.Layouts 1.1
19import QtTest 1.019import QtTest 1.0
20import Unity.Test 0.1 as UT20import Unity.Test 0.1
21import ".."21import ".."
22import "../../../qml/Stages"22import "../../../qml/Stages"
23import Ubuntu.Components 0.123import Ubuntu.Components 0.1
@@ -36,8 +36,8 @@
3636
37 Item {37 Item {
38 id: fakeWindow38 id: fakeWindow
39 property alias minWidth: moveResizeArea.minWidth39 property alias minWidth: windowResizeArea.minWidth
40 property alias minHeight: moveResizeArea.minHeight40 property alias minHeight: windowResizeArea.minHeight
41 x: units.gu(20)41 x: units.gu(20)
42 y: units.gu(20)42 y: units.gu(20)
43 height: units.gu(20)43 height: units.gu(20)
@@ -47,23 +47,27 @@
47 onWindowHeightChanged: height = windowHeight47 onWindowHeightChanged: height = windowHeight
48 onWindowWidthChanged: width = windowWidth48 onWindowWidthChanged: width = windowWidth
4949
50 WindowMoveResizeArea {50 WindowResizeArea {
51 id: moveResizeArea51 id: windowResizeArea
52 target: fakeWindow52 target: fakeWindow
53 resizeHandleWidth: units.gu(0.5)53 borderThickness: units.gu(2)
54 minWidth: units.gu(15)54 minWidth: units.gu(15)
55 minHeight: units.gu(10)55 minHeight: units.gu(10)
56 windowId: "test-window-id"56 windowId: "test-window-id"
57 }57 }
5858
59 Rectangle {59 Rectangle {
60 anchors.fill: moveResizeArea60 anchors.fill: windowResizeArea
61 color: "red"61 color: "red"
62 }62 }
6363
64 Rectangle {64 Rectangle {
65 anchors.fill: fakeWindow65 anchors.fill: fakeWindow
66 color: "blue"66 color: "blue"
67 MouseArea {
68 anchors.fill: parent
69 hoverEnabled: true
70 }
67 }71 }
68 }72 }
69 }73 }
@@ -73,8 +77,13 @@
73 sourceComponent: fakeWindowComponent77 sourceComponent: fakeWindowComponent
74 }78 }
7579
76 UT.UnityTestCase {80 MouseTouchEmulationCheckbox {
77 name: "WindowMoveResizeArea"81 checked: false
82 color: "black"
83 }
84
85 UnityTestCase {
86 name: "WindowResizeArea"
78 when: windowShown87 when: windowShown
7988
80 function init() {89 function init() {
@@ -84,34 +93,6 @@
84 fakeWindow.height = units.gu(20)93 fakeWindow.height = units.gu(20)
85 }94 }
8695
87 function test_dragWindow_data() {
88 return [
89 { tag: "up", dx: 0, dy: units.gu(-10) },
90 { tag: "down", dx: 0, dy: units.gu(10) },
91 { tag: "left", dx: units.gu(-10), dy: 0 },
92 { tag: "right", dx: units.gu(10), dy: 0 },
93 { tag: "right/down", dx: units.gu(10), dy: units.gu(10) },
94 { tag: "left/down", dx: units.gu(-10), dy: units.gu(10) }
95 ]
96 }
97
98 function test_dragWindow(data) {
99 var initialWindowX = fakeWindow.x;
100 var initialWindowY = fakeWindow.y;
101 var initialWindowWidth = fakeWindow.width
102 var initialWindowHeight = fakeWindow.height
103
104 var startDragX = initialWindowX + fakeWindow.width / 2;
105 var startDragY = initialWindowY + fakeWindow.height / 2;
106 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10)
107
108 tryCompare(fakeWindow, "x", initialWindowX + data.dx)
109 tryCompare(fakeWindow, "y", initialWindowX + data.dy)
110
111 compare(fakeWindow.height, initialWindowHeight);
112 compare(fakeWindow.width, initialWindowWidth);
113 }
114
115 function test_resizeWindowRightBottom_data() {96 function test_resizeWindowRightBottom_data() {
116 return [97 return [
117 { tag: "width", dx: units.gu(10), dy: 0 },98 { tag: "width", dx: units.gu(10), dy: 0 },
@@ -166,30 +147,6 @@
166 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));147 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
167 }148 }
168149
169 function test_saveRestorePosition() {
170 var initialWindowX = fakeWindow.x;
171 var initialWindowY = fakeWindow.y;
172 var initialWindowWidth = fakeWindow.width;
173 var initialWindowHeight = fakeWindow.height;
174
175 var moveDelta = units.gu(5);
176 var startDragX = initialWindowX + fakeWindow.width / 2;
177 var startDragY = initialWindowY + fakeWindow.height / 2;
178 mouseFlick(root, startDragX, startDragY, startDragX + moveDelta, startDragY + moveDelta, true, true, units.gu(.5), 10)
179
180 tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
181 tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
182
183 // This will destroy the window and recreate it
184 windowLoader.active = false;
185 waitForRendering(root);
186 windowLoader.active = true;
187
188 // Make sure it's again where we left it before destroying
189 tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
190 tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
191 }
192
193 function test_saveRestoreSize() {150 function test_saveRestoreSize() {
194 var initialWindowX = fakeWindow.x;151 var initialWindowX = fakeWindow.x;
195 var initialWindowY = fakeWindow.y;152 var initialWindowY = fakeWindow.y;
196153
=== modified file 'tests/qmltests/tst_OrientedShell.qml'
--- tests/qmltests/tst_OrientedShell.qml 2015-09-21 13:37:47 +0000
+++ tests/qmltests/tst_OrientedShell.qml 2015-10-19 14:53:23 +0000
@@ -264,6 +264,10 @@
264 text: "Usage Mode"264 text: "Usage Mode"
265 model: ["Staged", "Windowed", "Automatic"]265 model: ["Staged", "Windowed", "Automatic"]
266 }266 }
267 MouseTouchEmulationCheckbox {
268 checked: true
269 color: "white"
270 }
267 Button {271 Button {
268 text: "Switch fullscreen"272 text: "Switch fullscreen"
269 activeFocusOnPress: false273 activeFocusOnPress: false

Subscribers

People subscribed via source and target branches