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
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-09-30 16:49:52 +0000
3+++ CMakeLists.txt 2015-10-19 14:53:23 +0000
4@@ -57,7 +57,7 @@
5 find_package(Qt5Concurrent 5.2 REQUIRED)
6 find_package(Qt5Sql 5.2 REQUIRED)
7
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application>=8)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=9)
10
11 # Standard install paths
12 include(GNUInstallDirs)
13
14=== modified file 'debian/control'
15--- debian/control 2015-09-23 18:15:40 +0000
16+++ debian/control 2015-10-19 14:53:23 +0000
17@@ -25,10 +25,11 @@
18 libpay2-dev,
19 libpulse-dev,
20 libqmenumodel-dev (>= 0.2.9),
21+ libqt5svg5-dev,
22 libqt5xmlpatterns5-dev,
23 libsystemsettings-dev,
24 libudev-dev,
25- libunity-api-dev (>= 7.100),
26+ libunity-api-dev (>= 7.101),
27 libusermetricsoutput1-dev,
28 libxcb1-dev,
29 pkg-config,
30@@ -87,7 +88,8 @@
31 Package: unity8
32 Architecture: any
33 Provides: indicator-renderer,
34-Depends: gsettings-desktop-schemas,
35+Depends: dmz-cursor-theme,
36+ gsettings-desktop-schemas,
37 libcap2-bin,
38 libglib2.0-bin,
39 qmenumodel-qml (>= 0.2.9),
40@@ -125,7 +127,7 @@
41 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
42 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
43 ubuntu-thumbnailer-impl-0,
44- unity-application-impl-8,
45+ unity-application-impl-9,
46 unity-notifications-impl-3,
47 unity-plugin-scopes | unity-scopes-impl,
48 unity-scopes-impl-7,
49@@ -171,7 +173,7 @@
50 Depends: ${misc:Depends},
51 ${shlibs:Depends},
52 Provides: unity-application-impl,
53- unity-application-impl-8,
54+ unity-application-impl-9,
55 Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
56 Description: Fake environment for running Unity 8 shell
57 Provides fake implementations of some QML modules used by Unity 8 shell
58
59=== modified file 'debian/copyright'
60--- debian/copyright 2014-06-11 15:36:51 +0000
61+++ debian/copyright 2015-10-19 14:53:23 +0000
62@@ -60,3 +60,24 @@
63 packaging of this file. Please review the following information to
64 ensure the GNU General Public License version 3.0 requirements will be
65 met: http://www.gnu.org/copyleft/gpl.html.
66+
67+Files: plugins/Cursor/3rd_party/xcursor/xcursor.*
68+Copyright: 2002 Keith Packard
69+License: Keith Packard
70+ Permission to use, copy, modify, distribute, and sell this software and its
71+ documentation for any purpose is hereby granted without fee, provided that
72+ the above copyright notice appear in all copies and that both that
73+ copyright notice and this permission notice appear in supporting
74+ documentation, and that the name of Keith Packard not be used in
75+ advertising or publicity pertaining to distribution of the software without
76+ specific, written prior permission. Keith Packard makes no
77+ representations about the suitability of this software for any purpose. It
78+ is provided "as is" without express or implied warranty.
79+ .
80+ KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
81+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
82+ EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
83+ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
84+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
85+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
86+ PERFORMANCE OF THIS SOFTWARE.
87
88=== modified file 'debian/unity8-private.install'
89--- debian/unity8-private.install 2015-09-02 09:30:32 +0000
90+++ debian/unity8-private.install 2015-10-19 14:53:23 +0000
91@@ -1,6 +1,7 @@
92 usr/lib/*/libunity8-private.*
93 usr/lib/*/unity8/libUbuntuGestures*
94 usr/lib/*/unity8/qml/AccountsService
95+usr/lib/*/unity8/qml/Cursor
96 usr/lib/*/unity8/qml/Dash
97 usr/lib/*/unity8/qml/GlobalShortcut
98 usr/lib/*/unity8/qml/Greeter
99
100=== modified file 'plugins/CMakeLists.txt'
101--- plugins/CMakeLists.txt 2015-09-02 09:30:32 +0000
102+++ plugins/CMakeLists.txt 2015-10-19 14:53:23 +0000
103@@ -12,6 +12,7 @@
104 endmacro()
105
106 add_subdirectory(AccountsService)
107+add_subdirectory(Cursor)
108 add_subdirectory(GlobalShortcut)
109 add_subdirectory(Greeter)
110 add_subdirectory(IntegratedLightDM)
111
112=== added directory 'plugins/Cursor'
113=== added directory 'plugins/Cursor/3rd_party'
114=== added file 'plugins/Cursor/3rd_party/CMakeLists.txt'
115--- plugins/Cursor/3rd_party/CMakeLists.txt 1970-01-01 00:00:00 +0000
116+++ plugins/Cursor/3rd_party/CMakeLists.txt 2015-10-19 14:53:23 +0000
117@@ -0,0 +1,1 @@
118+add_subdirectory(xcursor)
119
120=== added directory 'plugins/Cursor/3rd_party/xcursor'
121=== added file 'plugins/Cursor/3rd_party/xcursor/CMakeLists.txt'
122--- plugins/Cursor/3rd_party/xcursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
123+++ plugins/Cursor/3rd_party/xcursor/CMakeLists.txt 2015-10-19 14:53:23 +0000
124@@ -0,0 +1,15 @@
125+add_definitions(-D_DEFAULT_SOURCE=1)
126+
127+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
128+
129+set(
130+ XCURSOR_SOURCES
131+
132+ xcursor.c
133+)
134+
135+add_library(
136+ xcursorloader-static STATIC
137+
138+ ${XCURSOR_SOURCES}
139+)
140
141=== added file 'plugins/Cursor/3rd_party/xcursor/xcursor.c'
142--- plugins/Cursor/3rd_party/xcursor/xcursor.c 1970-01-01 00:00:00 +0000
143+++ plugins/Cursor/3rd_party/xcursor/xcursor.c 2015-10-19 14:53:23 +0000
144@@ -0,0 +1,968 @@
145+/*
146+ * Copyright © 2002 Keith Packard
147+ *
148+ * Permission to use, copy, modify, distribute, and sell this software and its
149+ * documentation for any purpose is hereby granted without fee, provided that
150+ * the above copyright notice appear in all copies and that both that
151+ * copyright notice and this permission notice appear in supporting
152+ * documentation, and that the name of Keith Packard not be used in
153+ * advertising or publicity pertaining to distribution of the software without
154+ * specific, written prior permission. Keith Packard makes no
155+ * representations about the suitability of this software for any purpose. It
156+ * is provided "as is" without express or implied warranty.
157+ *
158+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
159+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
160+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
161+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
162+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
163+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
164+ * PERFORMANCE OF THIS SOFTWARE.
165+ */
166+
167+#include "xcursor.h"
168+#include <stdio.h>
169+#include <stdlib.h>
170+#include <string.h>
171+#include <dirent.h>
172+
173+/*
174+ * From libXcursor/include/X11/extensions/Xcursor.h
175+ */
176+
177+#define XcursorTrue 1
178+#define XcursorFalse 0
179+
180+/*
181+ * Cursor files start with a header. The header
182+ * contains a magic number, a version number and a
183+ * table of contents which has type and offset information
184+ * for the remaining tables in the file.
185+ *
186+ * File minor versions increment for compatible changes
187+ * File major versions increment for incompatible changes (never, we hope)
188+ *
189+ * Chunks of the same type are always upward compatible. Incompatible
190+ * changes are made with new chunk types; the old data can remain under
191+ * the old type. Upward compatible changes can add header data as the
192+ * header lengths are specified in the file.
193+ *
194+ * File:
195+ * FileHeader
196+ * LISTofChunk
197+ *
198+ * FileHeader:
199+ * CARD32 magic magic number
200+ * CARD32 header bytes in file header
201+ * CARD32 version file version
202+ * CARD32 ntoc number of toc entries
203+ * LISTofFileToc toc table of contents
204+ *
205+ * FileToc:
206+ * CARD32 type entry type
207+ * CARD32 subtype entry subtype (size for images)
208+ * CARD32 position absolute file position
209+ */
210+
211+#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
212+
213+/*
214+ * Current Xcursor version number. Will be substituted by configure
215+ * from the version in the libXcursor configure.ac file.
216+ */
217+
218+#define XCURSOR_LIB_MAJOR 1
219+#define XCURSOR_LIB_MINOR 1
220+#define XCURSOR_LIB_REVISION 13
221+#define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \
222+ (XCURSOR_LIB_MINOR * 100) + \
223+ (XCURSOR_LIB_REVISION))
224+
225+/*
226+ * This version number is stored in cursor files; changes to the
227+ * file format require updating this version number
228+ */
229+#define XCURSOR_FILE_MAJOR 1
230+#define XCURSOR_FILE_MINOR 0
231+#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
232+#define XCURSOR_FILE_HEADER_LEN (4 * 4)
233+#define XCURSOR_FILE_TOC_LEN (3 * 4)
234+
235+typedef struct _XcursorFileToc {
236+ XcursorUInt type; /* chunk type */
237+ XcursorUInt subtype; /* subtype (size for images) */
238+ XcursorUInt position; /* absolute position in file */
239+} XcursorFileToc;
240+
241+typedef struct _XcursorFileHeader {
242+ XcursorUInt magic; /* magic number */
243+ XcursorUInt header; /* byte length of header */
244+ XcursorUInt version; /* file version number */
245+ XcursorUInt ntoc; /* number of toc entries */
246+ XcursorFileToc *tocs; /* table of contents */
247+} XcursorFileHeader;
248+
249+/*
250+ * The rest of the file is a list of chunks, each tagged by type
251+ * and version.
252+ *
253+ * Chunk:
254+ * ChunkHeader
255+ * <extra type-specific header fields>
256+ * <type-specific data>
257+ *
258+ * ChunkHeader:
259+ * CARD32 header bytes in chunk header + type header
260+ * CARD32 type chunk type
261+ * CARD32 subtype chunk subtype
262+ * CARD32 version chunk type version
263+ */
264+
265+#define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
266+
267+typedef struct _XcursorChunkHeader {
268+ XcursorUInt header; /* bytes in chunk header */
269+ XcursorUInt type; /* chunk type */
270+ XcursorUInt subtype; /* chunk subtype (size for images) */
271+ XcursorUInt version; /* version of this type */
272+} XcursorChunkHeader;
273+
274+/*
275+ * Here's a list of the known chunk types
276+ */
277+
278+/*
279+ * Comments consist of a 4-byte length field followed by
280+ * UTF-8 encoded text
281+ *
282+ * Comment:
283+ * ChunkHeader header chunk header
284+ * CARD32 length bytes in text
285+ * LISTofCARD8 text UTF-8 encoded text
286+ */
287+
288+#define XCURSOR_COMMENT_TYPE 0xfffe0001
289+#define XCURSOR_COMMENT_VERSION 1
290+#define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
291+#define XCURSOR_COMMENT_COPYRIGHT 1
292+#define XCURSOR_COMMENT_LICENSE 2
293+#define XCURSOR_COMMENT_OTHER 3
294+#define XCURSOR_COMMENT_MAX_LEN 0x100000
295+
296+typedef struct _XcursorComment {
297+ XcursorUInt version;
298+ XcursorUInt comment_type;
299+ char *comment;
300+} XcursorComment;
301+
302+/*
303+ * Each cursor image occupies a separate image chunk.
304+ * The length of the image header follows the chunk header
305+ * so that future versions can extend the header without
306+ * breaking older applications
307+ *
308+ * Image:
309+ * ChunkHeader header chunk header
310+ * CARD32 width actual width
311+ * CARD32 height actual height
312+ * CARD32 xhot hot spot x
313+ * CARD32 yhot hot spot y
314+ * CARD32 delay animation delay
315+ * LISTofCARD32 pixels ARGB pixels
316+ */
317+
318+#define XCURSOR_IMAGE_TYPE 0xfffd0002
319+#define XCURSOR_IMAGE_VERSION 1
320+#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
321+#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
322+
323+typedef struct _XcursorFile XcursorFile;
324+
325+struct _XcursorFile {
326+ void *closure;
327+ int (*read) (XcursorFile *file, unsigned char *buf, int len);
328+ int (*write) (XcursorFile *file, unsigned char *buf, int len);
329+ int (*seek) (XcursorFile *file, long offset, int whence);
330+};
331+
332+typedef struct _XcursorComments {
333+ int ncomment; /* number of comments */
334+ XcursorComment **comments; /* array of XcursorComment pointers */
335+} XcursorComments;
336+
337+/*
338+ * From libXcursor/src/file.c
339+ */
340+
341+static XcursorImage *
342+XcursorImageCreate (int width, int height)
343+{
344+ XcursorImage *image;
345+
346+ image = malloc (sizeof (XcursorImage) +
347+ width * height * sizeof (XcursorPixel));
348+ if (!image)
349+ return NULL;
350+ image->version = XCURSOR_IMAGE_VERSION;
351+ image->pixels = (XcursorPixel *) (image + 1);
352+ image->size = width > height ? width : height;
353+ image->width = width;
354+ image->height = height;
355+ image->delay = 0;
356+ return image;
357+}
358+
359+static void
360+XcursorImageDestroy (XcursorImage *image)
361+{
362+ free (image);
363+}
364+
365+static XcursorImages *
366+XcursorImagesCreate (int size)
367+{
368+ XcursorImages *images;
369+
370+ images = malloc (sizeof (XcursorImages) +
371+ size * sizeof (XcursorImage *));
372+ if (!images)
373+ return NULL;
374+ images->nimage = 0;
375+ images->images = (XcursorImage **) (images + 1);
376+ images->name = NULL;
377+ return images;
378+}
379+
380+void
381+XcursorImagesDestroy (XcursorImages *images)
382+{
383+ int n;
384+
385+ if (!images)
386+ return;
387+
388+ for (n = 0; n < images->nimage; n++)
389+ XcursorImageDestroy (images->images[n]);
390+ if (images->name)
391+ free (images->name);
392+ free (images);
393+}
394+
395+static void
396+XcursorImagesSetName (XcursorImages *images, const char *name)
397+{
398+ char *new;
399+
400+ if (!images || !name)
401+ return;
402+
403+ new = malloc (strlen (name) + 1);
404+
405+ if (!new)
406+ return;
407+
408+ strcpy (new, name);
409+ if (images->name)
410+ free (images->name);
411+ images->name = new;
412+}
413+
414+static XcursorBool
415+_XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
416+{
417+ unsigned char bytes[4];
418+
419+ if (!file || !u)
420+ return XcursorFalse;
421+
422+ if ((*file->read) (file, bytes, 4) != 4)
423+ return XcursorFalse;
424+ *u = ((bytes[0] << 0) |
425+ (bytes[1] << 8) |
426+ (bytes[2] << 16) |
427+ (bytes[3] << 24));
428+ return XcursorTrue;
429+}
430+
431+static void
432+_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
433+{
434+ free (fileHeader);
435+}
436+
437+static XcursorFileHeader *
438+_XcursorFileHeaderCreate (int ntoc)
439+{
440+ XcursorFileHeader *fileHeader;
441+
442+ if (ntoc > 0x10000)
443+ return NULL;
444+ fileHeader = malloc (sizeof (XcursorFileHeader) +
445+ ntoc * sizeof (XcursorFileToc));
446+ if (!fileHeader)
447+ return NULL;
448+ fileHeader->magic = XCURSOR_MAGIC;
449+ fileHeader->header = XCURSOR_FILE_HEADER_LEN;
450+ fileHeader->version = XCURSOR_FILE_VERSION;
451+ fileHeader->ntoc = ntoc;
452+ fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
453+ return fileHeader;
454+}
455+
456+static XcursorFileHeader *
457+_XcursorReadFileHeader (XcursorFile *file)
458+{
459+ XcursorFileHeader head, *fileHeader;
460+ XcursorUInt skip;
461+ unsigned int n;
462+
463+ if (!file)
464+ return NULL;
465+
466+ if (!_XcursorReadUInt (file, &head.magic))
467+ return NULL;
468+ if (head.magic != XCURSOR_MAGIC)
469+ return NULL;
470+ if (!_XcursorReadUInt (file, &head.header))
471+ return NULL;
472+ if (!_XcursorReadUInt (file, &head.version))
473+ return NULL;
474+ if (!_XcursorReadUInt (file, &head.ntoc))
475+ return NULL;
476+ skip = head.header - XCURSOR_FILE_HEADER_LEN;
477+ if (skip)
478+ if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
479+ return NULL;
480+ fileHeader = _XcursorFileHeaderCreate (head.ntoc);
481+ if (!fileHeader)
482+ return NULL;
483+ fileHeader->magic = head.magic;
484+ fileHeader->header = head.header;
485+ fileHeader->version = head.version;
486+ fileHeader->ntoc = head.ntoc;
487+ for (n = 0; n < fileHeader->ntoc; n++)
488+ {
489+ if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
490+ break;
491+ if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
492+ break;
493+ if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
494+ break;
495+ }
496+ if (n != fileHeader->ntoc)
497+ {
498+ _XcursorFileHeaderDestroy (fileHeader);
499+ return NULL;
500+ }
501+ return fileHeader;
502+}
503+
504+static XcursorBool
505+_XcursorSeekToToc (XcursorFile *file,
506+ XcursorFileHeader *fileHeader,
507+ int toc)
508+{
509+ if (!file || !fileHeader || \
510+ (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
511+ return XcursorFalse;
512+ return XcursorTrue;
513+}
514+
515+static XcursorBool
516+_XcursorFileReadChunkHeader (XcursorFile *file,
517+ XcursorFileHeader *fileHeader,
518+ int toc,
519+ XcursorChunkHeader *chunkHeader)
520+{
521+ if (!file || !fileHeader || !chunkHeader)
522+ return XcursorFalse;
523+ if (!_XcursorSeekToToc (file, fileHeader, toc))
524+ return XcursorFalse;
525+ if (!_XcursorReadUInt (file, &chunkHeader->header))
526+ return XcursorFalse;
527+ if (!_XcursorReadUInt (file, &chunkHeader->type))
528+ return XcursorFalse;
529+ if (!_XcursorReadUInt (file, &chunkHeader->subtype))
530+ return XcursorFalse;
531+ if (!_XcursorReadUInt (file, &chunkHeader->version))
532+ return XcursorFalse;
533+ /* sanity check */
534+ if (chunkHeader->type != fileHeader->tocs[toc].type ||
535+ chunkHeader->subtype != fileHeader->tocs[toc].subtype)
536+ return XcursorFalse;
537+ return XcursorTrue;
538+}
539+
540+#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
541+
542+static XcursorDim
543+_XcursorFindBestSize (XcursorFileHeader *fileHeader,
544+ XcursorDim size,
545+ int *nsizesp)
546+{
547+ unsigned int n;
548+ int nsizes = 0;
549+ XcursorDim bestSize = 0;
550+ XcursorDim thisSize;
551+
552+ if (!fileHeader || !nsizesp)
553+ return 0;
554+
555+ for (n = 0; n < fileHeader->ntoc; n++)
556+ {
557+ if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
558+ continue;
559+ thisSize = fileHeader->tocs[n].subtype;
560+ if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
561+ {
562+ bestSize = thisSize;
563+ nsizes = 1;
564+ }
565+ else if (thisSize == bestSize)
566+ nsizes++;
567+ }
568+ *nsizesp = nsizes;
569+ return bestSize;
570+}
571+
572+static int
573+_XcursorFindImageToc (XcursorFileHeader *fileHeader,
574+ XcursorDim size,
575+ int count)
576+{
577+ unsigned int toc;
578+ XcursorDim thisSize;
579+
580+ if (!fileHeader)
581+ return 0;
582+
583+ for (toc = 0; toc < fileHeader->ntoc; toc++)
584+ {
585+ if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
586+ continue;
587+ thisSize = fileHeader->tocs[toc].subtype;
588+ if (thisSize != size)
589+ continue;
590+ if (!count)
591+ break;
592+ count--;
593+ }
594+ if (toc == fileHeader->ntoc)
595+ return -1;
596+ return toc;
597+}
598+
599+static XcursorImage *
600+_XcursorReadImage (XcursorFile *file,
601+ XcursorFileHeader *fileHeader,
602+ int toc)
603+{
604+ XcursorChunkHeader chunkHeader;
605+ XcursorImage head;
606+ XcursorImage *image;
607+ int n;
608+ XcursorPixel *p;
609+
610+ if (!file || !fileHeader)
611+ return NULL;
612+
613+ if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
614+ return NULL;
615+ if (!_XcursorReadUInt (file, &head.width))
616+ return NULL;
617+ if (!_XcursorReadUInt (file, &head.height))
618+ return NULL;
619+ if (!_XcursorReadUInt (file, &head.xhot))
620+ return NULL;
621+ if (!_XcursorReadUInt (file, &head.yhot))
622+ return NULL;
623+ if (!_XcursorReadUInt (file, &head.delay))
624+ return NULL;
625+ /* sanity check data */
626+ if (head.width >= 0x10000 || head.height > 0x10000)
627+ return NULL;
628+ if (head.width == 0 || head.height == 0)
629+ return NULL;
630+ if (head.xhot > head.width || head.yhot > head.height)
631+ return NULL;
632+
633+ /* Create the image and initialize it */
634+ image = XcursorImageCreate (head.width, head.height);
635+ if (image == NULL)
636+ return NULL;
637+ if (chunkHeader.version < image->version)
638+ image->version = chunkHeader.version;
639+ image->size = chunkHeader.subtype;
640+ image->xhot = head.xhot;
641+ image->yhot = head.yhot;
642+ image->delay = head.delay;
643+ n = image->width * image->height;
644+ p = image->pixels;
645+ while (n--)
646+ {
647+ if (!_XcursorReadUInt (file, p))
648+ {
649+ XcursorImageDestroy (image);
650+ return NULL;
651+ }
652+ p++;
653+ }
654+ return image;
655+}
656+
657+static XcursorImages *
658+XcursorXcFileLoadImages (XcursorFile *file, int size)
659+{
660+ XcursorFileHeader *fileHeader;
661+ XcursorDim bestSize;
662+ int nsize;
663+ XcursorImages *images;
664+ int n;
665+ int toc;
666+
667+ if (!file || size < 0)
668+ return NULL;
669+ fileHeader = _XcursorReadFileHeader (file);
670+ if (!fileHeader)
671+ return NULL;
672+ bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
673+ if (!bestSize)
674+ {
675+ _XcursorFileHeaderDestroy (fileHeader);
676+ return NULL;
677+ }
678+ images = XcursorImagesCreate (nsize);
679+ if (!images)
680+ {
681+ _XcursorFileHeaderDestroy (fileHeader);
682+ return NULL;
683+ }
684+ for (n = 0; n < nsize; n++)
685+ {
686+ toc = _XcursorFindImageToc (fileHeader, bestSize, n);
687+ if (toc < 0)
688+ break;
689+ images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
690+ toc);
691+ if (!images->images[images->nimage])
692+ break;
693+ images->nimage++;
694+ }
695+ _XcursorFileHeaderDestroy (fileHeader);
696+ if (images->nimage != nsize)
697+ {
698+ XcursorImagesDestroy (images);
699+ images = NULL;
700+ }
701+ return images;
702+}
703+
704+static int
705+_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
706+{
707+ FILE *f = file->closure;
708+ return fread (buf, 1, len, f);
709+}
710+
711+static int
712+_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
713+{
714+ FILE *f = file->closure;
715+ return fwrite (buf, 1, len, f);
716+}
717+
718+static int
719+_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
720+{
721+ FILE *f = file->closure;
722+ return fseek (f, offset, whence);
723+}
724+
725+static void
726+_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
727+{
728+ file->closure = stdfile;
729+ file->read = _XcursorStdioFileRead;
730+ file->write = _XcursorStdioFileWrite;
731+ file->seek = _XcursorStdioFileSeek;
732+}
733+
734+static XcursorImages *
735+XcursorFileLoadImages (FILE *file, int size)
736+{
737+ XcursorFile f;
738+
739+ if (!file)
740+ return NULL;
741+
742+ _XcursorStdioFileInitialize (file, &f);
743+ return XcursorXcFileLoadImages (&f, size);
744+}
745+
746+/*
747+ * From libXcursor/src/library.c
748+ */
749+
750+#ifndef ICONDIR
751+#define ICONDIR "/usr/X11R6/lib/X11/icons"
752+#endif
753+
754+#ifndef XCURSORPATH
755+#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
756+#endif
757+
758+static const char *
759+XcursorLibraryPath (void)
760+{
761+ static const char *path;
762+
763+ if (!path)
764+ {
765+ path = getenv ("XCURSOR_PATH");
766+ if (!path)
767+ path = XCURSORPATH;
768+ }
769+ return path;
770+}
771+
772+static void
773+_XcursorAddPathElt (char *path, const char *elt, int len)
774+{
775+ int pathlen = strlen (path);
776+
777+ /* append / if the path doesn't currently have one */
778+ if (path[0] == '\0' || path[pathlen - 1] != '/')
779+ {
780+ strcat (path, "/");
781+ pathlen++;
782+ }
783+ if (len == -1)
784+ len = strlen (elt);
785+ /* strip leading slashes */
786+ while (len && elt[0] == '/')
787+ {
788+ elt++;
789+ len--;
790+ }
791+ strncpy (path + pathlen, elt, len);
792+ path[pathlen + len] = '\0';
793+}
794+
795+static char *
796+_XcursorBuildThemeDir (const char *dir, const char *theme)
797+{
798+ const char *colon;
799+ const char *tcolon;
800+ char *full;
801+ char *home;
802+ int dirlen;
803+ int homelen;
804+ int themelen;
805+ int len;
806+
807+ if (!dir || !theme)
808+ return NULL;
809+
810+ colon = strchr (dir, ':');
811+ if (!colon)
812+ colon = dir + strlen (dir);
813+
814+ dirlen = colon - dir;
815+
816+ tcolon = strchr (theme, ':');
817+ if (!tcolon)
818+ tcolon = theme + strlen (theme);
819+
820+ themelen = tcolon - theme;
821+
822+ home = NULL;
823+ homelen = 0;
824+ if (*dir == '~')
825+ {
826+ home = getenv ("HOME");
827+ if (!home)
828+ return NULL;
829+ homelen = strlen (home);
830+ dir++;
831+ dirlen--;
832+ }
833+
834+ /*
835+ * add space for any needed directory separators, one per component,
836+ * and one for the trailing null
837+ */
838+ len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
839+
840+ full = malloc (len);
841+ if (!full)
842+ return NULL;
843+ full[0] = '\0';
844+
845+ if (home)
846+ _XcursorAddPathElt (full, home, -1);
847+ _XcursorAddPathElt (full, dir, dirlen);
848+ _XcursorAddPathElt (full, theme, themelen);
849+ return full;
850+}
851+
852+static char *
853+_XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
854+{
855+ char *full;
856+
857+ if (!dir || !subdir || !file)
858+ return NULL;
859+
860+ full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
861+ if (!full)
862+ return NULL;
863+ full[0] = '\0';
864+ _XcursorAddPathElt (full, dir, -1);
865+ _XcursorAddPathElt (full, subdir, -1);
866+ _XcursorAddPathElt (full, file, -1);
867+ return full;
868+}
869+
870+static const char *
871+_XcursorNextPath (const char *path)
872+{
873+ char *colon = strchr (path, ':');
874+
875+ if (!colon)
876+ return NULL;
877+ return colon + 1;
878+}
879+
880+#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
881+#define XcursorSep(c) ((c) == ';' || (c) == ',')
882+
883+static char *
884+_XcursorThemeInherits (const char *full)
885+{
886+ char line[8192];
887+ char *result = NULL;
888+ FILE *f;
889+
890+ if (!full)
891+ return NULL;
892+
893+ f = fopen (full, "r");
894+ if (f)
895+ {
896+ while (fgets (line, sizeof (line), f))
897+ {
898+ if (!strncmp (line, "Inherits", 8))
899+ {
900+ char *l = line + 8;
901+ char *r;
902+ while (*l == ' ') l++;
903+ if (*l != '=') continue;
904+ l++;
905+ while (*l == ' ') l++;
906+ result = malloc (strlen (l) + 1);
907+ if (result)
908+ {
909+ r = result;
910+ while (*l)
911+ {
912+ while (XcursorSep(*l) || XcursorWhite (*l)) l++;
913+ if (!*l)
914+ break;
915+ if (r != result)
916+ *r++ = ':';
917+ while (*l && !XcursorWhite(*l) &&
918+ !XcursorSep(*l))
919+ *r++ = *l++;
920+ }
921+ *r++ = '\0';
922+ }
923+ break;
924+ }
925+ }
926+ fclose (f);
927+ }
928+ return result;
929+}
930+
931+static FILE *
932+XcursorScanTheme (const char *theme, const char *name)
933+{
934+ FILE *f = NULL;
935+ char *full;
936+ char *dir;
937+ const char *path;
938+ char *inherits = NULL;
939+ const char *i;
940+
941+ if (!theme || !name)
942+ return NULL;
943+
944+ /*
945+ * Scan this theme
946+ */
947+ for (path = XcursorLibraryPath ();
948+ path && f == NULL;
949+ path = _XcursorNextPath (path))
950+ {
951+ dir = _XcursorBuildThemeDir (path, theme);
952+ if (dir)
953+ {
954+ full = _XcursorBuildFullname (dir, "cursors", name);
955+ if (full)
956+ {
957+ f = fopen (full, "r");
958+ free (full);
959+ }
960+ if (!f && !inherits)
961+ {
962+ full = _XcursorBuildFullname (dir, "", "index.theme");
963+ if (full)
964+ {
965+ inherits = _XcursorThemeInherits (full);
966+ free (full);
967+ }
968+ }
969+ free (dir);
970+ }
971+ }
972+ /*
973+ * Recurse to scan inherited themes
974+ */
975+ for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
976+ f = XcursorScanTheme (i, name);
977+ if (inherits != NULL)
978+ free (inherits);
979+ return f;
980+}
981+
982+XcursorImages *
983+XcursorLibraryLoadImages (const char *file, const char *theme, int size)
984+{
985+ FILE *f = NULL;
986+ XcursorImages *images = NULL;
987+
988+ if (!file)
989+ return NULL;
990+
991+ if (theme)
992+ f = XcursorScanTheme (theme, file);
993+ if (!f)
994+ f = XcursorScanTheme ("default", file);
995+ if (f)
996+ {
997+ images = XcursorFileLoadImages (f, size);
998+ if (images)
999+ XcursorImagesSetName (images, file);
1000+ fclose (f);
1001+ }
1002+ return images;
1003+}
1004+
1005+static void
1006+load_all_cursors_from_dir(const char *path, int size,
1007+ void (*load_callback)(XcursorImages *, void *),
1008+ void *user_data)
1009+{
1010+ FILE *f;
1011+ DIR *dir = opendir(path);
1012+ struct dirent *ent;
1013+ char *full;
1014+ XcursorImages *images;
1015+
1016+ if (!dir)
1017+ return;
1018+
1019+ for(ent = readdir(dir); ent; ent = readdir(dir)) {
1020+#ifdef _DIRENT_HAVE_D_TYPE
1021+ if (ent->d_type != DT_UNKNOWN &&
1022+ (ent->d_type != DT_REG && ent->d_type != DT_LNK))
1023+ continue;
1024+#endif
1025+
1026+ full = _XcursorBuildFullname(path, "", ent->d_name);
1027+ if (!full)
1028+ continue;
1029+
1030+ f = fopen(full, "r");
1031+ if (!f) {
1032+ free(full);
1033+ continue;
1034+ }
1035+
1036+ images = XcursorFileLoadImages(f, size);
1037+
1038+ if (images) {
1039+ XcursorImagesSetName(images, ent->d_name);
1040+ load_callback(images, user_data);
1041+ }
1042+
1043+ fclose (f);
1044+ free(full);
1045+ }
1046+
1047+ closedir(dir);
1048+}
1049+
1050+/** Load all the cursor of a theme
1051+ *
1052+ * This function loads all the cursor images of a given theme and its
1053+ * inherited themes. Each cursor is loaded into an XcursorImages object
1054+ * which is passed to the caller's load callback. If a cursor appears
1055+ * more than once across all the inherited themes, the load callback
1056+ * will be called multiple times, with possibly different XcursorImages
1057+ * object which have the same name. The user is expected to destroy the
1058+ * XcursorImages objects passed to the callback with
1059+ * XcursorImagesDestroy().
1060+ *
1061+ * \param theme The name of theme that should be loaded
1062+ * \param size The desired size of the cursor images
1063+ * \param load_callback A callback function that will be called
1064+ * for each cursor loaded. The first parameter is the XcursorImages
1065+ * object representing the loaded cursor and the second is a pointer
1066+ * to data provided by the user.
1067+ * \param user_data The data that should be passed to the load callback
1068+ */
1069+void
1070+xcursor_load_theme(const char *theme, int size,
1071+ void (*load_callback)(XcursorImages *, void *),
1072+ void *user_data)
1073+{
1074+ char *full, *dir;
1075+ char *inherits = NULL;
1076+ const char *path, *i;
1077+
1078+ if (!theme)
1079+ theme = "default";
1080+
1081+ for (path = XcursorLibraryPath();
1082+ path;
1083+ path = _XcursorNextPath(path)) {
1084+ dir = _XcursorBuildThemeDir(path, theme);
1085+ if (!dir)
1086+ continue;
1087+
1088+ full = _XcursorBuildFullname(dir, "cursors", "");
1089+
1090+ if (full) {
1091+ load_all_cursors_from_dir(full, size, load_callback,
1092+ user_data);
1093+ free(full);
1094+ }
1095+
1096+ if (!inherits) {
1097+ full = _XcursorBuildFullname(dir, "", "index.theme");
1098+ if (full) {
1099+ inherits = _XcursorThemeInherits(full);
1100+ free(full);
1101+ }
1102+ }
1103+
1104+ free(dir);
1105+ }
1106+
1107+ for (i = inherits; i; i = _XcursorNextPath(i))
1108+ xcursor_load_theme(i, size, load_callback, user_data);
1109+
1110+ if (inherits)
1111+ free(inherits);
1112+}
1113
1114=== added file 'plugins/Cursor/3rd_party/xcursor/xcursor.h'
1115--- plugins/Cursor/3rd_party/xcursor/xcursor.h 1970-01-01 00:00:00 +0000
1116+++ plugins/Cursor/3rd_party/xcursor/xcursor.h 2015-10-19 14:53:23 +0000
1117@@ -0,0 +1,65 @@
1118+/*
1119+ * Copyright © 2002 Keith Packard
1120+ *
1121+ * Permission to use, copy, modify, distribute, and sell this software and its
1122+ * documentation for any purpose is hereby granted without fee, provided that
1123+ * the above copyright notice appear in all copies and that both that
1124+ * copyright notice and this permission notice appear in supporting
1125+ * documentation, and that the name of Keith Packard not be used in
1126+ * advertising or publicity pertaining to distribution of the software without
1127+ * specific, written prior permission. Keith Packard makes no
1128+ * representations about the suitability of this software for any purpose. It
1129+ * is provided "as is" without express or implied warranty.
1130+ *
1131+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1132+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1133+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1134+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1135+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1136+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1137+ * PERFORMANCE OF THIS SOFTWARE.
1138+ */
1139+
1140+#ifndef XCURSOR_H
1141+#define XCURSOR_H
1142+
1143+#include <stdint.h>
1144+
1145+
1146+typedef int XcursorBool;
1147+typedef uint32_t XcursorUInt;
1148+
1149+typedef XcursorUInt XcursorDim;
1150+typedef XcursorUInt XcursorPixel;
1151+
1152+typedef struct _XcursorImage {
1153+ XcursorUInt version; /* version of the image data */
1154+ XcursorDim size; /* nominal size for matching */
1155+ XcursorDim width; /* actual width */
1156+ XcursorDim height; /* actual height */
1157+ XcursorDim xhot; /* hot spot x (must be inside image) */
1158+ XcursorDim yhot; /* hot spot y (must be inside image) */
1159+ XcursorUInt delay; /* animation delay to next frame (ms) */
1160+ XcursorPixel *pixels; /* pointer to pixels */
1161+} XcursorImage;
1162+
1163+/*
1164+ * Other data structures exposed by the library API
1165+ */
1166+typedef struct _XcursorImages {
1167+ int nimage; /* number of images */
1168+ XcursorImage **images; /* array of XcursorImage pointers */
1169+ char *name; /* name used to load images */
1170+} XcursorImages;
1171+
1172+XcursorImages *
1173+XcursorLibraryLoadImages (const char *file, const char *theme, int size);
1174+
1175+void
1176+XcursorImagesDestroy (XcursorImages *images);
1177+
1178+void
1179+xcursor_load_theme(const char *theme, int size,
1180+ void (*load_callback)(XcursorImages *, void *),
1181+ void *user_data);
1182+#endif
1183
1184=== added file 'plugins/Cursor/CMakeLists.txt'
1185--- plugins/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
1186+++ plugins/Cursor/CMakeLists.txt 2015-10-19 14:53:23 +0000
1187@@ -0,0 +1,28 @@
1188+add_subdirectory(3rd_party)
1189+
1190+include_directories(
1191+ ${CMAKE_CURRENT_SOURCE_DIR}
1192+ ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/xcursor
1193+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1194+)
1195+
1196+set(QMLPLUGIN_SRC
1197+ plugin.cpp
1198+ MousePointer.cpp
1199+ CursorImageProvider.cpp
1200+ # We need to run moc on this header
1201+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirMousePointerInterface.h
1202+ )
1203+
1204+add_library(Cursor-qml SHARED
1205+ ${QMLPLUGIN_SRC}
1206+ )
1207+
1208+target_link_libraries(Cursor-qml
1209+ xcursorloader-static
1210+ ${QT5PLATFORM_SUPPORT_LDFLAGS}
1211+)
1212+
1213+qt5_use_modules(Cursor-qml Qml Quick DBus Network Gui Sql Concurrent Svg)
1214+
1215+add_unity8_plugin(Cursor 1.0 Cursor TARGETS Cursor-qml)
1216
1217=== added file 'plugins/Cursor/Cursor.qml'
1218--- plugins/Cursor/Cursor.qml 1970-01-01 00:00:00 +0000
1219+++ plugins/Cursor/Cursor.qml 2015-10-19 14:53:23 +0000
1220@@ -0,0 +1,12 @@
1221+import QtQuick 2.4
1222+import Cursor 1.0 // For MousePointer
1223+
1224+MousePointer {
1225+ id: mousePointer
1226+
1227+ Image {
1228+ x: -mousePointer.hotspotX
1229+ y: -mousePointer.hotspotY
1230+ source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
1231+ }
1232+}
1233
1234=== added file 'plugins/Cursor/CursorImageProvider.cpp'
1235--- plugins/Cursor/CursorImageProvider.cpp 1970-01-01 00:00:00 +0000
1236+++ plugins/Cursor/CursorImageProvider.cpp 2015-10-19 14:53:23 +0000
1237@@ -0,0 +1,191 @@
1238+/*
1239+ * Copyright (C) 2015 Canonical, Ltd.
1240+ *
1241+ * This program is free software: you can redistribute it and/or modify it under
1242+ * the terms of the GNU Lesser General Public License version 3, as published by
1243+ * the Free Software Foundation.
1244+ *
1245+ * This program is distributed in the hope that it will be useful, but WITHOUT
1246+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1247+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1248+ * Lesser General Public License for more details.
1249+ *
1250+ * You should have received a copy of the GNU Lesser General Public License
1251+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1252+ */
1253+
1254+#include "CursorImageProvider.h"
1255+
1256+#include <QDebug>
1257+#include <QFile>
1258+#include <QPainter>
1259+#include <QSvgRenderer>
1260+
1261+CursorImageProvider *CursorImageProvider::m_instance = nullptr;
1262+
1263+/////
1264+// BuiltInCursorImage
1265+
1266+BuiltInCursorImage::BuiltInCursorImage()
1267+{
1268+ const char *svgString =
1269+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
1270+ "<svg"
1271+ " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
1272+ " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
1273+ " xmlns:svg=\"http://www.w3.org/2000/svg\""
1274+ " xmlns=\"http://www.w3.org/2000/svg\""
1275+ " version=\"1.1\">"
1276+ " <path"
1277+ " 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\""
1278+ " 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\" />"
1279+ "</svg>";
1280+
1281+ qimage = QImage(20, 32, QImage::Format_ARGB32);
1282+ QPainter imagePainter(&qimage);
1283+
1284+ QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
1285+ svgRenderer->render(&imagePainter);
1286+ delete svgRenderer;
1287+}
1288+
1289+/////
1290+// XCursorImage
1291+
1292+XCursorImage::XCursorImage(const QString &theme, const QString &file)
1293+ : xcursorImages(nullptr)
1294+{
1295+ xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme), 32);
1296+ if (!xcursorImages) {
1297+ return;
1298+ }
1299+
1300+ bool loaded = false;
1301+ for (int i = 0; i < xcursorImages->nimage && !loaded; ++i) {
1302+ XcursorImage *xcursorImage = xcursorImages->images[i];
1303+ if (xcursorImage->size == 32) {
1304+
1305+ qimage = QImage((uchar*)xcursorImage->pixels,
1306+ xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
1307+
1308+ hotspot.setX(xcursorImage->xhot);
1309+ hotspot.setY(xcursorImage->yhot);
1310+
1311+ loaded = true;
1312+ }
1313+ }
1314+}
1315+
1316+XCursorImage::~XCursorImage()
1317+{
1318+ XcursorImagesDestroy(xcursorImages);
1319+}
1320+
1321+/////
1322+// CursorImageProvider
1323+
1324+CursorImageProvider::CursorImageProvider()
1325+ : QQuickImageProvider(QQuickImageProvider::Image)
1326+{
1327+ if (m_instance) {
1328+ qFatal("Cannot have multiple CursorImageProvider instances");
1329+ }
1330+ m_instance = this;
1331+}
1332+
1333+CursorImageProvider::~CursorImageProvider()
1334+{
1335+ {
1336+ QList< QMap<QString, CursorImage*> > cursorList = m_cursors.values();
1337+
1338+ for (int i = 0; i < cursorList.count(); ++i) {
1339+ QList<CursorImage*> cursorImageList = cursorList[i].values();
1340+ for (int j = 0; j < cursorImageList.count(); ++j) {
1341+ delete cursorImageList[j];
1342+ }
1343+ }
1344+ }
1345+
1346+ m_cursors.clear();
1347+ m_instance = nullptr;
1348+}
1349+
1350+QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
1351+{
1352+ CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
1353+ size->setWidth(cursorImage->qimage.width());
1354+ size->setHeight(cursorImage->qimage.height());
1355+
1356+ return cursorImage->qimage;
1357+}
1358+
1359+QPoint CursorImageProvider::hotspot(const QString &themeName, const QString &cursorName)
1360+{
1361+ CursorImage *cursorImage = fetchCursor(themeName, cursorName);
1362+ if (cursorImage) {
1363+ return cursorImage->hotspot;
1364+ } else {
1365+ return QPoint(0,0);
1366+ }
1367+}
1368+
1369+CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
1370+{
1371+ QString themeName;
1372+ QString cursorName;
1373+ {
1374+ QStringList themeAndNameList = cursorThemeAndName.split("/");
1375+ if (themeAndNameList.size() != 2) {
1376+ return nullptr;
1377+ }
1378+ themeName = themeAndNameList[0];
1379+ cursorName = themeAndNameList[1];
1380+ }
1381+
1382+ return fetchCursor(themeName, cursorName);
1383+}
1384+
1385+CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
1386+{
1387+ CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
1388+
1389+ // Try some fallbacks
1390+ if (cursorImage->qimage.isNull()) {
1391+ if (cursorName == "ibeam") {
1392+ qDebug() << "CursorImageProvider: \"ibeam\" not found, falling back to \"xterm\"";
1393+ cursorImage = fetchCursorHelper(themeName, "xterm");
1394+ } else if (cursorName == "xterm") {
1395+ qDebug() << "CursorImageProvider: \"xterm\" not found, falling back to \"ibeam\"";
1396+ cursorImage = fetchCursorHelper(themeName, "ibeam");
1397+ }
1398+ }
1399+
1400+ // if it all fails, there must be at least a left_ptr
1401+ if (cursorImage->qimage.isNull() && cursorName != "left_ptr") {
1402+ qDebug() << "CursorImageProvider:" << cursorName
1403+ << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
1404+ cursorImage = fetchCursorHelper(themeName, "left_ptr");
1405+ }
1406+
1407+ if (cursorImage->qimage.isNull()) {
1408+ // finally, go for the built-in cursor
1409+ qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
1410+ if (!m_builtInCursorImage) {
1411+ m_builtInCursorImage.reset(new BuiltInCursorImage);
1412+ }
1413+ cursorImage = m_builtInCursorImage.data();
1414+ }
1415+
1416+ return cursorImage;
1417+}
1418+
1419+CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
1420+{
1421+ QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
1422+
1423+ if (!themeCursors.contains(cursorName)) {
1424+ themeCursors[cursorName] = new XCursorImage(themeName, cursorName);
1425+ }
1426+
1427+ return themeCursors[cursorName];
1428+}
1429
1430=== added file 'plugins/Cursor/CursorImageProvider.h'
1431--- plugins/Cursor/CursorImageProvider.h 1970-01-01 00:00:00 +0000
1432+++ plugins/Cursor/CursorImageProvider.h 2015-10-19 14:53:23 +0000
1433@@ -0,0 +1,76 @@
1434+/*
1435+ * Copyright (C) 2015 Canonical, Ltd.
1436+ *
1437+ * This program is free software: you can redistribute it and/or modify it under
1438+ * the terms of the GNU Lesser General Public License version 3, as published by
1439+ * the Free Software Foundation.
1440+ *
1441+ * This program is distributed in the hope that it will be useful, but WITHOUT
1442+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1443+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1444+ * Lesser General Public License for more details.
1445+ *
1446+ * You should have received a copy of the GNU Lesser General Public License
1447+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1448+ */
1449+
1450+#ifndef CURSORIMAGEPROVIDER_H
1451+#define CURSORIMAGEPROVIDER_H
1452+
1453+#include <QQuickImageProvider>
1454+#include <QScopedPointer>
1455+
1456+// xcursor static lib
1457+extern "C"
1458+{
1459+#include <xcursor.h>
1460+}
1461+
1462+class CursorImage {
1463+public:
1464+ virtual ~CursorImage() {}
1465+
1466+ QImage qimage;
1467+ QPoint hotspot;
1468+};
1469+
1470+class XCursorImage : public CursorImage {
1471+public:
1472+ XCursorImage(const QString &theme, const QString &file);
1473+ virtual ~XCursorImage();
1474+
1475+ XcursorImages *xcursorImages;
1476+};
1477+
1478+class BuiltInCursorImage : public CursorImage {
1479+public:
1480+ BuiltInCursorImage();
1481+};
1482+
1483+class CursorImageProvider : public QQuickImageProvider
1484+{
1485+public:
1486+ CursorImageProvider();
1487+ virtual ~CursorImageProvider();
1488+
1489+ static CursorImageProvider *instance() { return m_instance; }
1490+
1491+
1492+ QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
1493+
1494+ QPoint hotspot(const QString &themeName, const QString &cursorName);
1495+
1496+private:
1497+ CursorImage *fetchCursor(const QString &cursorThemeAndName);
1498+ CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
1499+ CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
1500+
1501+ // themeName -> (cursorName -> cursorImage)
1502+ QMap<QString, QMap<QString, CursorImage*> > m_cursors;
1503+
1504+ QScopedPointer<CursorImage> m_builtInCursorImage;
1505+
1506+ static CursorImageProvider *m_instance;
1507+};
1508+
1509+#endif // CURSORIMAGEPROVIDER_H
1510
1511=== added file 'plugins/Cursor/MousePointer.cpp'
1512--- plugins/Cursor/MousePointer.cpp 1970-01-01 00:00:00 +0000
1513+++ plugins/Cursor/MousePointer.cpp 2015-10-19 14:53:23 +0000
1514@@ -0,0 +1,125 @@
1515+/*
1516+ * Copyright (C) 2015 Canonical, Ltd.
1517+ *
1518+ * This program is free software: you can redistribute it and/or modify it under
1519+ * the terms of the GNU Lesser General Public License version 3, as published by
1520+ * the Free Software Foundation.
1521+ *
1522+ * This program is distributed in the hope that it will be useful, but WITHOUT
1523+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1524+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1525+ * Lesser General Public License for more details.
1526+ *
1527+ * You should have received a copy of the GNU Lesser General Public License
1528+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1529+ */
1530+
1531+#include "MousePointer.h"
1532+#include "CursorImageProvider.h"
1533+
1534+// Unity API
1535+#include <unity/shell/application/MirPlatformCursor.h>
1536+
1537+#include <QQuickWindow>
1538+#include <QGuiApplication>
1539+
1540+#include <qpa/qwindowsysteminterface.h>
1541+
1542+MousePointer::MousePointer(QQuickItem *parent)
1543+ : MirMousePointerInterface(parent)
1544+ , m_cursorName("left_ptr")
1545+ , m_themeName("default")
1546+ , m_hotspotX(0)
1547+ , m_hotspotY(0)
1548+{
1549+}
1550+
1551+void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
1552+ Qt::KeyboardModifiers modifiers)
1553+{
1554+ if (!parentItem()) {
1555+ return;
1556+ }
1557+
1558+ qreal newX = x() + movement.x();
1559+ if (newX < 0) {
1560+ newX = 0;
1561+ } else if (newX > parentItem()->width()) {
1562+ newX = parentItem()->width();
1563+ }
1564+ setX(newX);
1565+
1566+ qreal newY = y() + movement.y();
1567+ if (newY < 0) {
1568+ newY = 0;
1569+ } else if (newY > parentItem()->height()) {
1570+ newY = parentItem()->height();
1571+ }
1572+ setY(newY);
1573+
1574+ QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
1575+ QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
1576+ buttons, modifiers);
1577+}
1578+
1579+void MousePointer::itemChange(ItemChange change, const ItemChangeData &value)
1580+{
1581+ if (change == ItemSceneChange) {
1582+ registerWindow(value.window);
1583+ }
1584+}
1585+
1586+void MousePointer::registerWindow(QWindow *window)
1587+{
1588+ if (m_registeredWindow && window != m_registeredWindow) {
1589+ auto previousCursor = dynamic_cast<MirPlatformCursor*>(m_registeredWindow->screen()->handle()->cursor());
1590+ if (previousCursor) {
1591+ previousCursor->setMousePointer(nullptr);
1592+ } else {
1593+ qCritical("QPlatformCursor is not a MirPlatformCursor! Cursor module only works in a Mir server.");
1594+ }
1595+ }
1596+
1597+ m_registeredWindow = window;
1598+
1599+ if (m_registeredWindow) {
1600+ auto cursor = dynamic_cast<MirPlatformCursor*>(window->screen()->handle()->cursor());
1601+ if (cursor) {
1602+ cursor->setMousePointer(this);
1603+ } else {
1604+ qCritical("QPlaformCursor is not a MirPlatformCursor! Cursor module only works in Mir.");
1605+ }
1606+ }
1607+}
1608+
1609+void MousePointer::setCursorName(const QString &cursorName)
1610+{
1611+ if (cursorName != m_cursorName) {
1612+ m_cursorName = cursorName;
1613+ Q_EMIT cursorNameChanged(m_cursorName);
1614+ updateHotspot();
1615+ }
1616+}
1617+
1618+void MousePointer::updateHotspot()
1619+{
1620+ QPoint newHotspot = CursorImageProvider::instance()->hotspot(m_themeName, m_cursorName);
1621+
1622+ if (m_hotspotX != newHotspot.x()) {
1623+ m_hotspotX = newHotspot.x();
1624+ Q_EMIT hotspotXChanged(m_hotspotX);
1625+ }
1626+
1627+ if (m_hotspotY != newHotspot.y()) {
1628+ m_hotspotY = newHotspot.y();
1629+ Q_EMIT hotspotYChanged(m_hotspotY);
1630+ }
1631+}
1632+
1633+void MousePointer::setThemeName(const QString &themeName)
1634+{
1635+ if (m_themeName != themeName) {
1636+ m_themeName = themeName;
1637+ Q_EMIT themeNameChanged(m_themeName);
1638+ }
1639+}
1640
1641=== added file 'plugins/Cursor/MousePointer.h'
1642--- plugins/Cursor/MousePointer.h 1970-01-01 00:00:00 +0000
1643+++ plugins/Cursor/MousePointer.h 2015-10-19 14:53:23 +0000
1644@@ -0,0 +1,59 @@
1645+/*
1646+ * Copyright (C) 2015 Canonical, Ltd.
1647+ *
1648+ * This program is free software: you can redistribute it and/or modify it under
1649+ * the terms of the GNU Lesser General Public License version 3, as published by
1650+ * the Free Software Foundation.
1651+ *
1652+ * This program is distributed in the hope that it will be useful, but WITHOUT
1653+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1654+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1655+ * Lesser General Public License for more details.
1656+ *
1657+ * You should have received a copy of the GNU Lesser General Public License
1658+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1659+ */
1660+
1661+#ifndef MOUSEPOINTER_H
1662+#define MOUSEPOINTER_H
1663+
1664+// Qt
1665+#include <QPointer>
1666+#include <QWindow>
1667+
1668+// Unity API
1669+#include <unity/shell/application/MirMousePointerInterface.h>
1670+
1671+class MousePointer : public MirMousePointerInterface {
1672+ Q_OBJECT
1673+public:
1674+ MousePointer(QQuickItem *parent = nullptr);
1675+
1676+ void setCursorName(const QString &qtCursorName) override;
1677+ QString cursorName() const override { return m_cursorName; }
1678+
1679+ void setThemeName(const QString &themeName) override;
1680+ QString themeName() const override { return m_themeName; }
1681+
1682+ qreal hotspotX() const override { return m_hotspotX; }
1683+ qreal hotspotY() const override { return m_hotspotY; }
1684+
1685+public Q_SLOTS:
1686+ void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
1687+ Qt::KeyboardModifiers modifiers) override;
1688+
1689+protected:
1690+ void itemChange(ItemChange change, const ItemChangeData &value) override;
1691+
1692+private:
1693+ void registerWindow(QWindow *window);
1694+ void updateHotspot();
1695+
1696+ QPointer<QWindow> m_registeredWindow;
1697+ QString m_cursorName;
1698+ QString m_themeName;
1699+ int m_hotspotX;
1700+ int m_hotspotY;
1701+};
1702+
1703+#endif // MOUSEPOINTER_H
1704
1705=== added file 'plugins/Cursor/plugin.cpp'
1706--- plugins/Cursor/plugin.cpp 1970-01-01 00:00:00 +0000
1707+++ plugins/Cursor/plugin.cpp 2015-10-19 14:53:23 +0000
1708@@ -0,0 +1,39 @@
1709+/*
1710+ * Copyright (C) 2015 Canonical, Ltd.
1711+ *
1712+ * This program is free software; you can redistribute it and/or modify
1713+ * it under the terms of the GNU General Public License as published by
1714+ * the Free Software Foundation; version 3.
1715+ *
1716+ * This program is distributed in the hope that it will be useful,
1717+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1718+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1719+ * GNU General Public License for more details.
1720+ *
1721+ * You should have received a copy of the GNU General Public License
1722+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1723+ */
1724+
1725+// Qt
1726+#include <QtQml/qqml.h>
1727+#include <QQmlContext>
1728+
1729+// self
1730+#include "plugin.h"
1731+
1732+// local
1733+#include "CursorImageProvider.h"
1734+#include "MousePointer.h"
1735+
1736+void CursorPlugin::registerTypes(const char *uri)
1737+{
1738+ Q_ASSERT(uri == QLatin1String("Cursor"));
1739+ qmlRegisterType<MousePointer>(uri, 1, 0, "MousePointer");
1740+}
1741+
1742+void CursorPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
1743+{
1744+ QQmlExtensionPlugin::initializeEngine(engine, uri);
1745+
1746+ engine->addImageProvider(QLatin1String("cursor"), new CursorImageProvider());
1747+}
1748
1749=== added file 'plugins/Cursor/plugin.h'
1750--- plugins/Cursor/plugin.h 1970-01-01 00:00:00 +0000
1751+++ plugins/Cursor/plugin.h 2015-10-19 14:53:23 +0000
1752@@ -0,0 +1,33 @@
1753+/*
1754+ * Copyright (C) 2015 Canonical, Ltd.
1755+ *
1756+ * This program is free software; you can redistribute it and/or modify
1757+ * it under the terms of the GNU General Public License as published by
1758+ * the Free Software Foundation; version 3.
1759+ *
1760+ * This program is distributed in the hope that it will be useful,
1761+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1762+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1763+ * GNU General Public License for more details.
1764+ *
1765+ * You should have received a copy of the GNU General Public License
1766+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1767+ */
1768+
1769+#ifndef CURSOR_PLUGIN_H
1770+#define CURSOR_PLUGIN_H
1771+
1772+#include <QtQml/QQmlEngine>
1773+#include <QtQml/QQmlExtensionPlugin>
1774+
1775+class CursorPlugin : public QQmlExtensionPlugin
1776+{
1777+ Q_OBJECT
1778+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
1779+
1780+public:
1781+ void registerTypes(const char *uri) override;
1782+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
1783+};
1784+
1785+#endif // CURSOR_PLUGIN_H
1786
1787=== added file 'plugins/Cursor/qmldir'
1788--- plugins/Cursor/qmldir 1970-01-01 00:00:00 +0000
1789+++ plugins/Cursor/qmldir 2015-10-19 14:53:23 +0000
1790@@ -0,0 +1,3 @@
1791+module Cursor
1792+plugin Cursor-qml
1793+Cursor 1.0 Cursor.qml
1794
1795=== modified file 'qml/Greeter/Greeter.qml'
1796--- qml/Greeter/Greeter.qml 2015-09-02 13:06:56 +0000
1797+++ qml/Greeter/Greeter.qml 2015-10-19 14:53:23 +0000
1798@@ -251,7 +251,7 @@
1799
1800 // event eater
1801 // Nothing should leak to items behind the greeter
1802- MouseArea { anchors.fill: parent }
1803+ MouseArea { anchors.fill: parent; hoverEnabled: true }
1804
1805 Loader {
1806 id: loader
1807
1808=== modified file 'qml/Launcher/Launcher.qml'
1809--- qml/Launcher/Launcher.qml 2015-09-02 14:18:40 +0000
1810+++ qml/Launcher/Launcher.qml 2015-10-19 14:53:23 +0000
1811@@ -188,6 +188,7 @@
1812 bottom: parent.bottom
1813 }
1814 enabled: root.shadeBackground && root.state == "visible"
1815+ visible: enabled // otherwise it will get in the way of cursor selection for some reason
1816 onPressed: {
1817 root.state = ""
1818 }
1819
1820=== modified file 'qml/Panel/Panel.qml'
1821--- qml/Panel/Panel.qml 2015-06-29 03:58:22 +0000
1822+++ qml/Panel/Panel.qml 2015-10-19 14:53:23 +0000
1823@@ -48,6 +48,7 @@
1824 MouseArea {
1825 anchors.fill: parent
1826 onClicked: if (indicators.fullyOpened) indicators.hide();
1827+ hoverEnabled: true // should also eat hover events, otherwise they will pass through
1828 }
1829 }
1830
1831
1832=== modified file 'qml/Shell.qml'
1833--- qml/Shell.qml 2015-09-22 14:23:44 +0000
1834+++ qml/Shell.qml 2015-10-19 14:53:23 +0000
1835@@ -41,6 +41,7 @@
1836 import Unity.Session 0.1
1837 import Unity.DashCommunicator 0.1
1838 import Unity.Indicators 0.1 as Indicators
1839+import Cursor 1.0
1840
1841
1842 Item {
1843@@ -63,7 +64,7 @@
1844 function updateFocusedAppOrientationAnimated() {
1845 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
1846 }
1847- property bool hasMouse
1848+ property bool hasMouse: false
1849
1850 // to be read from outside
1851 readonly property int mainAppWindowOrientationAngle:
1852@@ -190,11 +191,6 @@
1853 onScreenshotTriggered: screenGrabber.capture();
1854 }
1855
1856- ScreenGrabber {
1857- id: screenGrabber
1858- z: dialogs.z + 10
1859- }
1860-
1861 GlobalShortcut {
1862 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
1863 }
1864@@ -684,9 +680,20 @@
1865 onShowHome: showHome()
1866 }
1867
1868+ ScreenGrabber {
1869+ id: screenGrabber
1870+ z: dialogs.z + 10
1871+ }
1872+
1873+ Cursor {
1874+ id: cursor
1875+ visible: shell.hasMouse
1876+ z: screenGrabber.z + 1
1877+ }
1878+
1879 Rectangle {
1880 id: shutdownFadeOutRectangle
1881- z: screenGrabber.z + 10
1882+ z: cursor.z + 1
1883 enabled: false
1884 visible: false
1885 color: "black"
1886
1887=== modified file 'qml/Stages/DecoratedWindow.qml'
1888--- qml/Stages/DecoratedWindow.qml 2015-09-08 10:32:28 +0000
1889+++ qml/Stages/DecoratedWindow.qml 2015-10-19 14:53:23 +0000
1890@@ -31,9 +31,10 @@
1891 property bool highlightShown: false
1892 property real shadowOpacity: 1
1893
1894- signal close();
1895- signal maximize();
1896- signal minimize();
1897+ signal close()
1898+ signal maximize()
1899+ signal minimize()
1900+ signal decorationPressed()
1901
1902 BorderImage {
1903 anchors {
1904@@ -61,6 +62,7 @@
1905
1906 WindowDecoration {
1907 id: decoration
1908+ target: root.parent
1909 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
1910 anchors { left: parent.left; top: parent.top; right: parent.right }
1911 height: units.gu(3)
1912@@ -68,6 +70,7 @@
1913 onClose: root.close();
1914 onMaximize: root.maximize();
1915 onMinimize: root.minimize();
1916+ onPressed: root.decorationPressed();
1917 visible: decorationShown
1918 }
1919
1920
1921=== modified file 'qml/Stages/DesktopStage.qml'
1922--- qml/Stages/DesktopStage.qml 2015-09-18 15:28:07 +0000
1923+++ qml/Stages/DesktopStage.qml 2015-10-19 14:53:23 +0000
1924@@ -86,6 +86,11 @@
1925 var index = indexOf(focusedAppId);
1926 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
1927 }
1928+ onFocusedAppDelegateChanged: { // restore the window from minimization when we focus it (e.g. using spread)
1929+ if (priv.focusedAppDelegate && priv.focusedAppDelegate.minimized) {
1930+ priv.focusedAppDelegate.unmaximize()
1931+ }
1932+ }
1933
1934 function indexOf(appId) {
1935 for (var i = 0; i < ApplicationManager.count; i++) {
1936@@ -138,9 +143,6 @@
1937 height: units.gu(50)
1938 focus: model.appId === priv.focusedAppId
1939
1940- readonly property int minWidth: units.gu(10)
1941- readonly property int minHeight: units.gu(10)
1942-
1943 property bool maximized: false
1944 property bool minimized: false
1945
1946@@ -215,15 +217,12 @@
1947 when: index == spread.highlightedIndex && blurLayer.ready
1948 }
1949
1950- WindowMoveResizeArea {
1951- id: windowMoveResizeArea
1952+ WindowResizeArea {
1953 target: appDelegate
1954- minWidth: appDelegate.minWidth
1955- minHeight: appDelegate.minHeight
1956- resizeHandleWidth: units.gu(2)
1957+ minWidth: units.gu(10)
1958+ minHeight: units.gu(10)
1959+ borderThickness: units.gu(2)
1960 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
1961-
1962- onPressed: { ApplicationManager.focusApplication(model.appId) }
1963 }
1964
1965 DecoratedWindow {
1966@@ -240,6 +239,7 @@
1967 onClose: ApplicationManager.stopApplication(model.appId)
1968 onMaximize: appDelegate.maximize()
1969 onMinimize: appDelegate.minimize()
1970+ onDecorationPressed: ApplicationManager.focusApplication(model.appId)
1971 }
1972 }
1973 }
1974
1975=== modified file 'qml/Stages/WindowDecoration.qml'
1976--- qml/Stages/WindowDecoration.qml 2015-03-13 19:18:35 +0000
1977+++ qml/Stages/WindowDecoration.qml 2015-10-19 14:53:23 +0000
1978@@ -1,5 +1,5 @@
1979 /*
1980- * Copyright (C) 2014 Canonical, Ltd.
1981+ * Copyright (C) 2014-2015 Canonical, Ltd.
1982 *
1983 * This program is free software; you can redistribute it and/or modify
1984 * it under the terms of the GNU General Public License as published by
1985@@ -12,25 +12,54 @@
1986 *
1987 * You should have received a copy of the GNU General Public License
1988 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1989- *
1990- * Authors: Michael Zanetti <michael.zanetti@canonical.com>
1991 */
1992
1993 import QtQuick 2.3
1994+import Unity.Application 0.1 // For Mir singleton
1995 import Ubuntu.Components 1.1
1996 import "../Components"
1997
1998-Item {
1999+MouseArea {
2000 id: root
2001 clip: true
2002
2003+ property Item target
2004 property alias title: titleLabel.text
2005 property bool active: false
2006+ hoverEnabled: true
2007
2008 signal close()
2009 signal minimize()
2010 signal maximize()
2011
2012+ QtObject {
2013+ id: priv
2014+ property real distanceX
2015+ property real distanceY
2016+ property bool dragging
2017+ }
2018+
2019+ onPressedChanged: {
2020+ if (pressed) {
2021+ var pos = mapToItem(root.target, mouseX, mouseY);
2022+ priv.distanceX = pos.x;
2023+ priv.distanceY = pos.y;
2024+ priv.dragging = true;
2025+ } else {
2026+ priv.dragging = false;
2027+ Mir.cursorName = "";
2028+ }
2029+ }
2030+
2031+ onPositionChanged: {
2032+ if (priv.dragging) {
2033+ Mir.cursorName = "grabbing";
2034+ var pos = mapToItem(root.target.parent, mouseX, mouseY);
2035+ root.target.x = pos.x - priv.distanceX;
2036+ root.target.y = pos.y - priv.distanceY;
2037+ }
2038+ }
2039+
2040 Rectangle {
2041 anchors.fill: parent
2042 anchors.bottomMargin: -radius
2043
2044=== renamed file 'qml/Stages/WindowMoveResizeArea.qml' => 'qml/Stages/WindowResizeArea.qml'
2045--- qml/Stages/WindowMoveResizeArea.qml 2015-09-08 10:32:28 +0000
2046+++ qml/Stages/WindowResizeArea.qml 2015-10-19 14:53:23 +0000
2047@@ -12,18 +12,19 @@
2048 *
2049 * You should have received a copy of the GNU General Public License
2050 * along with this program. If not, see <http://www.gnu.org/licenses/>.
2051- *
2052- * Authors: Michael Zanetti <michael.zanetti@canonical.com>
2053 */
2054
2055 import QtQuick 2.3
2056 import Ubuntu.Components 1.1
2057 import Utils 0.1
2058+import Unity.Application 0.1 // for Mir.cursorName
2059
2060 MouseArea {
2061 id: root
2062 anchors.fill: target
2063- anchors.margins: -resizeHandleWidth
2064+ anchors.margins: -borderThickness
2065+
2066+ hoverEnabled: true
2067
2068 property var windowStateStorage: WindowStateStorage
2069
2070@@ -31,24 +32,10 @@
2071 // The area will anchor to it and manage move and resize events
2072 property Item target: null
2073 property string windowId: ""
2074- property int resizeHandleWidth: 0
2075+ property int borderThickness: 0
2076 property int minWidth: 0
2077 property int minHeight: 0
2078
2079- QtObject {
2080- id: priv
2081- readonly property int windowWidth: root.width - root.resizeHandleWidth * 2
2082- readonly property int windowHeight: root.height - resizeHandleWidth * 2
2083-
2084- property var startPoint
2085-
2086- property bool resizeTop: false
2087- property bool resizeBottom: false
2088- property bool resizeLeft: false
2089- property bool resizeRight: false
2090-
2091- }
2092-
2093 Component.onCompleted: {
2094 var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
2095 if (windowState !== undefined) {
2096@@ -59,51 +46,137 @@
2097 }
2098 }
2099
2100- onPressed: {
2101- priv.startPoint = Qt.point(mouse.x, mouse.y);
2102- priv.resizeTop = mouseY < root.resizeHandleWidth;
2103- priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
2104- priv.resizeLeft = mouseX < root.resizeHandleWidth;
2105- priv.resizeRight = mouseX > (root.width - root.resizeHandleWidth);
2106- }
2107-
2108- onPositionChanged: {
2109- var currentPoint = Qt.point(mouse.x, mouse.y);
2110- var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
2111- var moveDiff = Qt.point(0, 0);
2112- var sizeDiff = Qt.point(0, 0);
2113- var maxSizeDiff = Qt.point(root.minWidth - root.target.width, root.minHeight - root.target.height)
2114-
2115- if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
2116- if (priv.resizeTop) {
2117- sizeDiff.y = Math.max(maxSizeDiff.y, -currentPoint.y + priv.startPoint.y)
2118- moveDiff.y = -sizeDiff.y
2119- }
2120- if (priv.resizeBottom) {
2121- sizeDiff.y = Math.max(maxSizeDiff.y, currentPoint.y - priv.startPoint.y)
2122- priv.startPoint.y += sizeDiff.y
2123- }
2124- if (priv.resizeLeft) {
2125- sizeDiff.x = Math.max(maxSizeDiff.x, -currentPoint.x + priv.startPoint.x)
2126- moveDiff.x = -sizeDiff.x
2127- }
2128- if (priv.resizeRight) {
2129- sizeDiff.x = Math.max(maxSizeDiff.x, currentPoint.x - priv.startPoint.x)
2130- priv.startPoint.x += sizeDiff.x
2131- }
2132-
2133- target.x += moveDiff.x;
2134- target.y += moveDiff.y;
2135- target.width += sizeDiff.x;
2136- target.height += sizeDiff.y;
2137- } else {
2138- target.x += mouseDiff.x;
2139- target.y += mouseDiff.y;
2140- }
2141-
2142- }
2143-
2144 Component.onDestruction: {
2145 windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
2146 }
2147+
2148+ QtObject {
2149+ id: d
2150+ property bool leftBorder: false
2151+ property bool rightBorder: false
2152+ property bool topBorder: false
2153+ property bool bottomBorder: false
2154+
2155+ property bool dragging: false
2156+ property real startMousePosX
2157+ property real startMousePosY
2158+ property real startX
2159+ property real startY
2160+ property real startWidth
2161+ property real startHeight
2162+
2163+ property string cursorName: {
2164+ if (root.containsMouse || root.pressed) {
2165+ if (leftBorder && !topBorder && !bottomBorder) {
2166+ return "left_side";
2167+ } else if (rightBorder && !topBorder && !bottomBorder) {
2168+ return "right_side";
2169+ } else if (topBorder && !leftBorder && !rightBorder) {
2170+ return "top_side";
2171+ } else if (bottomBorder && !leftBorder && !rightBorder) {
2172+ return "bottom_side";
2173+ } else if (leftBorder && topBorder) {
2174+ return "top_left_corner";
2175+ } else if (leftBorder && bottomBorder) {
2176+ return "bottom_left_corner";
2177+ } else if (rightBorder && topBorder) {
2178+ return "top_right_corner";
2179+ } else if (rightBorder && bottomBorder) {
2180+ return "bottom_right_corner";
2181+ } else {
2182+ return "";
2183+ }
2184+ } else {
2185+ return "";
2186+ }
2187+ }
2188+ onCursorNameChanged: {
2189+ Mir.cursorName = cursorName;
2190+ }
2191+
2192+ function updateBorders() {
2193+ leftBorder = mouseX <= borderThickness;
2194+ rightBorder = mouseX >= width - borderThickness;
2195+ topBorder = mouseY <= borderThickness;
2196+ bottomBorder = mouseY >= height - borderThickness;
2197+ }
2198+ }
2199+
2200+ onPressedChanged: {
2201+ var pos = mapToItem(target.parent, mouseX, mouseY);
2202+
2203+ if (pressed) {
2204+ d.updateBorders();
2205+ var pos = mapToItem(root.target.parent, mouseX, mouseY);
2206+ d.startMousePosX = pos.x;
2207+ d.startMousePosY = pos.y;
2208+ d.startX = target.x;
2209+ d.startY = target.y;
2210+ d.startWidth = target.width;
2211+ d.startHeight = target.height;
2212+ d.dragging = true;
2213+ } else {
2214+ d.dragging = false;
2215+ if (containsMouse) {
2216+ d.updateBorders();
2217+ }
2218+ }
2219+ }
2220+
2221+ onEntered: {
2222+ if (!pressed) {
2223+ d.updateBorders();
2224+ }
2225+ }
2226+
2227+ onPositionChanged: {
2228+ if (!pressed) {
2229+ d.updateBorders();
2230+ }
2231+
2232+ if (!d.dragging) {
2233+ return;
2234+ }
2235+
2236+ var pos = mapToItem(target.parent, mouse.x, mouse.y);
2237+
2238+ var deltaX = pos.x - d.startMousePosX;
2239+ var deltaY = pos.y - d.startMousePosY;
2240+
2241+ if (d.leftBorder) {
2242+ var newTargetX = d.startX + deltaX;
2243+ if (target.x + target.width > newTargetX + minWidth) {
2244+ target.width = target.x + target.width - newTargetX;
2245+ target.x = newTargetX;
2246+ } else {
2247+ target.x = target.x + target.width - minWidth;
2248+ target.width = minWidth;
2249+ }
2250+
2251+ } else if (d.rightBorder) {
2252+ if (d.startWidth + deltaX >= minWidth) {
2253+ target.width = d.startWidth + deltaX;
2254+ } else {
2255+ target.width = minWidth;
2256+ }
2257+ }
2258+
2259+ if (d.topBorder) {
2260+ var newTargetY = d.startY + deltaY;
2261+ if (target.y + target.height > newTargetY + minHeight) {
2262+ target.height = target.y + target.height - newTargetY;
2263+ target.y = newTargetY;
2264+ } else {
2265+ target.y = target.y + target.height - minHeight;
2266+ target.height = minHeight;
2267+ }
2268+
2269+ } else if (d.bottomBorder) {
2270+ if (d.startHeight + deltaY >= minHeight) {
2271+ target.height = d.startHeight + deltaY;
2272+ } else {
2273+ target.height = minHeight;
2274+ }
2275+ }
2276+ }
2277 }
2278
2279=== modified file 'tests/mocks/CMakeLists.txt'
2280--- tests/mocks/CMakeLists.txt 2015-08-06 22:45:56 +0000
2281+++ tests/mocks/CMakeLists.txt 2015-10-19 14:53:23 +0000
2282@@ -29,6 +29,7 @@
2283 endmacro()
2284
2285 add_subdirectory(AccountsService)
2286+add_subdirectory(Cursor)
2287 add_subdirectory(GSettings.1.0)
2288 add_subdirectory(indicator-service)
2289 add_subdirectory(libusermetrics)
2290
2291=== added directory 'tests/mocks/Cursor'
2292=== added file 'tests/mocks/Cursor/CMakeLists.txt'
2293--- tests/mocks/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000
2294+++ tests/mocks/Cursor/CMakeLists.txt 2015-10-19 14:53:23 +0000
2295@@ -0,0 +1,1 @@
2296+add_unity8_mock(Cursor 1.0 Cursor PREFIX mocks)
2297
2298=== added file 'tests/mocks/Cursor/Cursor.qml'
2299--- tests/mocks/Cursor/Cursor.qml 1970-01-01 00:00:00 +0000
2300+++ tests/mocks/Cursor/Cursor.qml 2015-10-19 14:53:23 +0000
2301@@ -0,0 +1,20 @@
2302+/*
2303+ * Copyright (C) 2015 Canonical, Ltd.
2304+ *
2305+ * This program is free software; you can redistribute it and/or modify
2306+ * it under the terms of the GNU General Public License as published by
2307+ * the Free Software Foundation; version 3.
2308+ *
2309+ * This program is distributed in the hope that it will be useful,
2310+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2311+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2312+ * GNU General Public License for more details.
2313+ *
2314+ * You should have received a copy of the GNU General Public License
2315+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2316+ */
2317+
2318+import QtQuick 2.4
2319+
2320+Item {
2321+}
2322
2323=== added file 'tests/mocks/Cursor/qmldir'
2324--- tests/mocks/Cursor/qmldir 1970-01-01 00:00:00 +0000
2325+++ tests/mocks/Cursor/qmldir 2015-10-19 14:53:23 +0000
2326@@ -0,0 +1,2 @@
2327+module Cursor
2328+Cursor 1.0 Cursor.qml
2329
2330=== modified file 'tests/qmltests/CMakeLists.txt'
2331--- tests/qmltests/CMakeLists.txt 2015-09-19 07:37:52 +0000
2332+++ tests/qmltests/CMakeLists.txt 2015-10-19 14:53:23 +0000
2333@@ -78,7 +78,7 @@
2334 add_unity8_qmltest(Stages SurfaceContainer)
2335 add_unity8_qmltest(Stages SessionContainer)
2336 add_unity8_qmltest(Stages TabletStage)
2337-add_unity8_qmltest(Stages WindowMoveResizeArea)
2338+add_unity8_qmltest(Stages WindowResizeArea)
2339 add_unity8_qmltest(Stages Splash)
2340 add_unity8_qmltest(Tutorial Tutorial LIGHTDM)
2341 add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")
2342
2343=== renamed file 'tests/qmltests/Stages/tst_WindowMoveResizeArea.qml' => 'tests/qmltests/Stages/tst_WindowResizeArea.qml'
2344--- tests/qmltests/Stages/tst_WindowMoveResizeArea.qml 2015-09-07 13:45:50 +0000
2345+++ tests/qmltests/Stages/tst_WindowResizeArea.qml 2015-10-19 14:53:23 +0000
2346@@ -17,7 +17,7 @@
2347 import QtQuick 2.1
2348 import QtQuick.Layouts 1.1
2349 import QtTest 1.0
2350-import Unity.Test 0.1 as UT
2351+import Unity.Test 0.1
2352 import ".."
2353 import "../../../qml/Stages"
2354 import Ubuntu.Components 0.1
2355@@ -36,8 +36,8 @@
2356
2357 Item {
2358 id: fakeWindow
2359- property alias minWidth: moveResizeArea.minWidth
2360- property alias minHeight: moveResizeArea.minHeight
2361+ property alias minWidth: windowResizeArea.minWidth
2362+ property alias minHeight: windowResizeArea.minHeight
2363 x: units.gu(20)
2364 y: units.gu(20)
2365 height: units.gu(20)
2366@@ -47,23 +47,27 @@
2367 onWindowHeightChanged: height = windowHeight
2368 onWindowWidthChanged: width = windowWidth
2369
2370- WindowMoveResizeArea {
2371- id: moveResizeArea
2372+ WindowResizeArea {
2373+ id: windowResizeArea
2374 target: fakeWindow
2375- resizeHandleWidth: units.gu(0.5)
2376+ borderThickness: units.gu(2)
2377 minWidth: units.gu(15)
2378 minHeight: units.gu(10)
2379 windowId: "test-window-id"
2380 }
2381
2382 Rectangle {
2383- anchors.fill: moveResizeArea
2384+ anchors.fill: windowResizeArea
2385 color: "red"
2386 }
2387
2388 Rectangle {
2389 anchors.fill: fakeWindow
2390 color: "blue"
2391+ MouseArea {
2392+ anchors.fill: parent
2393+ hoverEnabled: true
2394+ }
2395 }
2396 }
2397 }
2398@@ -73,8 +77,13 @@
2399 sourceComponent: fakeWindowComponent
2400 }
2401
2402- UT.UnityTestCase {
2403- name: "WindowMoveResizeArea"
2404+ MouseTouchEmulationCheckbox {
2405+ checked: false
2406+ color: "black"
2407+ }
2408+
2409+ UnityTestCase {
2410+ name: "WindowResizeArea"
2411 when: windowShown
2412
2413 function init() {
2414@@ -84,34 +93,6 @@
2415 fakeWindow.height = units.gu(20)
2416 }
2417
2418- function test_dragWindow_data() {
2419- return [
2420- { tag: "up", dx: 0, dy: units.gu(-10) },
2421- { tag: "down", dx: 0, dy: units.gu(10) },
2422- { tag: "left", dx: units.gu(-10), dy: 0 },
2423- { tag: "right", dx: units.gu(10), dy: 0 },
2424- { tag: "right/down", dx: units.gu(10), dy: units.gu(10) },
2425- { tag: "left/down", dx: units.gu(-10), dy: units.gu(10) }
2426- ]
2427- }
2428-
2429- function test_dragWindow(data) {
2430- var initialWindowX = fakeWindow.x;
2431- var initialWindowY = fakeWindow.y;
2432- var initialWindowWidth = fakeWindow.width
2433- var initialWindowHeight = fakeWindow.height
2434-
2435- var startDragX = initialWindowX + fakeWindow.width / 2;
2436- var startDragY = initialWindowY + fakeWindow.height / 2;
2437- mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10)
2438-
2439- tryCompare(fakeWindow, "x", initialWindowX + data.dx)
2440- tryCompare(fakeWindow, "y", initialWindowX + data.dy)
2441-
2442- compare(fakeWindow.height, initialWindowHeight);
2443- compare(fakeWindow.width, initialWindowWidth);
2444- }
2445-
2446 function test_resizeWindowRightBottom_data() {
2447 return [
2448 { tag: "width", dx: units.gu(10), dy: 0 },
2449@@ -166,30 +147,6 @@
2450 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
2451 }
2452
2453- function test_saveRestorePosition() {
2454- var initialWindowX = fakeWindow.x;
2455- var initialWindowY = fakeWindow.y;
2456- var initialWindowWidth = fakeWindow.width;
2457- var initialWindowHeight = fakeWindow.height;
2458-
2459- var moveDelta = units.gu(5);
2460- var startDragX = initialWindowX + fakeWindow.width / 2;
2461- var startDragY = initialWindowY + fakeWindow.height / 2;
2462- mouseFlick(root, startDragX, startDragY, startDragX + moveDelta, startDragY + moveDelta, true, true, units.gu(.5), 10)
2463-
2464- tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
2465- tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
2466-
2467- // This will destroy the window and recreate it
2468- windowLoader.active = false;
2469- waitForRendering(root);
2470- windowLoader.active = true;
2471-
2472- // Make sure it's again where we left it before destroying
2473- tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
2474- tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
2475- }
2476-
2477 function test_saveRestoreSize() {
2478 var initialWindowX = fakeWindow.x;
2479 var initialWindowY = fakeWindow.y;
2480
2481=== modified file 'tests/qmltests/tst_OrientedShell.qml'
2482--- tests/qmltests/tst_OrientedShell.qml 2015-09-21 13:37:47 +0000
2483+++ tests/qmltests/tst_OrientedShell.qml 2015-10-19 14:53:23 +0000
2484@@ -264,6 +264,10 @@
2485 text: "Usage Mode"
2486 model: ["Staged", "Windowed", "Automatic"]
2487 }
2488+ MouseTouchEmulationCheckbox {
2489+ checked: true
2490+ color: "white"
2491+ }
2492 Button {
2493 text: "Switch fullscreen"
2494 activeFocusOnPress: false

Subscribers

People subscribed via source and target branches