Merge lp:~albaguirre/qtubuntu/use-mir-surface-apis into lp:qtubuntu

Proposed by Alberto Aguirre on 2015-08-06
Status: Merged
Approved by: Daniel d'Andrada on 2015-10-28
Approved revision: 302
Merged at revision: 290
Proposed branch: lp:~albaguirre/qtubuntu/use-mir-surface-apis
Merge into: lp:qtubuntu
Prerequisite: lp:~dandrader/qtubuntu/busySwap-lp1473720
Diff against target: 1390 lines (+587/-421)
9 files modified
src/ubuntumirclient/glcontext.cpp (+1/-13)
src/ubuntumirclient/input.cpp (+37/-10)
src/ubuntumirclient/input.h (+10/-5)
src/ubuntumirclient/integration.cpp (+16/-19)
src/ubuntumirclient/nativeinterface.cpp (+5/-5)
src/ubuntumirclient/screen.cpp (+9/-6)
src/ubuntumirclient/screen.h (+2/-0)
src/ubuntumirclient/window.cpp (+491/-350)
src/ubuntumirclient/window.h (+16/-13)
To merge this branch: bzr merge lp:~albaguirre/qtubuntu/use-mir-surface-apis
Reviewer Review Type Date Requested Status
Daniel d'Andrada (community) Approve on 2015-10-28
PS Jenkins bot continuous-integration 2015-08-06 Approve on 2015-10-27
Gerry Boland 2015-08-06 Abstain on 2015-10-21
Alberto Aguirre Abstain on 2015-10-16
Review via email: mp+267228@code.launchpad.net

This proposal supersedes a proposal from 2015-06-17.

Commit Message

Add support for Qt popups and dialog windows.

I have done some refactoring, cleanup and some bug fixing.

- An UbuntuWindow internally creates UbuntuSurface
- UbuntuSurface is responsible for creating the mir surfaces and calling mir apis
- Fix mutex locking (now really protecting access to mSurface) was not locked properly (QMutexLocker temporaries were created : QMutexLocker(&d->mutex) instead of QMutexLocker lock(&d->mutex).
- Handling resize events from the server has been improved.
-- Old resize events are dropped - meaning no redraw requests are issued if we know there are newer resize events in the queue
-- Redraw requests are never issued through the rendering thread only through the Qt event dispatch thread.
-- No flushing of event queue which leads to fewer interruptions in other surfaces (specially noticeable on surfaces that do animations).
- Workaround QtCreator not assigning a parent to its menu bar menus
-- The last focused window is tracked and used if a Qt popup is created without a parent
- Client requested resizes (through setGeometry) is now supported
- Resizing constraints are supported (propagateSizeHints)
- Visibility and window state are tracked separately
- Better focusing event handling
-- When an app has multiple windows, mir will send focus lost/gain pairs,
   in which case we need to peek into the queue to avoid telling Qt to unfocus all windows prematurely.

Description of the Change

Add support for Qt popups and dialog windows.

I have done some refactoring, cleanup and some bug fixing.

- An UbuntuWindow internally creates UbuntuSurface
- UbuntuSurface is responsible for creating the mir surfaces and calling mir apis
- Fix mutex locking (now really protecting access to mSurface) was not locked properly (QMutexLocker temporaries were created : QMutexLocker(&d->mutex) instead of QMutexLocker lock(&d->mutex).
- Handling resize events from the server has been improved.
-- Old resize events are dropped - meaning no redraw requests are issued if we know there are newer resize events in the queue
-- Redraw requests are never issued through the rendering thread only through the Qt event dispatch thread.
-- No flushing of event queue which leads to fewer interruptions in other surfaces (specially noticeable on surfaces that do animations).
- Workaround QtCreator not assigning a parent to its menu bar menus
-- The last focused window is tracked and used if a Qt popup is created without a parent
- Client requested resizes (through setGeometry) is now supported
- Resizing constraints are supported (propagateSizeHints)
- Visibility and window state are tracked separately
- Better focusing event handling
-- When an app has multiple windows, mir will send focus lost/gain pairs,
   in which case we need to peek into the queue to avoid telling Qt to unfocus all windows prematurely.

To post a comment you must log in.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

+++ src/ubuntumirclient/window.cpp
+ MirSurface * const mirSurface_;
+ EGLSurface eglSurface_;
Class members are named mSomething in qtubuntu, please adopt that naming scheme.

+MirPixelFormat defaultPixelFormat(MirConnection *connection)
....
+ return format;
+}
+}
That last brace is the end of the namespace, would you please stick a comment like "// namespace" after it, helps me see the namespace end more clearly.

+ if (state == Qt::WindowFullScreen) {
+ auto displayConfig = mir_connection_create_display_config(connection);
+ if (displayConfig->num_outputs > 0) {
+ auto outputId = displayConfig->outputs[0].output_id;
Think we should add a FIXME here, as we're guessing there's only 1 display at the moment.

+void UbuntuWindowPrivate::setSurfaceState(Qt::WindowState new_state)
+{
+ mir_wait_for(mir_surface_set_state(surface->mirSurface(), qtWindowStateToMirSurfaceState(new_state)));
Does this need to be sync?

+ geom.setY(panelHeight);
/me wants to kill this so badly! :)

#if !defined(QT_NO_DEBUG)
- LOG("panelHeight: '%d'", panelHeight);
+ DLOG("[ubuntumirclient QPA] panelHeight: '%d'", panelHeight);
The QT_NO_DEBUG should do the same things as DLOG - only print when compiled in debug mode. So can probably drop the !defined(QT_NO_DEBUG)

The fun part is testing, I'll be at that soon. While I know we're missing the window management side of things, should I give some real apps a try? What would you consider fair testing?

review: Needs Fixing
Alberto Aguirre (albaguirre) wrote : Posted in a previous version of this proposal

@Gerry, yeah any real apps. I tried QtCreator in the desktop and mir_demo_server. It's not quite there yet - the big one is the relative positioning for the embedded window.

Alberto Aguirre (albaguirre) wrote : Posted in a previous version of this proposal

>+ geom.setY(panelHeight);
>/me wants to kill this so badly! :)

Can we? We added a debug mirclient library to support autopilot testing a while back (mir_toolkit/debug/surface.h - mir_debug_surface_coords_to_screen(...))

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Alberto Aguirre (albaguirre) wrote : Posted in a previous version of this proposal

>+void UbuntuWindowPrivate::setSurfaceState(Qt::WindowState new_state)
>+{
>+ mir_wait_for(mir_surface_set_state(surface->mirSurface(), >qtWindowStateToMirSurfaceState(new_state)));
>Does this need to be sync?

As in does the call really need to wait for the wait handle? or call a mir_surface_set_state_sync api? (which does not exist in any case)

In any case the wait for set_state is pre-existing, so I didn't change it.

The rest should be fixed now.

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Gerry Boland (gerboland) wrote :

Just tested, no regressions on touch, let's go with it

review: Approve
Gerry Boland (gerboland) wrote :

Hmm crashing on startup. Seems expose event happening before the GL context is set up

review: Needs Fixing
Alberto Aguirre (albaguirre) wrote :

OK, will retest. Startup of touch? Or startup of an app?

Gerry Boland (gerboland) wrote :

Startup of app

Alberto Aguirre (albaguirre) wrote :

> Hmm crashing on startup. Seems expose event happening before the GL context is
> set up

Fixed - I made a typo during the variable renaming, I was returning the mir surface instead of the EGL surface.

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Alberto Aguirre (albaguirre) wrote :

Actually this looks like it will need some more work. After the rebase and mir 0.14 things are not working quite right...

There's some issues:
- Displaying a menu (http://pastebin.ubuntu.com/12049608/)
 -- No rendering is done (looks grey) until the mouse hovers the menu area - adding a handleExposeEvent during setGeometry helps but I don't think that's the right place for it.

-- Qt destroys the menu window immediately after shown - mir sends a focus false event for the parent surface and focus true event for the menu surface which is translated to handleWindowActivated(0, Qt::ActiveWindowFocusReason) and handleWindowActivated(menu_surface, Qt::ActiveWindowFocusReason).
 Since the menu surface is already visible the first call makes Qt destroy the menu surface.
-- mir is not destroying submenu surfaces when they lose focus

Visibility and window state are both linked to the mir surface state - if a window is made invisible, this makes the mir surface always stay in the minimized state.

review: Needs Fixing
review: Abstain
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Alberto Aguirre (albaguirre) wrote :

Updated it to address issues mentioned above.

Daniel d'Andrada (dandrader) wrote :

Not the easiest diff to follow as you also do a lot of refactoring along with it.

Daniel d'Andrada (dandrader) wrote :

There are some tab characters in UbuntuWindow::handleSurfaceResize and UbuntuWindow::handleSurfaceFocusChange

----

Could you please bring back the frameNumber debug variable?

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

"""
    if (!focused) {
     // Mir will send a pair of events when a new surface is focused, one for the surface
     // that was unfocused and one for the surface what just gained focus. There is no
     // equivalent Qt API to "unfocus" a single window only handleWindowActivated(NULL, ...)
     // which has different semantics (all windows lose focus) which is problematic for popups.
     // Hence unfocused events are ignored.
     return;
    }
"""

This is a true regression, will break current behavior and create bugs.

Look at these scenarios:

Scenario 1 (focus then unfocus):

* Application creates top level window A
* Window A gets focused.
  - qtubuntu sees that A is not active[1] and thus calls handleWindowActivated(A)
* Application creates pop-up window B
* Window B gets focused
  - qtubuntu sees that B is not active and thus calls handleWindowActivated(B)
* Window A gets unfocused.
  - qtbuntu sees that A is not active already, so no need to do anything

Scenario 2 (unfocus then focus):

* Application creates top level window A
* Window A gets focused.
  - qtubuntu sees that A is not active and thus calls handleWindowActivated(A)
* Application creates pop-up window B
* Window A gets unfocused.
  - qtbuntu sees that A is active and thus calls handleWindowActivated(nullptr)
* Window B gets focused
  - qtubuntu sees that B is not active and thus calls handleWindowActivated(B)

Could you make it work that way?

[1] see QGuiApplication::focusWindow()

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

There's a rogue tab char also in UbuntuWindow::onBuffersSwapped_threadSafe

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

"""
        // TODO: Use the new mir_surface_state_hidden state instead of mir_surface_state_minimized.
        // Will have to change qtmir and unity8 for that.
"""

Could you please keep that comment somewhere?

review: Needs Fixing
Gerry Boland (gerboland) wrote :

Handing over review to Daniel

review: Abstain
282. By Alberto Aguirre on 2015-10-21

Remove tab chars

283. By Alberto Aguirre on 2015-10-21

Do not remove framenumber debug logs

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Alberto Aguirre (albaguirre) wrote :

"This is a true regression, will break current behavior and create bugs."

Why would that be the case? Can you elaborate or describe a test case I can run?

handleSurfaceFocusChange is only called in response to a mir focus event.

284. By Alberto Aguirre on 2015-10-21

Restore TODO to use mir_surface_state_hidden

285. By Alberto Aguirre on 2015-10-21

Send expose event if Qt resizes a window after its visible.

Some window types created in QML are made visible before their final geometry is set. If the content of the window does not update, the first frame will be grey unless we inform Qt of the new exposed size.

286. By Alberto Aguirre on 2015-10-21

When a window is made visible expose the correct region in local coordinates.

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

On 21/10/2015 17:47, Alberto Aguirre wrote:
> "This is a true regression, will break current behavior and create bugs."
>
> Why would that be the case? Can you elaborate or describe a test case I can run?
>
> handleSurfaceFocusChange is only called in response to a mir focus event.
>
>
Switching focus between applications. The application that lost focus
would still think it's active.

287. By Alberto Aguirre on 2015-10-22

Track the last surface to receive input.

The last surface to receive input can be used as a parent when QT decides to create popups without parent information (such as the menubar menus in qtcreator).

288. By Alberto Aguirre on 2015-10-22

More cleanup.

Go back to creating surfaces at window creation time. On older QT releases, some menu surfaces
did not have transientParent information at create time.

289. By Alberto Aguirre on 2015-10-22

Fix usage of mutex to protect private state.

Previously "QMutexLocker(&d->mMutex)" was used which is just a temporary that gets immedietely deleted so there was no mutex protection.

290. By Alberto Aguirre on 2015-10-22

Initialize egl surface at construction time.

Windows are created at construction time again, so eglsurfaces are safe to be constructed there too.

291. By Alberto Aguirre on 2015-10-23

More cleanup

292. By Alberto Aguirre on 2015-10-23

Fix resize handling behavior.

We discard old accumulated resize events to avoid issuing too many redraw events greatly
improving resize performance.

293. By Alberto Aguirre on 2015-10-23

Use screen output id when creating a fullscreen mir surface.

294. By Alberto Aguirre on 2015-10-24

Avoid telling Qt that no windows have focus when receiving a focus lost/gained pair.

Mir may send a focus lost/gained pair of events whenever a new surface gains focus (previous surface loses focus).
Qt's handleWindowActivated API however only supports focusing a single window or not focusing anything.
When processing the surface focus lost message we need to peek into the pending events to check for any posted focus gained event so that only a single handleWindowActivated call is made.

295. By Alberto Aguirre on 2015-10-25

Support morphing non-modal to modal dialog.

QML dialogs are not parented when created. The transientParent will be set after creation but before the window is made visible.
Update the mir surface with the new parent information when the window is made visible

296. By Alberto Aguirre on 2015-10-25

Support updating title

297. By Alberto Aguirre on 2015-10-25

Add support for propagateSizeHints

298. By Alberto Aguirre on 2015-10-25

Update log message

299. By Alberto Aguirre on 2015-10-25

Slight cleanup

300. By Alberto Aguirre on 2015-10-26

Fix resize corner case

301. By Alberto Aguirre on 2015-10-26

merge lp:qtubuntu, fix conflicts

Alberto Aguirre (albaguirre) wrote :

> On 21/10/2015 17:47, Alberto Aguirre wrote:
> > "This is a true regression, will break current behavior and create bugs."
> >
> > Why would that be the case? Can you elaborate or describe a test case I can
> run?
> >
> > handleSurfaceFocusChange is only called in response to a mir focus event.
> >
> >
> Switching focus between applications. The application that lost focus
> would still think it's active.

OK I've fixed that now.

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

In UbuntuWindow::handleSurfaceResize

"""
    auto const numRepaints = mSurface->needsRepaint();
    DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) redraw %d times", window(), numRepaints);
    for (int i = 0; i < numRepaints; i++) {
        DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) repainting width=%d, height=%d", window(), geometry().size().width(), geometry().size().height());
        QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
    }
"""

You sure this works as you expect?

From what I understand, if you queue two expose events for processing, they will both be processed on the same event loop iteration and the second one will be pretty much a NO-OP as a redraw will already be scheduled at that point (ie, window will already be dirty/exposed).

review: Needs Information
Daniel d'Andrada (dandrader) wrote :

Since this is quite a big change, the commit message deserves more information. Copy-pasted the description text into it.

Daniel d'Andrada (dandrader) wrote :

Didn't stop any regressions on the phone or desktop.

Trying out qtcreator with mir_proving_server is mostly ok. Although I did get a crash when selecting text and dragging. Maybe during the creation some popup/tooltip I guess...

http://pastebin.ubuntu.com/12979688/

But it's is a major improvement over the current situation where qtcreator wouldn't run at all.

Daniel d'Andrada (dandrader) wrote :

s/Didn't stop/Didn't spot/

Alberto Aguirre (albaguirre) wrote :

@Daniel, I would suggest using mir_demo_server instead as that has the window management rules that are in mir core.

We consider mir_proving_server experimental.

Alberto Aguirre (albaguirre) wrote :

> In UbuntuWindow::handleSurfaceResize
>
> """
> auto const numRepaints = mSurface->needsRepaint();
> DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) redraw %d
> times", window(), numRepaints);
> for (int i = 0; i < numRepaints; i++) {
> DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) repainting
> width=%d, height=%d", window(), geometry().size().width(),
> geometry().size().height());
> QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(),
> geometry().size()));
> }
> """
>
> You sure this works as you expect?
>
> From what I understand, if you queue two expose events for processing, they
> will both be processed on the same event loop iteration and the second one
> will be pretty much a NO-OP as a redraw will already be scheduled at that
> point (ie, window will already be dirty/exposed).

Yeah you can see the output logs that the two redraw events are handled and it makes
sense because this is the callstack for an expose event for a QQuickWindow:

QSGGuiThreadRenderLoop::renderWindow (https://github.com/qtproject/qtdeclarative/blob/5.4/src/quick/scenegraph/qsgrenderloop.cpp#L301)
QSGGuiThreadRenderLoop::exposureChanged (https://github.com/qtproject/qtdeclarative/blob/5.4/src/quick/scenegraph/qsgrenderloop.cpp#L406)
QQuickWindow::exposeEvent (https://github.com/qtproject/qtdeclarative/blob/5.4/src/quick/items/qquickwindow.cpp#L202)
QGuiApplicationPrivate::processExposeEvent (https://github.com/qtproject/qtbase/blob/5.4/src/gui/kernel/qguiapplication.cpp#L2589)
QWindowSystemInterface::handleExposeEvent (https://github.com/qtproject/qtbase/blob/5.4/src/gui/kernel/qwindowsysteminterface.cpp#L523)

The main checks through out that call stack are:

- QWindow::isExposed, QPlatformWindow::isExposed (both will be true if the surface is visible)

In QSGGuiThreadRenderLoop::renderWindow checks
- QQuickWindowPrivate::isRenderable() (https://github.com/qtproject/qtdeclarative/blob/5.4/src/quick/scenegraph/qsgrenderloop.cpp#L304)
--- which does: (https://github.com/qtproject/qtdeclarative/blob/5.4/src/quick/items/qquickwindow.cpp#L2463)
- and another isVisible check before issuing a swapBuffers (https://github.com/qtproject/qtdeclarative/blob/5.4/src/quick/scenegraph/qsgrenderloop.cpp#L374)

The end result is that issuing the two calls, the first event waits for swapBuffers, which can then update the geometry (and that geometry change event I suppose is prioritized because it's generated by the rendering thread) - in the second event, then the scengraph should render with the new geometry.

302. By Alberto Aguirre on 2015-10-27

Don't create 0x0 mir surfaces

Seems to be a bug in the pixmap window that is created for the drag and drop default platform implementation.

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Daniel d'Andrada (dandrader) wrote :

"""
QSGGuiThreadRenderLoop::renderWindow
QSGGuiThreadRenderLoop::exposureChanged
QQuickWindow::exposeEvent
QGuiApplicationPrivate::processExposeEvent
QWindowSystemInterface::handleExposeEvent
"""

I think that call stack will only happen if event delivery is synchronous (synchronousWindowsSystemEvents), which I think (and hope) it's not our case.

But anyway, if you checked and did see the results you expected than that's enough for me. Won't delve into it.

review: Approve
Alberto Aguirre (albaguirre) wrote :

>
> """
> QSGGuiThreadRenderLoop::renderWindow
> QSGGuiThreadRenderLoop::exposureChanged
> QQuickWindow::exposeEvent
> QGuiApplicationPrivate::processExposeEvent
> QWindowSystemInterface::handleExposeEvent
> """
>
> I think that call stack will only happen if event delivery is synchronous
> (synchronousWindowsSystemEvents), which I think (and hope) it's not our case.
>
> But anyway, if you checked and did see the results you expected than that's
> enough for me. Won't delve into it.

Yeah sorry, it was more of a "logical" call stack. Yeah handleExposeEVent will queue into the event queue. Then the event queue dispatcher will then eventually call QGuiApplicationPrivate::processExposeEvent which will result in a callstack kinda like described above.

Gerry Boland (gerboland) wrote :

I think you've removed a hack that was added to work around the fact qt has no idea of the window position on screen, which Autopilot relied on to calculate absolute input coordinates.

I'm working on correctly fixing this (i.e. removing the hack, using the mir-client-debug API), so please hold off landing this until my branch is ready

Alberto Aguirre (albaguirre) wrote :

@Gerry,

Yes I did.. though I didn't remove the offset in the input coordinates which mentions the same workaround - I wasn't sure how it helped there.

"I'm working on correctly fixing this"
Awesome!

"please hold off landing this until my branch is ready"
Will do.

Daniel d'Andrada (dandrader) wrote :

Could you please merge lp:~dandrader/qtubuntu/fix-use-mir-surface-apis ?

That will allow us to merge this branch right away

303. By Alberto Aguirre on 2015-11-16

merge lp:~dandrader/qtubuntu/fix-use-mir-surface-apis

Daniel d'Andrada (dandrader) wrote :

And we should set size hints also on window creation:
http://bazaar.launchpad.net/~dandrader/qtubuntu/sizeHintsOnWindowCreation/revision/290

Otherwise size hints that the user set before showing the window (which is the moment the backing QPlatformWindow gets created) will be ignored.

304. By Alberto Aguirre on 2015-11-16

merge lp:~dandrader/qtubuntu/sizeHintsOnWindowCreation

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/ubuntumirclient/glcontext.cpp'
2--- src/ubuntumirclient/glcontext.cpp 2014-06-18 23:10:00 +0000
3+++ src/ubuntumirclient/glcontext.cpp 2015-11-16 22:00:40 +0000
4@@ -109,19 +109,7 @@
5 ASSERT(eglSwapBuffers(mEglDisplay, eglSurface) == EGL_TRUE);
6 #endif
7
8- // "Technique" copied from mir, in examples/eglapp.c around line 96
9- EGLint newBufferWidth = -1;
10- EGLint newBufferHeight = -1;
11- /*
12- * Querying the surface (actually the current buffer) dimensions here is
13- * the only truly safe way to be sure that the dimensions we think we
14- * have are those of the buffer being rendered to. But this should be
15- * improved in future; https://bugs.launchpad.net/mir/+bug/1194384
16- */
17- eglQuerySurface(mEglDisplay, eglSurface, EGL_WIDTH, &newBufferWidth);
18- eglQuerySurface(mEglDisplay, eglSurface, EGL_HEIGHT, &newBufferHeight);
19-
20- ubuntuWindow->onBuffersSwapped_threadSafe(newBufferWidth, newBufferHeight);
21+ ubuntuWindow->onSwapBuffersDone();
22 }
23
24 void (*UbuntuOpenGLContext::getProcAddress(const QByteArray& procName)) ()
25
26=== modified file 'src/ubuntumirclient/input.cpp'
27--- src/ubuntumirclient/input.cpp 2015-11-09 19:26:55 +0000
28+++ src/ubuntumirclient/input.cpp 2015-11-16 22:00:40 +0000
29@@ -142,6 +142,7 @@
30 , mEventFilterType(static_cast<UbuntuNativeInterface*>(
31 integration->nativeInterface())->genericEventFilterType())
32 , mEventType(static_cast<QEvent::Type>(QEvent::registerEventType()))
33+ , mLastFocusedWindow(nullptr)
34 {
35 // Initialize touch device.
36 mTouchDevice = new QTouchDevice;
37@@ -213,7 +214,7 @@
38 switch (mir_event_get_type(nativeEvent))
39 {
40 case mir_event_type_input:
41- dispatchInputEvent(ubuntuEvent->window->window(), mir_event_get_input_event(nativeEvent));
42+ dispatchInputEvent(ubuntuEvent->window, mir_event_get_input_event(nativeEvent));
43 break;
44 case mir_event_type_resize:
45 {
46@@ -225,7 +226,7 @@
47 mir_resize_event_get_width(resizeEvent),
48 mir_resize_event_get_height(resizeEvent));
49
50- ubuntuEvent->window->handleSurfaceResize(mir_resize_event_get_width(resizeEvent),
51+ ubuntuEvent->window->handleSurfaceResized(mir_resize_event_get_width(resizeEvent),
52 mir_resize_event_get_height(resizeEvent));
53 break;
54 }
55@@ -233,8 +234,16 @@
56 {
57 auto surfaceEvent = mir_event_get_surface_event(nativeEvent);
58 if (mir_surface_event_get_attribute(surfaceEvent) == mir_surface_attrib_focus) {
59- ubuntuEvent->window->handleSurfaceFocusChange(mir_surface_event_get_attribute_value(surfaceEvent) ==
60- mir_surface_focused);
61+ const bool focused = mir_surface_event_get_attribute_value(surfaceEvent) == mir_surface_focused;
62+ // Mir may have sent a pair of focus lost/gained events, so we need to "peek" into the queue
63+ // so that we don't deactivate windows prematurely.
64+ if (focused) {
65+ mPendingFocusGainedEvents--;
66+ ubuntuEvent->window->handleSurfaceFocused();
67+ } else if(!mPendingFocusGainedEvents) {
68+ DLOG("[ubuntumirclient QPA] No windows have focus");
69+ QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
70+ }
71 }
72 break;
73 }
74@@ -253,6 +262,17 @@
75 {
76 QWindow *window = platformWindow->window();
77
78+ const auto eventType = mir_event_get_type(event);
79+ if (mir_event_type_surface == eventType) {
80+ auto surfaceEvent = mir_event_get_surface_event(event);
81+ if (mir_surface_attrib_focus == mir_surface_event_get_attribute(surfaceEvent)) {
82+ const bool focused = mir_surface_event_get_attribute_value(surfaceEvent) == mir_surface_focused;
83+ if (focused) {
84+ mPendingFocusGainedEvents++;
85+ }
86+ }
87+ }
88+
89 QCoreApplication::postEvent(this, new UbuntuEvent(
90 platformWindow, event, mEventType));
91
92@@ -263,7 +283,7 @@
93 }
94 }
95
96-void UbuntuInput::dispatchInputEvent(QWindow *window, const MirInputEvent *ev)
97+void UbuntuInput::dispatchInputEvent(UbuntuWindow *window, const MirInputEvent *ev)
98 {
99 switch (mir_input_event_get_type(ev))
100 {
101@@ -281,7 +301,7 @@
102 }
103 }
104
105-void UbuntuInput::dispatchTouchEvent(QWindow *window, const MirInputEvent *ev)
106+void UbuntuInput::dispatchTouchEvent(UbuntuWindow *window, const MirInputEvent *ev)
107 {
108 const MirTouchEvent *tev = mir_input_event_get_touch_event(ev);
109
110@@ -312,6 +332,7 @@
111 switch (touch_action)
112 {
113 case mir_touch_action_down:
114+ mLastFocusedWindow = window;
115 touchPoint.state = Qt::TouchPointPressed;
116 break;
117 case mir_touch_action_up:
118@@ -326,7 +347,7 @@
119 }
120
121 ulong timestamp = mir_input_event_get_event_time(ev) / 1000000;
122- QWindowSystemInterface::handleTouchEvent(window, timestamp,
123+ QWindowSystemInterface::handleTouchEvent(window->window(), timestamp,
124 mTouchDevice, touchPoints);
125 }
126
127@@ -369,7 +390,7 @@
128 }
129 }
130
131-void UbuntuInput::dispatchKeyEvent(QWindow *window, const MirInputEvent *event)
132+void UbuntuInput::dispatchKeyEvent(UbuntuWindow *window, const MirInputEvent *event)
133 {
134 const MirKeyboardEvent *key_event = mir_input_event_get_keyboard_event(event);
135
136@@ -383,6 +404,9 @@
137 QEvent::Type keyType = action == mir_keyboard_action_up
138 ? QEvent::KeyRelease : QEvent::KeyPress;
139
140+ if (action == mir_keyboard_action_down)
141+ mLastFocusedWindow = window;
142+
143 char s[2];
144 int sym = translateKeysym(xk_sym, s, sizeof(s));
145 QString text = QString::fromLatin1(s);
146@@ -399,7 +423,7 @@
147 }
148 }
149
150- QWindowSystemInterface::handleKeyEvent(window, timestamp, keyType, sym, modifiers, text, is_auto_rep);
151+ QWindowSystemInterface::handleKeyEvent(window->window(), timestamp, keyType, sym, modifiers, text, is_auto_rep);
152 }
153
154 namespace
155@@ -422,8 +446,9 @@
156 }
157 }
158
159-void UbuntuInput::dispatchPointerEvent(QWindow *window, const MirInputEvent *ev)
160+void UbuntuInput::dispatchPointerEvent(UbuntuWindow *platformWindow, const MirInputEvent *ev)
161 {
162+ auto window = platformWindow->window();
163 auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
164
165 auto pev = mir_input_event_get_pointer_event(ev);
166@@ -446,6 +471,8 @@
167 QPoint(), angleDelta, modifiers, Qt::ScrollUpdate);
168 } else {
169 auto buttons = extract_buttons(pev);
170+ if (buttons != Qt::NoButton)
171+ mLastFocusedWindow = platformWindow;
172 QWindowSystemInterface::handleMouseEvent(window, timestamp, localPoint, localPoint /* Should we omit global point instead? */,
173 buttons, modifiers);
174 }
175
176=== modified file 'src/ubuntumirclient/input.h'
177--- src/ubuntumirclient/input.h 2015-02-11 20:41:17 +0000
178+++ src/ubuntumirclient/input.h 2015-11-16 22:00:40 +0000
179@@ -19,6 +19,7 @@
180
181 // Qt
182 #include <qpa/qwindowsysteminterface.h>
183+#include <QAtomicInt>
184
185 #include <mir_toolkit/mir_client_library.h>
186
187@@ -38,13 +39,14 @@
188
189 void postEvent(UbuntuWindow* window, const MirEvent *event);
190 UbuntuClientIntegration* integration() const { return mIntegration; }
191+ UbuntuWindow *lastFocusedWindow() const {return mLastFocusedWindow; }
192
193 protected:
194- void dispatchKeyEvent(QWindow *window, const MirInputEvent *event);
195- void dispatchPointerEvent(QWindow *window, const MirInputEvent *event);
196- void dispatchTouchEvent(QWindow *window, const MirInputEvent *event);
197- void dispatchInputEvent(QWindow *window, const MirInputEvent *event);
198-
199+ void dispatchKeyEvent(UbuntuWindow *window, const MirInputEvent *event);
200+ void dispatchPointerEvent(UbuntuWindow *window, const MirInputEvent *event);
201+ void dispatchTouchEvent(UbuntuWindow *window, const MirInputEvent *event);
202+ void dispatchInputEvent(UbuntuWindow *window, const MirInputEvent *event);
203+
204 void dispatchOrientationEvent(QWindow* window, const MirOrientationEvent *event);
205
206 private:
207@@ -52,6 +54,9 @@
208 QTouchDevice* mTouchDevice;
209 const QByteArray mEventFilterType;
210 const QEvent::Type mEventType;
211+
212+ UbuntuWindow *mLastFocusedWindow;
213+ QAtomicInt mPendingFocusGainedEvents;
214 };
215
216 #endif // UBUNTU_INPUT_H
217
218=== modified file 'src/ubuntumirclient/integration.cpp'
219--- src/ubuntumirclient/integration.cpp 2015-09-14 13:12:16 +0000
220+++ src/ubuntumirclient/integration.cpp 2015-11-16 22:00:40 +0000
221@@ -14,28 +14,28 @@
222 * along with this program. If not, see <http://www.gnu.org/licenses/>.
223 */
224
225-// Qt
226-#include <QGuiApplication>
227-#include <private/qguiapplication_p.h>
228-#include <qpa/qplatformnativeinterface.h>
229-#include <qpa/qplatforminputcontextfactory_p.h>
230-#include <qpa/qplatforminputcontext.h>
231-#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h>
232-#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h>
233-#include <QOpenGLContext>
234-
235 // Local
236+#include "integration.h"
237 #include "backingstore.h"
238 #include "clipboard.h"
239 #include "glcontext.h"
240 #include "input.h"
241-#include "integration.h"
242 #include "logging.h"
243 #include "nativeinterface.h"
244 #include "screen.h"
245 #include "theme.h"
246 #include "window.h"
247
248+// Qt
249+#include <QGuiApplication>
250+#include <private/qguiapplication_p.h>
251+#include <qpa/qplatformnativeinterface.h>
252+#include <qpa/qplatforminputcontextfactory_p.h>
253+#include <qpa/qplatforminputcontext.h>
254+#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h>
255+#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h>
256+#include <QOpenGLContext>
257+
258 // platform-api
259 #include <ubuntu/application/lifecycle_delegate.h>
260 #include <ubuntu/application/id.h>
261@@ -162,10 +162,8 @@
262
263 QPlatformWindow* UbuntuClientIntegration::createPlatformWindow(QWindow* window)
264 {
265- QPlatformWindow* platformWindow = new UbuntuWindow(
266- window, mClipboard, static_cast<UbuntuScreen*>(mScreen), mInput, u_application_instance_get_mir_connection(mInstance));
267- platformWindow->requestActivateWindow();
268- return platformWindow;
269+ return new UbuntuWindow(window, mClipboard, static_cast<UbuntuScreen*>(mScreen),
270+ mInput, u_application_instance_get_mir_connection(mInstance));
271 }
272
273 bool UbuntuClientIntegration::hasCapability(QPlatformIntegration::Capability cap) const
274@@ -173,11 +171,9 @@
275 switch (cap) {
276 case ThreadedPixmaps:
277 return true;
278- break;
279
280 case OpenGL:
281 return true;
282- break;
283
284 case ThreadedOpenGL:
285 if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_THREADED_OPENGL")) {
286@@ -186,8 +182,9 @@
287 DLOG("ubuntumirclient: disabled threaded OpenGL");
288 return false;
289 }
290- break;
291-
292+ case MultipleWindows:
293+ case NonFullScreenWindows:
294+ return true;
295 default:
296 return QPlatformIntegration::hasCapability(cap);
297 }
298
299=== modified file 'src/ubuntumirclient/nativeinterface.cpp'
300--- src/ubuntumirclient/nativeinterface.cpp 2015-08-27 09:41:47 +0000
301+++ src/ubuntumirclient/nativeinterface.cpp 2015-11-16 22:00:40 +0000
302@@ -14,17 +14,17 @@
303 * along with this program. If not, see <http://www.gnu.org/licenses/>.
304 */
305
306+// Local
307+#include "nativeinterface.h"
308+#include "screen.h"
309+#include "glcontext.h"
310+
311 // Qt
312 #include <private/qguiapplication_p.h>
313 #include <QtGui/qopenglcontext.h>
314 #include <QtGui/qscreen.h>
315 #include <QtCore/QMap>
316
317-// Local
318-#include "nativeinterface.h"
319-#include "screen.h"
320-#include "glcontext.h"
321-
322 class UbuntuResourceMap : public QMap<QByteArray, UbuntuNativeInterface::ResourceType>
323 {
324 public:
325
326=== modified file 'src/ubuntumirclient/screen.cpp'
327--- src/ubuntumirclient/screen.cpp 2015-10-09 10:57:03 +0000
328+++ src/ubuntumirclient/screen.cpp 2015-11-16 22:00:40 +0000
329@@ -14,6 +14,11 @@
330 * along with this program. If not, see <http://www.gnu.org/licenses/>.
331 */
332
333+// local
334+#include "screen.h"
335+#include "logging.h"
336+#include "orientationchangeevent_p.h"
337+
338 #include <mir_toolkit/mir_client_library.h>
339
340 // Qt
341@@ -24,12 +29,7 @@
342 #include <qpa/qwindowsysteminterface.h>
343 #include <QtPlatformSupport/private/qeglconvenience_p.h>
344
345-// local
346-#include "screen.h"
347-#include "logging.h"
348-#include "orientationchangeevent_p.h"
349-
350-#include "memory"
351+#include <memory>
352
353 static const int kSwapInterval = 1;
354
355@@ -128,6 +128,7 @@
356 UbuntuScreen::UbuntuScreen(MirConnection *connection)
357 : mFormat(QImage::Format_RGB32)
358 , mDepth(32)
359+ , mOutputId(0)
360 , mSurfaceFormat()
361 , mEglDisplay(EGL_NO_DISPLAY)
362 , mEglConfig(nullptr)
363@@ -182,6 +183,8 @@
364 auto const displayOutput = find_active_output(displayConfig.get());
365 ASSERT(displayOutput != nullptr);
366
367+ mOutputId = displayOutput->output_id;
368+
369 mPhysicalSize = QSizeF(displayOutput->physical_width_mm, displayOutput->physical_height_mm);
370 DLOG("ubuntumirclient: screen physical size: %.2fx%.2f", mPhysicalSize.width(), mPhysicalSize.height());
371
372
373=== modified file 'src/ubuntumirclient/screen.h'
374--- src/ubuntumirclient/screen.h 2015-10-09 10:57:03 +0000
375+++ src/ubuntumirclient/screen.h 2015-11-16 22:00:40 +0000
376@@ -45,6 +45,7 @@
377 EGLConfig eglConfig() const { return mEglConfig; }
378 EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; }
379 void handleWindowSurfaceResize(int width, int height);
380+ uint32_t mirOutputId() const { return mOutputId; }
381
382 // QObject methods.
383 void customEvent(QEvent* event);
384@@ -56,6 +57,7 @@
385 Qt::ScreenOrientation mCurrentOrientation;
386 QImage::Format mFormat;
387 int mDepth;
388+ uint32_t mOutputId;
389 QSurfaceFormat mSurfaceFormat;
390 EGLDisplay mEglDisplay;
391 EGLConfig mEglConfig;
392
393=== modified file 'src/ubuntumirclient/window.cpp'
394--- src/ubuntumirclient/window.cpp 2015-11-09 19:26:55 +0000
395+++ src/ubuntumirclient/window.cpp 2015-11-16 22:00:40 +0000
396@@ -15,15 +15,16 @@
397 */
398
399 // Local
400+#include "window.h"
401 #include "clipboard.h"
402 #include "input.h"
403-#include "window.h"
404 #include "screen.h"
405 #include "logging.h"
406
407+#include <mir_toolkit/mir_client_library.h>
408+
409 // Qt
410 #include <qpa/qwindowsysteminterface.h>
411-#include <QMutex>
412 #include <QMutexLocker>
413 #include <QSize>
414 #include <QtMath>
415@@ -33,25 +34,46 @@
416
417 #include <EGL/egl.h>
418
419-#define IS_OPAQUE_FLAG 1
420-
421 namespace
422 {
423+
424+// FIXME: this used to be defined by platform-api, but it's been removed in v3. Change ubuntu-keyboard to use
425+// a different enum for window roles.
426+enum UAUiWindowRole {
427+ U_MAIN_ROLE = 1,
428+ U_DASH_ROLE,
429+ U_INDICATOR_ROLE,
430+ U_NOTIFICATIONS_ROLE,
431+ U_GREETER_ROLE,
432+ U_LAUNCHER_ROLE,
433+ U_ON_SCREEN_KEYBOARD_ROLE,
434+ U_SHUTDOWN_DIALOG_ROLE,
435+};
436+
437+struct MirSpecDeleter
438+{
439+ void operator()(MirSurfaceSpec *spec) { mir_surface_spec_release(spec); }
440+};
441+
442+using Spec = std::unique_ptr<MirSurfaceSpec, MirSpecDeleter>;
443+
444+EGLNativeWindowType nativeWindowFor(MirSurface *surf)
445+{
446+ auto stream = mir_surface_get_buffer_stream(surf);
447+ return reinterpret_cast<EGLNativeWindowType>(mir_buffer_stream_get_egl_native_window(stream));
448+}
449+
450 MirSurfaceState qtWindowStateToMirSurfaceState(Qt::WindowState state)
451 {
452 switch (state) {
453 case Qt::WindowNoState:
454 return mir_surface_state_restored;
455-
456 case Qt::WindowFullScreen:
457 return mir_surface_state_fullscreen;
458-
459 case Qt::WindowMaximized:
460 return mir_surface_state_maximized;
461-
462 case Qt::WindowMinimized:
463 return mir_surface_state_minimized;
464-
465 default:
466 LOG("Unexpected Qt::WindowState: %d", state);
467 return mir_surface_state_restored;
468@@ -64,126 +86,137 @@
469 switch (state) {
470 case Qt::WindowNoState:
471 return "NoState";
472-
473 case Qt::WindowFullScreen:
474 return "FullScreen";
475-
476 case Qt::WindowMaximized:
477 return "Maximized";
478-
479 case Qt::WindowMinimized:
480 return "Minimized";
481-
482 default:
483 return "!?";
484 }
485 }
486 #endif
487
488-} // anonymous namespace
489-
490-class UbuntuWindowPrivate
491-{
492-public:
493- void createEGLSurface(EGLNativeWindowType nativeWindow);
494- void destroyEGLSurface();
495- int panelHeight();
496-
497- UbuntuScreen* screen;
498- EGLSurface eglSurface;
499- WId id;
500- UbuntuInput* input;
501- Qt::WindowState state;
502- MirConnection *connection;
503- MirSurface* surface;
504- QSize bufferSize;
505- QMutex mutex;
506- QSharedPointer<UbuntuClipboard> clipboard;
507- int resizeCatchUpAttempts;
508-#if !defined(QT_NO_DEBUG)
509- int frameNumber;
510-#endif
511-};
512-
513-static void eventCallback(MirSurface* surface, const MirEvent *event, void* context)
514-{
515- (void) surface;
516- DASSERT(context != NULL);
517- UbuntuWindow* platformWindow = static_cast<UbuntuWindow*>(context);
518- platformWindow->priv()->input->postEvent(platformWindow, event);
519-}
520-
521-static void surfaceCreateCallback(MirSurface* surface, void* context)
522-{
523- DASSERT(context != NULL);
524- UbuntuWindow* platformWindow = static_cast<UbuntuWindow*>(context);
525- platformWindow->priv()->surface = surface;
526-
527- mir_surface_set_event_handler(surface, eventCallback, context);
528-}
529-
530-UbuntuWindow::UbuntuWindow(QWindow* w, QSharedPointer<UbuntuClipboard> clipboard, UbuntuScreen* screen,
531- UbuntuInput* input, MirConnection* connection)
532- : QObject(nullptr), QPlatformWindow(w)
533-{
534- DASSERT(screen != NULL);
535-
536- d = new UbuntuWindowPrivate;
537- d->screen = screen;
538- d->eglSurface = EGL_NO_SURFACE;
539- d->input = input;
540- d->state = window()->windowState();
541- d->connection = connection;
542- d->clipboard = clipboard;
543- d->resizeCatchUpAttempts = 0;
544-
545+WId makeId()
546+{
547 static int id = 1;
548- d->id = id++;
549-
550-#if !defined(QT_NO_DEBUG)
551- d->frameNumber = 0;
552-#endif
553-
554- // Use client geometry if set explicitly, use available screen geometry otherwise.
555- QPlatformWindow::setGeometry(window()->geometry() != screen->geometry() ?
556- window()->geometry() : screen->availableGeometry());
557- createWindow();
558- DLOG("UbuntuWindow::UbuntuWindow (this=%p, w=%p, screen=%p, input=%p)", this, w, screen, input);
559-}
560-
561-UbuntuWindow::~UbuntuWindow()
562-{
563- DLOG("UbuntuWindow::~UbuntuWindow");
564- d->destroyEGLSurface();
565-
566- mir_surface_release_sync(d->surface);
567-
568- delete d;
569-}
570-
571-void UbuntuWindowPrivate::createEGLSurface(EGLNativeWindowType nativeWindow)
572-{
573- DLOG("UbuntuWindowPrivate::createEGLSurface (this=%p, nativeWindow=%p)",
574- this, reinterpret_cast<void*>(nativeWindow));
575-
576- eglSurface = eglCreateWindowSurface(screen->eglDisplay(), screen->eglConfig(),
577- nativeWindow, nullptr);
578-
579- DASSERT(eglSurface != EGL_NO_SURFACE);
580-}
581-
582-void UbuntuWindowPrivate::destroyEGLSurface()
583-{
584- DLOG("UbuntuWindowPrivate::destroyEGLSurface (this=%p)", this);
585- if (eglSurface != EGL_NO_SURFACE) {
586- eglDestroySurface(screen->eglDisplay(), eglSurface);
587- eglSurface = EGL_NO_SURFACE;
588- }
589+ return id++;
590+}
591+
592+MirPixelFormat defaultPixelFormatFor(MirConnection *connection)
593+{
594+ MirPixelFormat format;
595+ unsigned int nformats;
596+ mir_connection_get_available_surface_formats(connection, &format, 1, &nformats);
597+ return format;
598+}
599+
600+UAUiWindowRole roleFor(QWindow *window)
601+{
602+ QVariant roleVariant = window->property("role");
603+ if (!roleVariant.isValid())
604+ return U_MAIN_ROLE;
605+
606+ uint role = roleVariant.toUInt();
607+ if (role < U_MAIN_ROLE || role > U_SHUTDOWN_DIALOG_ROLE)
608+ return U_MAIN_ROLE;
609+
610+ return static_cast<UAUiWindowRole>(role);
611+}
612+
613+UbuntuWindow *transientParentFor(QWindow *window)
614+{
615+ QWindow *parent = window->transientParent();
616+ return parent ? static_cast<UbuntuWindow *>(parent->handle()) : nullptr;
617+}
618+
619+Spec makeSurfaceSpec(QWindow *window, UbuntuInput *input, MirConnection *connection)
620+{
621+ const auto geom = window->geometry();
622+ const int width = geom.width() > 0 ? geom.width() : 1;
623+ const int height = geom.height() > 0 ? geom.height() : 1;
624+ const auto pixelFormat = defaultPixelFormatFor(connection);
625+
626+ if (U_ON_SCREEN_KEYBOARD_ROLE == roleFor(window)) {
627+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating input method surface (width=%d, height=%d", window, width, height);
628+ return Spec{mir_connection_create_spec_for_input_method(connection, width, height, pixelFormat)};
629+ }
630+
631+ const Qt::WindowType type = window->type();
632+ if (type == Qt::Popup) {
633+ auto parent = transientParentFor(window);
634+ if (parent == nullptr) {
635+ //NOTE: We cannot have a parentless popup -
636+ //try using the last surface to receive input as that will most likely be
637+ //the one that caused this popup to be created
638+ parent = input->lastFocusedWindow();
639+ }
640+ if (parent) {
641+ auto pos = geom.topLeft();
642+ pos -= parent->geometry().topLeft();
643+ MirRectangle location{pos.x(), pos.y(), 0, 0};
644+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating menu surface(width:%d, height:%d)", window, width, height);
645+ return Spec{mir_connection_create_spec_for_menu(
646+ connection, width, height, pixelFormat, parent->mirSurface(),
647+ &location, mir_edge_attachment_any)};
648+ } else {
649+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - cannot create a menu without a parent!", window);
650+ }
651+ } else if (type == Qt::Dialog) {
652+ auto parent = transientParentFor(window);
653+ if (parent) {
654+ // Modal dialog
655+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating modal dialog (width=%d, height=%d", window, width, height);
656+ return Spec{mir_connection_create_spec_for_modal_dialog(connection, width, height, pixelFormat, parent->mirSurface())};
657+ } else {
658+ // TODO: do Qt parentless dialogs have the same semantics as mir?
659+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating parentless dialog (width=%d, height=%d)", window, width, height);
660+ return Spec{mir_connection_create_spec_for_dialog(connection, width, height, pixelFormat)};
661+ }
662+ }
663+ DLOG("[ubuntumirclient QPA] makeSurfaceSpec(window=%p) - creating normal surface(type=0x%x, width=%d, height=%d)", window, type, width, height);
664+ return Spec{mir_connection_create_spec_for_normal_surface(connection, width, height, pixelFormat)};
665+}
666+
667+void setSizingConstraints(MirSurfaceSpec *spec, const QSize& minSize, const QSize& maxSize, const QSize& increment)
668+{
669+ mir_surface_spec_set_min_width(spec, minSize.width());
670+ mir_surface_spec_set_min_height(spec, minSize.height());
671+ if (maxSize.width() >= minSize.width()) {
672+ mir_surface_spec_set_max_width(spec, maxSize.width());
673+ }
674+ if (maxSize.height() >= minSize.height()) {
675+ mir_surface_spec_set_max_height(spec, maxSize.height());
676+ }
677+ if (increment.width() > 0) {
678+ mir_surface_spec_set_width_increment(spec, increment.width());
679+ }
680+ if (increment.height() > 0) {
681+ mir_surface_spec_set_height_increment(spec, increment.height());
682+ }
683+}
684+
685+MirSurface *createMirSurface(QWindow *window, UbuntuScreen *screen, UbuntuInput *input, MirConnection *connection)
686+{
687+ auto spec = makeSurfaceSpec(window, input, connection);
688+ const auto title = window->title().toUtf8();
689+ mir_surface_spec_set_name(spec.get(), title.constData());
690+
691+ setSizingConstraints(spec.get(), window->minimumSize(), window->maximumSize(), window->sizeIncrement());
692+
693+ if (window->windowState() == Qt::WindowFullScreen) {
694+ mir_surface_spec_set_fullscreen_on_output(spec.get(), screen->mirOutputId());
695+ }
696+
697+ auto surface = mir_surface_create_sync(spec.get());
698+ Q_ASSERT(mir_surface_is_valid(surface));
699+ return surface;
700 }
701
702 // FIXME - in order to work around https://bugs.launchpad.net/mir/+bug/1346633
703 // we need to guess the panel height (3GU + 2DP)
704-int UbuntuWindowPrivate::panelHeight()
705+int panelHeight()
706 {
707 const int defaultGridUnit = 8;
708 int gridUnit = defaultGridUnit;
709@@ -199,272 +232,380 @@
710 return gridUnit * 3 + qFloor(densityPixelRatio) * 2;
711 }
712
713-namespace
714-{
715-static MirPixelFormat
716-mir_choose_default_pixel_format(MirConnection *connection)
717-{
718- MirPixelFormat format[mir_pixel_formats];
719- unsigned int nformats;
720-
721- mir_connection_get_available_surface_formats(connection,
722- format, mir_pixel_formats, &nformats);
723-
724- return format[0];
725-}
726-}
727-
728-void UbuntuWindow::createWindow()
729-{
730- DLOG("UbuntuWindow::createWindow (this=%p)", this);
731-
732- // FIXME: remove this remnant of an old platform-api enum - needs ubuntu-keyboard update
733- const int SCREEN_KEYBOARD_ROLE = 7;
734- // Get surface role and flags.
735- QVariant roleVariant = window()->property("role");
736- int role = roleVariant.isValid() ? roleVariant.toUInt() : 1; // 1 is the default role for apps.
737- QVariant opaqueVariant = window()->property("opaque");
738- uint flags = opaqueVariant.isValid() ?
739- opaqueVariant.toUInt() ? static_cast<uint>(IS_OPAQUE_FLAG) : 0 : 0;
740-
741- // FIXME(loicm) Opaque flag is forced for now for non-system sessions (applications) for
742- // performance reasons.
743- flags |= static_cast<uint>(IS_OPAQUE_FLAG);
744-
745- const QByteArray title = (!window()->title().isNull()) ? window()->title().toUtf8() : "Window 1"; // legacy title
746- const int panelHeight = d->panelHeight();
747-
748-#if !defined(QT_NO_DEBUG)
749- LOG("panelHeight: '%d'", panelHeight);
750- LOG("role: '%d'", role);
751- LOG("flags: '%s'", (flags & static_cast<uint>(1)) ? "Opaque" : "NotOpaque");
752- LOG("title: '%s'", title.constData());
753-#endif
754-
755- // Get surface geometry.
756- QRect geometry;
757- if (d->state == Qt::WindowFullScreen) {
758- printf("UbuntuWindow - fullscreen geometry\n");
759- geometry = screen()->geometry();
760- } else if (d->state == Qt::WindowMaximized) {
761- printf("UbuntuWindow - maximized geometry\n");
762- geometry = screen()->availableGeometry();
763- /*
764- * FIXME: Autopilot relies on being able to convert coordinates relative of the window
765- * into absolute screen coordinates. Mir does not allow this, see bug lp:1346633
766- * Until there's a correct way to perform this transformation agreed, this horrible hack
767- * guesses the transformation heuristically.
768- *
769- * Assumption: this method only used on phone devices!
770- */
771- geometry.setY(panelHeight);
772- } else {
773- printf("UbuntuWindow - regular geometry\n");
774- geometry = this->geometry();
775- geometry.setY(panelHeight);
776- }
777-
778- DLOG("[ubuntumirclient QPA] creating surface at (%d, %d) with size (%d, %d) with title '%s'\n",
779- geometry.x(), geometry.y(), geometry.width(), geometry.height(), title.data());
780-
781- MirSurfaceSpec *spec;
782- if (role == SCREEN_KEYBOARD_ROLE)
783- {
784- spec = mir_connection_create_spec_for_input_method(d->connection, geometry.width(),
785- geometry.height(), mir_choose_default_pixel_format(d->connection));
786- }
787- else
788- {
789- spec = mir_connection_create_spec_for_normal_surface(d->connection, geometry.width(),
790- geometry.height(), mir_choose_default_pixel_format(d->connection));
791- }
792- mir_surface_spec_set_name(spec, title.data());
793-
794- // Create platform window
795- mir_wait_for(mir_surface_create(spec, surfaceCreateCallback, this));
796- mir_surface_spec_release(spec);
797-
798- DASSERT(d->surface != NULL);
799- d->createEGLSurface((EGLNativeWindowType)mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(d->surface)));
800-
801- if (d->state == Qt::WindowFullScreen) {
802- // TODO: We could set this on creation once surface spec supports it (mps already up)
803- mir_wait_for(mir_surface_set_state(d->surface, mir_surface_state_fullscreen));
804- }
805-
806- // Window manager can give us a final size different from what we asked for
807- // so let's check what we ended up getting
808- {
809+} //namespace
810+
811+class UbuntuSurface
812+{
813+public:
814+ UbuntuSurface(UbuntuWindow *platformWindow, UbuntuScreen *screen, UbuntuInput *input, MirConnection *connection)
815+ : mWindow(platformWindow->window())
816+ , mPlatformWindow(platformWindow)
817+ , mScreen(screen)
818+ , mInput(input)
819+ , mConnection(connection)
820+ , mMirSurface(createMirSurface(mWindow, screen, input, connection))
821+ , mEglDisplay(screen->eglDisplay())
822+ , mEglSurface(eglCreateWindowSurface(mEglDisplay, screen->eglConfig(), nativeWindowFor(mMirSurface), nullptr))
823+ , mVisible(false)
824+ , mNeedsRepaint(false)
825+ , mParented(mWindow->transientParent() || mWindow->parent())
826+ , mWindowState(mWindow->windowState())
827+
828+ {
829+ mir_surface_set_event_handler(mMirSurface, surfaceEventCallback, this);
830+
831+ // Window manager can give us a final size different from what we asked for
832+ // so let's check what we ended up getting
833 MirSurfaceParameters parameters;
834- mir_surface_get_parameters(d->surface, &parameters);
835-
836- geometry.setWidth(parameters.width);
837- geometry.setHeight(parameters.height);
838- }
839-
840- DLOG("[ubuntumirclient QPA] created surface has size (%d, %d)",
841- geometry.width(), geometry.height());
842-
843- // Assume that the buffer size matches the surface size at creation time
844- d->bufferSize = geometry.size();
845-
846- // Tell Qt about the geometry.
847- QWindowSystemInterface::handleGeometryChange(window(), geometry);
848- QPlatformWindow::setGeometry(geometry);
849-}
850-
851-void UbuntuWindow::moveResize(const QRect& rect)
852-{
853- (void) rect;
854- // TODO: Not yet supported by mir.
855-}
856-
857-void UbuntuWindow::handleSurfaceResize(int width, int height)
858-{
859- QMutexLocker(&d->mutex);
860- DLOG("UbuntuWindow::handleSurfaceResize(width=%d, height=%d) [%d]", width, height,
861- d->frameNumber);
862-
863- // The current buffer size hasn't actually changed. so just render on it and swap
864- // buffers in the hope that the next buffer will match the surface size advertised
865- // in this event.
866- // But since this event is processed by a thread different from the one that swaps
867- // buffers, you can never know if this information is already outdated as there's
868- // no synchronicity whatsoever between the processing of resize events and the
869- // consumption of buffers.
870- if (d->bufferSize.width() != width || d->bufferSize.height() != height) {
871- // if the next buffer doesn't have a different size, try some
872- // more
873- // FIXME: This is working around a mir bug! We really shound't have to
874- // swap more than once to get a buffer with the new size!
875- d->resizeCatchUpAttempts = 2;
876-
877- QWindowSystemInterface::handleExposeEvent(window(), geometry());
878- QWindowSystemInterface::flushWindowSystemEvents();
879- }
880-}
881-
882-void UbuntuWindow::handleSurfaceFocusChange(bool focused)
883-{
884- LOG("UbuntuWindow::handleSurfaceFocusChange(focused=%s)", focused ? "true" : "false");
885- QWindow *activatedWindow = focused ? window() : nullptr;
886-
887- // System clipboard contents might have changed while this window was unfocused and wihtout
888+ mir_surface_get_parameters(mMirSurface, &parameters);
889+
890+ auto geom = mWindow->geometry();
891+ geom.setWidth(parameters.width);
892+ geom.setHeight(parameters.height);
893+ geom.setY(panelHeight());
894+
895+ // Assume that the buffer size matches the surface size at creation time
896+ mBufferSize = geom.size();
897+ platformWindow->QPlatformWindow::setGeometry(geom);
898+ QWindowSystemInterface::handleGeometryChange(mWindow, geom);
899+
900+ DLOG("[ubuntumirclient QPA] created surface at (%d, %d) with size (%d, %d), title '%s', role: '%d'\n",
901+ geom.x(), geom.y(), geom.width(), geom.height(), mWindow->title().toUtf8().constData(), roleFor(mWindow));
902+ }
903+
904+ ~UbuntuSurface()
905+ {
906+ if (mEglSurface != EGL_NO_SURFACE)
907+ eglDestroySurface(mEglDisplay, mEglSurface);
908+ if (mMirSurface)
909+ mir_surface_release_sync(mMirSurface);
910+ }
911+
912+ void resize(const QSize& newSize);
913+ void setState(Qt::WindowState newState);
914+ void setVisible(bool state);
915+ void updateTitle(const QString& title);
916+ void setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment);
917+
918+ void onSwapBuffersDone();
919+ void handleSurfaceResized(int width, int height);
920+ int needsRepaint() const;
921+
922+ EGLSurface eglSurface() const { return mEglSurface; }
923+ MirSurface *mirSurface() const { return mMirSurface; }
924+
925+private:
926+ static void surfaceEventCallback(MirSurface* surface, const MirEvent *event, void* context);
927+ void postEvent(const MirEvent *event);
928+ void updateSurface();
929+
930+ QWindow * const mWindow;
931+ UbuntuWindow * const mPlatformWindow;
932+ UbuntuScreen * const mScreen;
933+ UbuntuInput * const mInput;
934+ MirConnection * const mConnection;
935+
936+ MirSurface * const mMirSurface;
937+ const EGLDisplay mEglDisplay;
938+ const EGLSurface mEglSurface;
939+
940+ bool mVisible;
941+ bool mNeedsRepaint;
942+ bool mParented;
943+ Qt::WindowState mWindowState;
944+ QSize mBufferSize;
945+
946+ QMutex mTargetSizeMutex;
947+ QSize mTargetSize;
948+};
949+
950+void UbuntuSurface::resize(const QSize& size)
951+{
952+ DLOG("[ubuntumirclient QPA] resize(window=%p, width=%d, height=%d)", mWindow, size.width(), size.height());
953+
954+ if (mWindowState == Qt::WindowFullScreen || mWindowState == Qt::WindowMaximized) {
955+ DLOG("[ubuntumirclient QPA] resize(window=%p) - not resizing, window is maximized or fullscreen", mWindow);
956+ return;
957+ }
958+
959+ if (size.isEmpty()) {
960+ DLOG("[ubuntumirclient QPA] resize(window=%p) - not resizing, size is empty", mWindow);
961+ return;
962+ }
963+
964+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
965+ mir_surface_spec_set_width(spec.get(), size.width());
966+ mir_surface_spec_set_height(spec.get(), size.height());
967+ mir_surface_apply_spec(mMirSurface, spec.get());
968+}
969+
970+void UbuntuSurface::setState(Qt::WindowState newState)
971+{
972+ mir_wait_for(mir_surface_set_state(mMirSurface, qtWindowStateToMirSurfaceState(newState)));
973+ mWindowState = newState;
974+}
975+
976+void UbuntuSurface::setVisible(bool visible)
977+{
978+ if (mVisible == visible)
979+ return;
980+
981+ mVisible = visible;
982+
983+ if (mVisible)
984+ updateSurface();
985+
986+ // TODO: Use the new mir_surface_state_hidden state instead of mir_surface_state_minimized.
987+ // Will have to change qtmir and unity8 for that.
988+ const auto newState = visible ? qtWindowStateToMirSurfaceState(mWindowState) : mir_surface_state_minimized;
989+ mir_wait_for(mir_surface_set_state(mMirSurface, newState));
990+}
991+
992+void UbuntuSurface::updateTitle(const QString& newTitle)
993+{
994+ const auto title = newTitle.toUtf8();
995+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
996+ mir_surface_spec_set_name(spec.get(), title.constData());
997+ mir_surface_apply_spec(mMirSurface, spec.get());
998+}
999+
1000+void UbuntuSurface::setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment)
1001+{
1002+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
1003+ ::setSizingConstraints(spec.get(), minSize, maxSize, increment);
1004+ mir_surface_apply_spec(mMirSurface, spec.get());
1005+}
1006+
1007+void UbuntuSurface::handleSurfaceResized(int width, int height)
1008+{
1009+ QMutexLocker lock(&mTargetSizeMutex);
1010+
1011+ // mir's resize event is mainly a signal that we need to redraw our content. We use the
1012+ // width/height as identifiers to figure out if this is the latest surface resize event
1013+ // that has posted, discarding any old ones. This avoids issuing too many redraw events.
1014+ // see TODO in postEvent as the ideal way we should handle this.
1015+ // The actual buffer size may or may have not changed at this point, so let the rendering
1016+ // thread drive the window geometry updates.
1017+ mNeedsRepaint = mTargetSize.width() == width && mTargetSize.height() == height;
1018+}
1019+
1020+int UbuntuSurface::needsRepaint() const
1021+{
1022+ if (mNeedsRepaint) {
1023+ if (mTargetSize != mBufferSize) {
1024+ //If the buffer hasn't changed yet, we need at least two redraws,
1025+ //once to get the new buffer size and propagate the geometry changes
1026+ //and the second to redraw the content at the new size
1027+ return 2;
1028+ } else {
1029+ // The buffer size has already been updated so we only need one redraw
1030+ // to render at the new size
1031+ return 1;
1032+ }
1033+ }
1034+ return 0;
1035+}
1036+
1037+void UbuntuSurface::onSwapBuffersDone()
1038+{
1039+#if !defined(QT_NO_DEBUG)
1040+ static int sFrameNumber = 0;
1041+ ++sFrameNumber;
1042+#endif
1043+
1044+ EGLint eglSurfaceWidth = -1;
1045+ EGLint eglSurfaceHeight = -1;
1046+ eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &eglSurfaceWidth);
1047+ eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &eglSurfaceHeight);
1048+
1049+ const bool validSize = eglSurfaceWidth > 0 && eglSurfaceHeight > 0;
1050+
1051+ if (validSize && (mBufferSize.width() != eglSurfaceWidth || mBufferSize.height() != eglSurfaceHeight)) {
1052+
1053+ DLOG("[ubuntumirclient QPA] onSwapBuffersDone(window=%p) [%d] - size changed (%d, %d) => (%d, %d)",
1054+ mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height(), eglSurfaceWidth, eglSurfaceHeight);
1055+
1056+ mBufferSize.rwidth() = eglSurfaceWidth;
1057+ mBufferSize.rheight() = eglSurfaceHeight;
1058+
1059+ QRect newGeometry = mPlatformWindow->geometry();
1060+ newGeometry.setSize(mBufferSize);
1061+
1062+ mPlatformWindow->QPlatformWindow::setGeometry(newGeometry);
1063+ QWindowSystemInterface::handleGeometryChange(mWindow, newGeometry);
1064+ } else {
1065+ DLOG("[ubuntumirclient QPA] onSwapBuffersDone(window=%p) [%d] - buffer size (%d,%d)",
1066+ mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height());
1067+ }
1068+}
1069+
1070+void UbuntuSurface::surfaceEventCallback(MirSurface *surface, const MirEvent *event, void* context)
1071+{
1072+ Q_UNUSED(surface);
1073+ Q_ASSERT(context != nullptr);
1074+
1075+ auto s = static_cast<UbuntuSurface *>(context);
1076+ s->postEvent(event);
1077+}
1078+
1079+void UbuntuSurface::postEvent(const MirEvent *event)
1080+{
1081+ if (mir_event_type_resize == mir_event_get_type(event)) {
1082+ // TODO: The current event queue just accumulates all resize events;
1083+ // It would be nicer if we could update just one event if that event has not been dispatched.
1084+ // As a workaround, we use the width/height as an identifier of this latest event
1085+ // so the event handler (handleSurfaceResized) can discard/ignore old ones.
1086+ const auto resizeEvent = mir_event_get_resize_event(event);
1087+ const auto width = mir_resize_event_get_width(resizeEvent);
1088+ const auto height = mir_resize_event_get_height(resizeEvent);
1089+ DLOG("[ubuntumirclient QPA] resizeEvent(window=%p, width=%d, height=%d)", mWindow, width, height);
1090+
1091+ QMutexLocker lock(&mTargetSizeMutex);
1092+ mTargetSize.rwidth() = width;
1093+ mTargetSize.rheight() = height;
1094+ }
1095+
1096+ mInput->postEvent(mPlatformWindow, event);
1097+}
1098+
1099+void UbuntuSurface::updateSurface()
1100+{
1101+ DLOG("[ubuntumirclient QPA] updateSurface(window=%p)", mWindow);
1102+
1103+ if (!mParented && mWindow->type() == Qt::Dialog) {
1104+ // The dialog may have been parented after creation time
1105+ // so morph it into a modal dialog
1106+ auto parent = transientParentFor(mWindow);
1107+ if (parent) {
1108+ DLOG("[ubuntumirclient QPA] updateSurface(window=%p) dialog now parented", mWindow);
1109+ mParented = true;
1110+ Spec spec{mir_connection_create_spec_for_changes(mConnection)};
1111+ mir_surface_spec_set_parent(spec.get(), parent->mirSurface());
1112+ mir_surface_apply_spec(mMirSurface, spec.get());
1113+ }
1114+ }
1115+}
1116+
1117+UbuntuWindow::UbuntuWindow(QWindow *w, QSharedPointer<UbuntuClipboard> clipboard, UbuntuScreen *screen,
1118+ UbuntuInput *input, MirConnection *connection)
1119+ : QObject(nullptr)
1120+ , QPlatformWindow(w)
1121+ , mId(makeId())
1122+ , mClipboard(clipboard)
1123+ , mSurface(new UbuntuSurface{this, screen, input, connection})
1124+{
1125+ DLOG("[ubuntumirclient QPA] UbuntuWindow(window=%p, screen=%p, input=%p, surf=%p)", w, screen, input, mSurface.get());
1126+}
1127+
1128+UbuntuWindow::~UbuntuWindow()
1129+{
1130+ DLOG("[ubuntumirclient QPA] ~UbuntuWindow(window=%p)", this);
1131+}
1132+
1133+void UbuntuWindow::handleSurfaceResized(int width, int height)
1134+{
1135+ QMutexLocker lock(&mMutex);
1136+ DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p, width=%d, height=%d)", window(), width, height);
1137+
1138+ mSurface->handleSurfaceResized(width, height);
1139+
1140+ // This resize event could have occurred just after the last buffer swap for this window.
1141+ // This means the client may still be holding a buffer with the older size. The first redraw call
1142+ // will then render at the old size. After swapping the client now will get a new buffer with the
1143+ // updated size but it still needs re-rendering so another redraw may be needed.
1144+ // A mir API to drop the currently held buffer would help here, so that we wouldn't have to redraw twice
1145+ auto const numRepaints = mSurface->needsRepaint();
1146+ DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) redraw %d times", window(), numRepaints);
1147+ for (int i = 0; i < numRepaints; i++) {
1148+ DLOG("[ubuntumirclient QPA] handleSurfaceResize(window=%p) repainting width=%d, height=%d", window(), geometry().size().width(), geometry().size().height());
1149+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size()));
1150+ }
1151+}
1152+
1153+void UbuntuWindow::handleSurfaceFocused()
1154+{
1155+ DLOG("[ubuntumirclient QPA] handleSurfaceFocused(window=%p)", window());
1156+
1157+ // System clipboard contents might have changed while this window was unfocused and without
1158 // this process getting notified about it because it might have been suspended (due to
1159 // application lifecycle policies), thus unable to listen to any changes notified through
1160 // D-Bus.
1161 // Therefore let's ensure we are up to date with the system clipboard now that we are getting
1162 // focused again.
1163- if (focused) {
1164- d->clipboard->requestDBusClipboardContents();
1165- }
1166-
1167- QWindowSystemInterface::handleWindowActivated(activatedWindow, Qt::ActiveWindowFocusReason);
1168+ mClipboard->requestDBusClipboardContents();
1169+ QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
1170 }
1171
1172 void UbuntuWindow::setWindowState(Qt::WindowState state)
1173 {
1174- QMutexLocker(&d->mutex);
1175- DLOG("UbuntuWindow::setWindowState (this=%p, %s)", this, qtWindowStateToStr(state));
1176-
1177- if (state == d->state)
1178- return;
1179-
1180- // TODO: Perhaps we should check if the states are applied?
1181- mir_wait_for(mir_surface_set_state(d->surface, qtWindowStateToMirSurfaceState(state)));
1182- d->state = state;
1183+ QMutexLocker lock(&mMutex);
1184+ DLOG("[ubuntumirclient QPA] setWindowState(window=%p, %s)", this, qtWindowStateToStr(state));
1185+ mSurface->setState(state);
1186 }
1187
1188 void UbuntuWindow::setGeometry(const QRect& rect)
1189 {
1190- DLOG("UbuntuWindow::setGeometry (this=%p)", this);
1191-
1192- bool doMoveResize;
1193-
1194- {
1195- QMutexLocker(&d->mutex);
1196- QPlatformWindow::setGeometry(rect);
1197- doMoveResize = d->state != Qt::WindowFullScreen && d->state != Qt::WindowMaximized;
1198- }
1199-
1200- if (doMoveResize) {
1201- moveResize(rect);
1202- }
1203+ QMutexLocker lock(&mMutex);
1204+ DLOG("[ubuntumirclient QPA] setGeometry (window=%p, x=%d, y=%d, width=%d, height=%d)",
1205+ window(), rect.x(), rect.y(), rect.width(), rect.height());
1206+
1207+ //NOTE: mir surfaces cannot be moved by the client so ignore the topLeft coordinates
1208+ const auto newSize = rect.size();
1209+ auto newGeometry = geometry();
1210+ newGeometry.setSize(newSize);
1211+ QPlatformWindow::setGeometry(newGeometry);
1212+
1213+ mSurface->resize(newSize);
1214 }
1215
1216 void UbuntuWindow::setVisible(bool visible)
1217 {
1218- QMutexLocker(&d->mutex);
1219- DLOG("UbuntuWindow::setVisible (this=%p, visible=%s)", this, visible ? "true" : "false");
1220-
1221- if (visible) {
1222- mir_wait_for(mir_surface_set_state(d->surface, qtWindowStateToMirSurfaceState(d->state)));
1223-
1224- QWindowSystemInterface::handleExposeEvent(window(), QRect());
1225- QWindowSystemInterface::flushWindowSystemEvents();
1226- } else {
1227- // TODO: Use the new mir_surface_state_hidden state instead of mir_surface_state_minimized.
1228- // Will have to change qtmir and unity8 for that.
1229- mir_wait_for(mir_surface_set_state(d->surface, mir_surface_state_minimized));
1230- }
1231-}
1232-
1233-void UbuntuWindow::setWindowTitle(const QString &title)
1234-{
1235- MirSurfaceSpec *spec = mir_connection_create_spec_for_changes(d->connection);
1236- mir_surface_spec_set_name(spec, title.toUtf8().constData());
1237- mir_surface_apply_spec(d->surface, spec);
1238- mir_surface_spec_release(spec);
1239+ QMutexLocker lock(&mMutex);
1240+ DLOG("[ubuntumirclient QPA] setVisible (window=%p, visible=%s)", window(), visible ? "true" : "false");
1241+
1242+ mSurface->setVisible(visible);
1243+ const QRect& exposeRect = visible ? QRect(QPoint(), geometry().size()) : QRect();
1244+
1245+ lock.unlock();
1246+ QWindowSystemInterface::handleExposeEvent(window(), exposeRect);
1247+ QWindowSystemInterface::flushWindowSystemEvents();
1248+}
1249+
1250+void UbuntuWindow::setWindowTitle(const QString& title)
1251+{
1252+ QMutexLocker lock(&mMutex);
1253+ DLOG("[ubuntumirclient QPA] setWindowTitle(window=%p) title=%s)", window(), title.toUtf8().constData());
1254+ mSurface->updateTitle(title);
1255+}
1256+
1257+void UbuntuWindow::propagateSizeHints()
1258+{
1259+ QMutexLocker lock(&mMutex);
1260+ const auto win = window();
1261+ DLOG("[ubuntumirclient QPA] propagateSizeHints(window=%p) min(%d,%d), max(%d,%d) increment(%d, %d)",
1262+ win, win->minimumSize().width(), win->minimumSize().height(),
1263+ win->maximumSize().width(), win->maximumSize().height(),
1264+ win->sizeIncrement().width(), win->sizeIncrement().height());
1265+ mSurface->setSizingConstraints(win->minimumSize(), win->maximumSize(), win->sizeIncrement());
1266 }
1267
1268 void* UbuntuWindow::eglSurface() const
1269 {
1270- return d->eglSurface;
1271+ return mSurface->eglSurface();
1272+}
1273+
1274+MirSurface *UbuntuWindow::mirSurface() const
1275+{
1276+ return mSurface->mirSurface();
1277 }
1278
1279 WId UbuntuWindow::winId() const
1280 {
1281- return d->id;
1282+ return mId;
1283 }
1284
1285-void UbuntuWindow::onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight)
1286+void UbuntuWindow::onSwapBuffersDone()
1287 {
1288- QMutexLocker(&d->mutex);
1289-
1290- bool sizeKnown = newBufferWidth > 0 && newBufferHeight > 0;
1291-
1292-#if !defined(QT_NO_DEBUG)
1293- ++d->frameNumber;
1294-#endif
1295-
1296- if (sizeKnown && (d->bufferSize.width() != newBufferWidth ||
1297- d->bufferSize.height() != newBufferHeight)) {
1298- d->resizeCatchUpAttempts = 0;
1299-
1300- DLOG("UbuntuWindow::onBuffersSwapped_threadSafe [%d] - buffer size changed from (%d,%d) to (%d,%d)"
1301- " resizeCatchUpAttempts=%d",
1302- d->frameNumber, d->bufferSize.width(), d->bufferSize.height(), newBufferWidth, newBufferHeight,
1303- d->resizeCatchUpAttempts);
1304-
1305- d->bufferSize.rwidth() = newBufferWidth;
1306- d->bufferSize.rheight() = newBufferHeight;
1307-
1308- QRect newGeometry;
1309-
1310- newGeometry = geometry();
1311- newGeometry.setWidth(d->bufferSize.width());
1312- newGeometry.setHeight(d->bufferSize.height());
1313-
1314- QPlatformWindow::setGeometry(newGeometry);
1315- QWindowSystemInterface::handleGeometryChange(window(), newGeometry, QRect());
1316- } else if (d->resizeCatchUpAttempts > 0) {
1317- --d->resizeCatchUpAttempts;
1318- DLOG("UbuntuWindow::onBuffersSwapped_threadSafe [%d] - buffer size (%d,%d). Redrawing to catch up a resized buffer."
1319- " resizeCatchUpAttempts=%d",
1320- d->frameNumber, d->bufferSize.width(), d->bufferSize.height(), d->resizeCatchUpAttempts);
1321- QWindowSystemInterface::handleExposeEvent(window(), geometry());
1322- } else {
1323- DLOG("UbuntuWindow::onBuffersSwapped_threadSafe [%d] - buffer size (%d,%d). resizeCatchUpAttempts=%d",
1324- d->frameNumber, d->bufferSize.width(), d->bufferSize.height(), d->resizeCatchUpAttempts);
1325- }
1326+ QMutexLocker lock(&mMutex);
1327+ mSurface->onSwapBuffersDone();
1328 }
1329
1330=== modified file 'src/ubuntumirclient/window.h'
1331--- src/ubuntumirclient/window.h 2015-11-09 19:26:55 +0000
1332+++ src/ubuntumirclient/window.h 2015-11-16 22:00:40 +0000
1333@@ -19,20 +19,23 @@
1334
1335 #include <qpa/qplatformwindow.h>
1336 #include <QSharedPointer>
1337+#include <QMutex>
1338
1339-#include <mir_toolkit/mir_client_library.h>
1340+#include <memory>
1341
1342 class UbuntuClipboard;
1343 class UbuntuInput;
1344 class UbuntuScreen;
1345-class UbuntuWindowPrivate;
1346+class UbuntuSurface;
1347+struct MirConnection;
1348+struct MirSurface;
1349
1350 class UbuntuWindow : public QObject, public QPlatformWindow
1351 {
1352 Q_OBJECT
1353 public:
1354 UbuntuWindow(QWindow *w, QSharedPointer<UbuntuClipboard> clipboard, UbuntuScreen *screen,
1355- UbuntuInput *input, MirConnection *mir_connection);
1356+ UbuntuInput *input, MirConnection *mirConnection);
1357 virtual ~UbuntuWindow();
1358
1359 // QPlatformWindow methods.
1360@@ -41,20 +44,20 @@
1361 void setWindowState(Qt::WindowState state) override;
1362 void setVisible(bool visible) override;
1363 void setWindowTitle(const QString &title) override;
1364+ void propagateSizeHints() override;
1365
1366 // New methods.
1367- void* eglSurface() const;
1368- void handleSurfaceResize(int width, int height);
1369- void handleSurfaceFocusChange(bool focused);
1370- void onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight);
1371-
1372- UbuntuWindowPrivate* priv() { return d; }
1373+ void *eglSurface() const;
1374+ MirSurface *mirSurface() const;
1375+ void handleSurfaceResized(int width, int height);
1376+ void handleSurfaceFocused();
1377+ void onSwapBuffersDone();
1378
1379 private:
1380- void createWindow();
1381- void moveResize(const QRect& rect);
1382-
1383- UbuntuWindowPrivate *d;
1384+ mutable QMutex mMutex;
1385+ const WId mId;
1386+ const QSharedPointer<UbuntuClipboard> mClipboard;
1387+ std::unique_ptr<UbuntuSurface> mSurface;
1388 };
1389
1390 #endif // UBUNTU_WINDOW_H

Subscribers

People subscribed via source and target branches