Status: | Work in progress |
---|---|
Proposed branch: | lp:~unity-team/qtubuntu/DPR2 |
Merge into: | lp:qtubuntu |
Prerequisite: | lp:~dandrader/qtubuntu/loggingCategory |
Diff against target: |
767 lines (+229/-152) 9 files modified
debian/changelog (+6/-0) src/ubuntumirclient/backingstore.cpp (+11/-5) src/ubuntumirclient/input.cpp (+15/-12) src/ubuntumirclient/screen.cpp (+24/-15) src/ubuntumirclient/screen.h (+2/-0) src/ubuntumirclient/ubuntumirclient.pro (+2/-1) src/ubuntumirclient/utils.h (+29/-0) src/ubuntumirclient/window.cpp (+136/-117) src/ubuntumirclient/window.h (+4/-2) |
To merge this branch: | bzr merge lp:~unity-team/qtubuntu/DPR2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Daniel d'Andrada (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+281664@code.launchpad.net |
This proposal supersedes a proposal from 2015-12-17.
Commit message
Add device pixel ratio support. Small fix for the panel hack.
Description of the change
To test DPR 2:
stop unity8
initctl set-env --global QT_DEVICE_
start unity8
* Are there any related MPs required for this MP to build/function as expected? Please list.
Nothing depends on this directly, but to do a full test, you'll need:
https:/
* Did you perform an exploratory manual test run of your code change and any related functionality?
Y
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:262
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In src/ubuntumircl
"""
// Window manager can give us a final size different from what we asked for
// so let's check what we ended up getting
{
}
// Assume that the buffer size matches the surface size at creation time
d->bufferSize = geometry.size();
"""
Here bufferSize will be initialized with a scaled size, but it should have the real size.
So bufferSize initialization should probably moved above the line "// Window manager can give us a final size different[...]".
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:264
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal | # |
@Daniel, you were correct, I had mixed up a case where pixel != devicePixel. Since this is far too easy to screw up, I thought it a good idea to append "Px" to any variable that is in pixels, and so any other geometry is in device pixels. I think it helps distinguish them.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
In src/ubuntumircl
"""
DLOG(
"""
bufferSizePx has no x() and y() (as it's a QSize, not a QRect). I think code won't build if you enable debug logging?
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:265
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:266
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:268
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:269
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:270
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:271
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:272
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:273
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:274
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:275
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:276
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:277
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:278
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal | # |
Ok, should be good to go
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Looks good to me (didn't test yet).
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:280
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
And it works fine as well.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Why is that function in src/ubuntumircl
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:290
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal | # |
> Why is that function in src/ubuntumircl
> namespace?
To stop it polluting the global namespace. The header file is private.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Why did you remove that chunk from UbuntuSurface constructor in src/ubuntumircl
"""
auto geom = mWindow-
if (mWindowState == Qt::WindowFullS
} else {
}
"""
Daniel d'Andrada (dandrader) : Posted in a previous version of this proposal | # |
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
On 17/12/2015 11:52, Daniel d'Andrada wrote:
> Why did you remove that chunk from UbuntuSurface constructor in src/ubuntumircl
>
> """
> auto geom = mWindow-
> geom.setWidth(
> geom.setHeight(
> if (mWindowState == Qt::WindowFullS
> geom.setY(0);
> } else {
> geom.setY(
> }
> """
And so is the panel hack refactoring you're doing.
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal | # |
> Why did you remove that chunk from UbuntuSurface constructor in
> src/ubuntumircl
> work.
>
> """
> auto geom = mWindow-
> geom.setWidth(
> geom.setHeight(
> if (mWindowState == Qt::WindowFullS
> geom.setY(0);
> } else {
> geom.setY(
> }
> """
It was a bit related. I wanted to prevent UbuntuSurface having any knowledge of device-indepedent pixels, I wanted it to be purely pixel based. UbuntuSurface pretty much a wrapper for a MirSurface. So that small refactor allowed me to do that, plus eliminate a small bit of duplicate code.
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal | # |
Panel hack refactoring was to ensure the hack was applied at UbuntuWindow creation in a correct fashion.
The issue I found with the old hack was if window was created in fullscreen state, with y=0, then
if (state == Qt::WindowFullS
was not entered, but
} else if (geometry().y() == 0) {
was. So fullscreen surface got the panel hack applied.
I fixed it here as I was testing widget-based apps which did this.
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Code looks ok
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |
Works fine.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:306
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:307
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Daniel d'Andrada (dandrader) wrote : | # |
Still works fine
Michał Sawicz (saviq) wrote : | # |
Text conflict in src/ubuntumircl
Text conflict in src/ubuntumircl
Text conflict in src/ubuntumircl
3 conflicts encountered.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:307
https:/
Executed test runs:
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unmerged revisions
- 307. By Gerry Boland
-
Fix compile error from bad merge
- 306. By Gerry Boland
-
Merge DPI branch
- 305. By Gerry Boland
Preview Diff
1 | === modified file 'debian/changelog' | |||
2 | --- debian/changelog 2016-01-04 09:38:35 +0000 | |||
3 | +++ debian/changelog 2016-01-06 13:29:28 +0000 | |||
4 | @@ -1,3 +1,9 @@ | |||
5 | 1 | qtubuntu (0.63) UNRELEASED; urgency=medium | ||
6 | 2 | |||
7 | 3 | * Add support for Qt5's devicePixelRatio UI scaling mechanism | ||
8 | 4 | |||
9 | 5 | -- Gerry Boland <gerry.boland@canonical.com> Tue, 05 Jan 2016 16:31:27 +0100 | ||
10 | 6 | |||
11 | 1 | qtubuntu (0.62+16.04.20160104-0ubuntu1) xenial; urgency=medium | 7 | qtubuntu (0.62+16.04.20160104-0ubuntu1) xenial; urgency=medium |
12 | 2 | 8 | ||
13 | 3 | [ Daniel d'Andrada ] | 9 | [ Daniel d'Andrada ] |
14 | 4 | 10 | ||
15 | === modified file 'src/ubuntumirclient/backingstore.cpp' | |||
16 | --- src/ubuntumirclient/backingstore.cpp 2014-12-04 12:29:39 +0000 | |||
17 | +++ src/ubuntumirclient/backingstore.cpp 2016-01-06 13:29:28 +0000 | |||
18 | @@ -1,5 +1,5 @@ | |||
19 | 1 | /* | 1 | /* |
21 | 2 | * Copyright (C) 2014 Canonical, Ltd. | 2 | * Copyright (C) 2014-2015 Canonical, Ltd. |
22 | 3 | * | 3 | * |
23 | 4 | * This program is free software: you can redistribute it and/or modify it under | 4 | * This program is free software: you can redistribute it and/or modify it under |
24 | 5 | * the terms of the GNU Lesser General Public License version 3, as published by | 5 | * the terms of the GNU Lesser General Public License version 3, as published by |
25 | @@ -43,8 +43,10 @@ | |||
26 | 43 | { | 43 | { |
27 | 44 | Q_UNUSED(region); | 44 | Q_UNUSED(region); |
28 | 45 | Q_UNUSED(offset); | 45 | Q_UNUSED(offset); |
29 | 46 | const int dpr = int(window->devicePixelRatio()); | ||
30 | 47 | |||
31 | 46 | mContext->makeCurrent(window); | 48 | mContext->makeCurrent(window); |
33 | 47 | glViewport(0, 0, window->width(), window->height()); | 49 | glViewport(0, 0, window->width() * dpr, window->height() * dpr); |
34 | 48 | 50 | ||
35 | 49 | updateTexture(); | 51 | updateTexture(); |
36 | 50 | 52 | ||
37 | @@ -75,12 +77,14 @@ | |||
38 | 75 | 77 | ||
39 | 76 | QRegion fixed; | 78 | QRegion fixed; |
40 | 77 | QRect imageRect = mImage.rect(); | 79 | QRect imageRect = mImage.rect(); |
41 | 80 | const int dpr = int(window()->devicePixelRatio()); | ||
42 | 78 | 81 | ||
44 | 79 | /* Following code taken from QEGLPlatformBackingStore under the terms of the Lesser GPL v2.1 licence | 82 | /* Following code a modified form of that in QEGLPlatformBackingStore, used under the terms of the Lesser GPL v2.1 licence |
45 | 80 | * Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). */ | 83 | * Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). */ |
46 | 81 | Q_FOREACH (const QRect &rect, mDirty.rects()) { | 84 | Q_FOREACH (const QRect &rect, mDirty.rects()) { |
47 | 85 | QRect scaledRect(rect.topLeft() * dpr, rect.size() * dpr); | ||
48 | 82 | // intersect with image rect to be sure | 86 | // intersect with image rect to be sure |
50 | 83 | QRect r = imageRect & rect; | 87 | QRect r = imageRect & scaledRect; |
51 | 84 | 88 | ||
52 | 85 | // if the rect is wide enough it is cheaper to just extend it instead of doing an image copy | 89 | // if the rect is wide enough it is cheaper to just extend it instead of doing an image copy |
53 | 86 | if (r.width() >= imageRect.width() / 2) { | 90 | if (r.width() >= imageRect.width() / 2) { |
54 | @@ -115,7 +119,9 @@ | |||
55 | 115 | 119 | ||
56 | 116 | void UbuntuBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/) | 120 | void UbuntuBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/) |
57 | 117 | { | 121 | { |
59 | 118 | mImage = QImage(size, QImage::Format_RGB32); | 122 | const int dpr = int(window()->devicePixelRatio()); |
60 | 123 | mImage = QImage(size * dpr, QImage::Format_RGB32); | ||
61 | 124 | mImage.setDevicePixelRatio(dpr); | ||
62 | 119 | 125 | ||
63 | 120 | if (mTexture->isCreated()) | 126 | if (mTexture->isCreated()) |
64 | 121 | mTexture->destroy(); | 127 | mTexture->destroy(); |
65 | 122 | 128 | ||
66 | === modified file 'src/ubuntumirclient/input.cpp' | |||
67 | --- src/ubuntumirclient/input.cpp 2016-01-06 13:29:28 +0000 | |||
68 | +++ src/ubuntumirclient/input.cpp 2016-01-06 13:29:28 +0000 | |||
69 | @@ -312,6 +312,7 @@ | |||
70 | 312 | const QRect kWindowGeometry = window->geometry(); | 312 | const QRect kWindowGeometry = window->geometry(); |
71 | 313 | QList<QWindowSystemInterface::TouchPoint> touchPoints; | 313 | QList<QWindowSystemInterface::TouchPoint> touchPoints; |
72 | 314 | 314 | ||
73 | 315 | const int dpr = int(window->devicePixelRatio()); | ||
74 | 315 | 316 | ||
75 | 316 | // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left | 317 | // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left |
76 | 317 | // as Qt::TouchPointMoved | 318 | // as Qt::TouchPointMoved |
77 | @@ -319,10 +320,10 @@ | |||
78 | 319 | for (unsigned int i = 0; i < kPointerCount; ++i) { | 320 | for (unsigned int i = 0; i < kPointerCount; ++i) { |
79 | 320 | QWindowSystemInterface::TouchPoint touchPoint; | 321 | QWindowSystemInterface::TouchPoint touchPoint; |
80 | 321 | 322 | ||
85 | 322 | const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x) + kWindowGeometry.x(); | 323 | const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x) / dpr + kWindowGeometry.x(); |
86 | 323 | const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y) + kWindowGeometry.y(); // see bug lp:1346633 workaround comments elsewhere | 324 | const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y) / dpr + kWindowGeometry.y(); // see bug lp:1346633 workaround comments elsewhere |
87 | 324 | const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major); | 325 | const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major) / dpr; |
88 | 325 | const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor); | 326 | const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor) / dpr; |
89 | 326 | const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure); | 327 | const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure); |
90 | 327 | touchPoint.id = mir_touch_event_id(tev, i); | 328 | touchPoint.id = mir_touch_event_id(tev, i); |
91 | 328 | touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height()); | 329 | touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height()); |
92 | @@ -449,14 +450,16 @@ | |||
93 | 449 | 450 | ||
94 | 450 | void UbuntuInput::dispatchPointerEvent(UbuntuWindow *platformWindow, const MirInputEvent *ev) | 451 | void UbuntuInput::dispatchPointerEvent(UbuntuWindow *platformWindow, const MirInputEvent *ev) |
95 | 451 | { | 452 | { |
104 | 452 | auto window = platformWindow->window(); | 453 | const int dpr = int(platformWindow->devicePixelRatio()); |
105 | 453 | auto timestamp = mir_input_event_get_event_time(ev) / 1000000; | 454 | const auto window = platformWindow->window(); |
106 | 454 | 455 | const auto timestamp = mir_input_event_get_event_time(ev) / 1000000; | |
107 | 455 | auto pev = mir_input_event_get_pointer_event(ev); | 456 | |
108 | 456 | auto action = mir_pointer_event_action(pev); | 457 | const auto pev = mir_input_event_get_pointer_event(ev); |
109 | 457 | auto localPoint = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x), | 458 | const auto action = mir_pointer_event_action(pev); |
110 | 458 | mir_pointer_event_axis_value(pev, mir_pointer_axis_y)); | 459 | |
111 | 459 | auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev)); | 460 | const auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev)); |
112 | 461 | const auto localPoint = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x) / dpr, | ||
113 | 462 | mir_pointer_event_axis_value(pev, mir_pointer_axis_y) / dpr); | ||
114 | 460 | 463 | ||
115 | 461 | switch (action) { | 464 | switch (action) { |
116 | 462 | case mir_pointer_action_button_up: | 465 | case mir_pointer_action_button_up: |
117 | 463 | 466 | ||
118 | === modified file 'src/ubuntumirclient/screen.cpp' | |||
119 | --- src/ubuntumirclient/screen.cpp 2016-01-06 13:29:28 +0000 | |||
120 | +++ src/ubuntumirclient/screen.cpp 2016-01-06 13:29:28 +0000 | |||
121 | @@ -18,6 +18,7 @@ | |||
122 | 18 | #include "screen.h" | 18 | #include "screen.h" |
123 | 19 | #include "logging.h" | 19 | #include "logging.h" |
124 | 20 | #include "orientationchangeevent_p.h" | 20 | #include "orientationchangeevent_p.h" |
125 | 21 | #include "utils.h" | ||
126 | 21 | 22 | ||
127 | 22 | #include <mir_toolkit/mir_client_library.h> | 23 | #include <mir_toolkit/mir_client_library.h> |
128 | 23 | 24 | ||
129 | @@ -101,6 +102,14 @@ | |||
130 | 101 | } | 102 | } |
131 | 102 | } | 103 | } |
132 | 103 | 104 | ||
133 | 105 | namespace { | ||
134 | 106 | int qGetEnvIntValue(const char *varName, bool *ok) | ||
135 | 107 | { | ||
136 | 108 | return qgetenv(varName).toInt(ok); | ||
137 | 109 | } | ||
138 | 110 | } // anonymous namespace | ||
139 | 111 | |||
140 | 112 | |||
141 | 104 | const QEvent::Type OrientationChangeEvent::mType = | 113 | const QEvent::Type OrientationChangeEvent::mType = |
142 | 105 | static_cast<QEvent::Type>(QEvent::registerEventType()); | 114 | static_cast<QEvent::Type>(QEvent::registerEventType()); |
143 | 106 | 115 | ||
144 | @@ -164,18 +173,18 @@ | |||
145 | 164 | } | 173 | } |
146 | 165 | 174 | ||
147 | 166 | // Set vblank swap interval. | 175 | // Set vblank swap interval. |
157 | 167 | int swapInterval = kSwapInterval; | 176 | bool ok; |
158 | 168 | QByteArray swapIntervalString = qgetenv("QTUBUNTU_SWAPINTERVAL"); | 177 | int swapInterval = qGetEnvIntValue("QTUBUNTU_SWAPINTERVAL", &ok); |
159 | 169 | if (!swapIntervalString.isEmpty()) { | 178 | if (!ok) |
160 | 170 | bool ok; | 179 | swapInterval = kSwapInterval; |
161 | 171 | swapInterval = swapIntervalString.toInt(&ok); | 180 | |
162 | 172 | if (!ok) | 181 | qCDebug(ubuntumirclient, "Setting swap interval to %d", swapInterval); |
154 | 173 | swapInterval = kSwapInterval; | ||
155 | 174 | } | ||
156 | 175 | qCDebug(ubuntumirclient, "setting swap interval to %d", swapInterval); | ||
163 | 176 | eglSwapInterval(mEglDisplay, swapInterval); | 182 | eglSwapInterval(mEglDisplay, swapInterval); |
164 | 177 | 183 | ||
166 | 178 | // Get screen resolution. | 184 | // Get screen resolution and properties. |
167 | 185 | int dpr = qGetEnvIntValue("QT_DEVICE_PIXEL_RATIO", &ok); | ||
168 | 186 | mDevicePixelRatio = (ok && dpr > 0) ? dpr : 1.0; | ||
169 | 187 | |||
170 | 179 | auto configDeleter = [](MirDisplayConfiguration *config) { mir_display_config_destroy(config); }; | 188 | auto configDeleter = [](MirDisplayConfiguration *config) { mir_display_config_destroy(config); }; |
171 | 180 | using configUp = std::unique_ptr<MirDisplayConfiguration, decltype(configDeleter)>; | 189 | using configUp = std::unique_ptr<MirDisplayConfiguration, decltype(configDeleter)>; |
172 | 181 | configUp displayConfig(mir_connection_create_display_config(connection), configDeleter); | 190 | configUp displayConfig(mir_connection_create_display_config(connection), configDeleter); |
173 | @@ -187,14 +196,14 @@ | |||
174 | 187 | mOutputId = displayOutput->output_id; | 196 | mOutputId = displayOutput->output_id; |
175 | 188 | 197 | ||
176 | 189 | mPhysicalSize = QSizeF(displayOutput->physical_width_mm, displayOutput->physical_height_mm); | 198 | mPhysicalSize = QSizeF(displayOutput->physical_width_mm, displayOutput->physical_height_mm); |
178 | 190 | qCDebug(ubuntumirclient, "screen physical size: %.2fx%.2f", mPhysicalSize.width(), mPhysicalSize.height()); | 199 | qCDebug(ubuntumirclient, "Screen physical size: %.2fx%.2f mm", mPhysicalSize.width(), mPhysicalSize.height()); |
179 | 191 | 200 | ||
180 | 192 | const MirDisplayMode *mode = &displayOutput->modes[displayOutput->current_mode]; | 201 | const MirDisplayMode *mode = &displayOutput->modes[displayOutput->current_mode]; |
184 | 193 | const int kScreenWidth = mode->horizontal_resolution; | 202 | const int kScreenWidth = divideAndRoundUp(mode->horizontal_resolution, mDevicePixelRatio); |
185 | 194 | const int kScreenHeight = mode->vertical_resolution; | 203 | const int kScreenHeight = divideAndRoundUp(mode->vertical_resolution, mDevicePixelRatio); |
186 | 195 | Q_ASSERT(kScreenWidth > 0 && kScreenHeight > 0); | 204 | ASSERT(kScreenWidth > 0 && kScreenHeight > 0); |
187 | 196 | 205 | ||
189 | 197 | qCDebug(ubuntumirclient, "screen resolution: %dx%d", kScreenWidth, kScreenHeight); | 206 | qCDebug(ubuntumirclient, "Screen resolution: %dx%ddp", kScreenWidth, kScreenHeight); |
190 | 198 | 207 | ||
191 | 199 | mGeometry = QRect(0, 0, kScreenWidth, kScreenHeight); | 208 | mGeometry = QRect(0, 0, kScreenWidth, kScreenHeight); |
192 | 200 | 209 | ||
193 | 201 | 210 | ||
194 | === modified file 'src/ubuntumirclient/screen.h' | |||
195 | --- src/ubuntumirclient/screen.h 2015-11-23 11:10:24 +0000 | |||
196 | +++ src/ubuntumirclient/screen.h 2016-01-06 13:29:28 +0000 | |||
197 | @@ -38,6 +38,7 @@ | |||
198 | 38 | QRect geometry() const override { return mGeometry; } | 38 | QRect geometry() const override { return mGeometry; } |
199 | 39 | QRect availableGeometry() const override { return mGeometry; } | 39 | QRect availableGeometry() const override { return mGeometry; } |
200 | 40 | QSizeF physicalSize() const override { return mPhysicalSize; } | 40 | QSizeF physicalSize() const override { return mPhysicalSize; } |
201 | 41 | qreal devicePixelRatio() const override { return mDevicePixelRatio; } | ||
202 | 41 | Qt::ScreenOrientation nativeOrientation() const override { return mNativeOrientation; } | 42 | Qt::ScreenOrientation nativeOrientation() const override { return mNativeOrientation; } |
203 | 42 | Qt::ScreenOrientation orientation() const override { return mNativeOrientation; } | 43 | Qt::ScreenOrientation orientation() const override { return mNativeOrientation; } |
204 | 43 | QPlatformCursor *cursor() const override { return const_cast<UbuntuCursor*>(&mCursor); } | 44 | QPlatformCursor *cursor() const override { return const_cast<UbuntuCursor*>(&mCursor); } |
205 | @@ -56,6 +57,7 @@ | |||
206 | 56 | private: | 57 | private: |
207 | 57 | QRect mGeometry; | 58 | QRect mGeometry; |
208 | 58 | QSizeF mPhysicalSize; | 59 | QSizeF mPhysicalSize; |
209 | 60 | qreal mDevicePixelRatio; | ||
210 | 59 | Qt::ScreenOrientation mNativeOrientation; | 61 | Qt::ScreenOrientation mNativeOrientation; |
211 | 60 | Qt::ScreenOrientation mCurrentOrientation; | 62 | Qt::ScreenOrientation mCurrentOrientation; |
212 | 61 | QImage::Format mFormat; | 63 | QImage::Format mFormat; |
213 | 62 | 64 | ||
214 | === modified file 'src/ubuntumirclient/ubuntumirclient.pro' | |||
215 | --- src/ubuntumirclient/ubuntumirclient.pro 2015-11-10 11:16:14 +0000 | |||
216 | +++ src/ubuntumirclient/ubuntumirclient.pro 2016-01-06 13:29:28 +0000 | |||
217 | @@ -42,7 +42,8 @@ | |||
218 | 42 | plugin.h \ | 42 | plugin.h \ |
219 | 43 | screen.h \ | 43 | screen.h \ |
220 | 44 | theme.h \ | 44 | theme.h \ |
222 | 45 | window.h | 45 | window.h \ |
223 | 46 | utils.h | ||
224 | 46 | 47 | ||
225 | 47 | # Installation path | 48 | # Installation path |
226 | 48 | target.path += $$[QT_INSTALL_PLUGINS]/platforms | 49 | target.path += $$[QT_INSTALL_PLUGINS]/platforms |
227 | 49 | 50 | ||
228 | === added file 'src/ubuntumirclient/utils.h' | |||
229 | --- src/ubuntumirclient/utils.h 1970-01-01 00:00:00 +0000 | |||
230 | +++ src/ubuntumirclient/utils.h 2016-01-06 13:29:28 +0000 | |||
231 | @@ -0,0 +1,29 @@ | |||
232 | 1 | /* | ||
233 | 2 | * Copyright (C) 2015 Canonical, Ltd. | ||
234 | 3 | * | ||
235 | 4 | * This program is free software: you can redistribute it and/or modify it under | ||
236 | 5 | * the terms of the GNU Lesser General Public License version 3, as published by | ||
237 | 6 | * the Free Software Foundation. | ||
238 | 7 | * | ||
239 | 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
240 | 9 | * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, | ||
241 | 10 | * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
242 | 11 | * Lesser General Public License for more details. | ||
243 | 12 | * | ||
244 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
245 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
246 | 15 | */ | ||
247 | 16 | |||
248 | 17 | #ifndef UTILS_H | ||
249 | 18 | #define UTILS_H | ||
250 | 19 | |||
251 | 20 | #include <QtGlobal> | ||
252 | 21 | |||
253 | 22 | namespace { | ||
254 | 23 | inline int divideAndRoundUp(int numerator, qreal denominator) | ||
255 | 24 | { | ||
256 | 25 | return ceil((qreal)numerator / denominator); | ||
257 | 26 | } | ||
258 | 27 | } // anonymous namespace | ||
259 | 28 | |||
260 | 29 | #endif // UTILS_H | ||
261 | 0 | 30 | ||
262 | === modified file 'src/ubuntumirclient/window.cpp' | |||
263 | --- src/ubuntumirclient/window.cpp 2016-01-06 13:29:28 +0000 | |||
264 | +++ src/ubuntumirclient/window.cpp 2016-01-06 13:29:28 +0000 | |||
265 | @@ -19,6 +19,7 @@ | |||
266 | 19 | #include "clipboard.h" | 19 | #include "clipboard.h" |
267 | 20 | #include "input.h" | 20 | #include "input.h" |
268 | 21 | #include "screen.h" | 21 | #include "screen.h" |
269 | 22 | #include "utils.h" | ||
270 | 22 | #include "logging.h" | 23 | #include "logging.h" |
271 | 23 | 24 | ||
272 | 24 | #include <mir_toolkit/mir_client_library.h> | 25 | #include <mir_toolkit/mir_client_library.h> |
273 | @@ -34,6 +35,11 @@ | |||
274 | 34 | 35 | ||
275 | 35 | #include <EGL/egl.h> | 36 | #include <EGL/egl.h> |
276 | 36 | 37 | ||
277 | 38 | |||
278 | 39 | /* | ||
279 | 40 | * Note: all geometry is in device-independent pixels, except that contained in variables with the | ||
280 | 41 | * suffix "Px" - whose units are (physical) pixels | ||
281 | 42 | */ | ||
282 | 37 | Q_LOGGING_CATEGORY(ubuntumirclientBufferSwap, "ubuntumirclient.bufferSwap", QtWarningMsg) | 43 | Q_LOGGING_CATEGORY(ubuntumirclientBufferSwap, "ubuntumirclient.bufferSwap", QtWarningMsg) |
283 | 38 | 44 | ||
284 | 39 | namespace | 45 | namespace |
285 | @@ -134,13 +140,14 @@ | |||
286 | 134 | Spec makeSurfaceSpec(QWindow *window, UbuntuInput *input, MirConnection *connection) | 140 | Spec makeSurfaceSpec(QWindow *window, UbuntuInput *input, MirConnection *connection) |
287 | 135 | { | 141 | { |
288 | 136 | const auto geom = window->geometry(); | 142 | const auto geom = window->geometry(); |
291 | 137 | const int width = geom.width() > 0 ? geom.width() : 1; | 143 | const int dpr = int(window->devicePixelRatio()); |
292 | 138 | const int height = geom.height() > 0 ? geom.height() : 1; | 144 | const int widthPx = geom.width() > 0 ? geom.width() * dpr : 1; |
293 | 145 | const int heightPx = geom.height() > 0 ? geom.height() * dpr : 1; | ||
294 | 139 | const auto pixelFormat = defaultPixelFormatFor(connection); | 146 | const auto pixelFormat = defaultPixelFormatFor(connection); |
295 | 140 | 147 | ||
296 | 141 | if (U_ON_SCREEN_KEYBOARD_ROLE == roleFor(window)) { | 148 | if (U_ON_SCREEN_KEYBOARD_ROLE == roleFor(window)) { |
299 | 142 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating input method surface (width=%d, height=%d", window, width, height); | 149 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating input method surface with size=(%dx%d)px", window, widthPx, heightPx); |
300 | 143 | return Spec{mir_connection_create_spec_for_input_method(connection, width, height, pixelFormat)}; | 150 | return Spec{mir_connection_create_spec_for_input_method(connection, widthPx, heightPx, pixelFormat)}; |
301 | 144 | } | 151 | } |
302 | 145 | 152 | ||
303 | 146 | const Qt::WindowType type = window->type(); | 153 | const Qt::WindowType type = window->type(); |
304 | @@ -153,13 +160,13 @@ | |||
305 | 153 | parent = input->lastFocusedWindow(); | 160 | parent = input->lastFocusedWindow(); |
306 | 154 | } | 161 | } |
307 | 155 | if (parent) { | 162 | if (parent) { |
312 | 156 | auto pos = geom.topLeft(); | 163 | auto posPx = geom.topLeft() * dpr; |
313 | 157 | pos -= parent->geometry().topLeft(); | 164 | posPx -= parent->geometry().topLeft() * dpr; |
314 | 158 | MirRectangle location{pos.x(), pos.y(), 0, 0}; | 165 | MirRectangle location{posPx.x(), posPx.y(), 0, 0}; |
315 | 159 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating menu surface(width:%d, height:%d)", window, width, height); | 166 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating menu surface with size=(%dx%d)px", window, widthPx, heightPx); |
316 | 160 | return Spec{mir_connection_create_spec_for_menu( | 167 | return Spec{mir_connection_create_spec_for_menu( |
319 | 161 | connection, width, height, pixelFormat, parent->mirSurface(), | 168 | connection, widthPx, heightPx, pixelFormat, parent->mirSurface(), |
320 | 162 | &location, mir_edge_attachment_any)}; | 169 | &location, mir_edge_attachment_any)}; |
321 | 163 | } else { | 170 | } else { |
322 | 164 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - cannot create a menu without a parent!", window); | 171 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - cannot create a menu without a parent!", window); |
323 | 165 | } | 172 | } |
324 | @@ -167,33 +174,33 @@ | |||
325 | 167 | auto parent = transientParentFor(window); | 174 | auto parent = transientParentFor(window); |
326 | 168 | if (parent) { | 175 | if (parent) { |
327 | 169 | // Modal dialog | 176 | // Modal dialog |
330 | 170 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating modal dialog (width=%d, height=%d", window, width, height); | 177 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating modal dialog with size=(%dx%d)px", window, widthPx, heightPx); |
331 | 171 | return Spec{mir_connection_create_spec_for_modal_dialog(connection, width, height, pixelFormat, parent->mirSurface())}; | 178 | return Spec{mir_connection_create_spec_for_modal_dialog(connection, widthPx, heightPx, pixelFormat, parent->mirSurface())}; |
332 | 172 | } else { | 179 | } else { |
333 | 173 | // TODO: do Qt parentless dialogs have the same semantics as mir? | 180 | // TODO: do Qt parentless dialogs have the same semantics as mir? |
336 | 174 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating parentless dialog (width=%d, height=%d)", window, width, height); | 181 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating parentless dialog with size=(%dx%d)px", window, widthPx, heightPx); |
337 | 175 | return Spec{mir_connection_create_spec_for_dialog(connection, width, height, pixelFormat)}; | 182 | return Spec{mir_connection_create_spec_for_dialog(connection, widthPx, heightPx, pixelFormat)}; |
338 | 176 | } | 183 | } |
339 | 177 | } | 184 | } |
342 | 178 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating normal surface(type=0x%x, width=%d, height=%d)", window, type, width, height); | 185 | qCDebug(ubuntumirclient, "makeSurfaceSpec(window=%p) - creating normal surface(type=0x%x, with size=(%dx%d)px", window, type, widthPx, heightPx); |
343 | 179 | return Spec{mir_connection_create_spec_for_normal_surface(connection, width, height, pixelFormat)}; | 186 | return Spec{mir_connection_create_spec_for_normal_surface(connection, widthPx, heightPx, pixelFormat)}; |
344 | 180 | } | 187 | } |
345 | 181 | 188 | ||
347 | 182 | void setSizingConstraints(MirSurfaceSpec *spec, const QSize& minSize, const QSize& maxSize, const QSize& increment) | 189 | void setSizingConstraints(MirSurfaceSpec *spec, const QSize &minSizePx, const QSize &maxSizePx, const QSize &incrementPx) |
348 | 183 | { | 190 | { |
362 | 184 | mir_surface_spec_set_min_width(spec, minSize.width()); | 191 | mir_surface_spec_set_min_width(spec, minSizePx.width()); |
363 | 185 | mir_surface_spec_set_min_height(spec, minSize.height()); | 192 | mir_surface_spec_set_min_height(spec, minSizePx.height()); |
364 | 186 | if (maxSize.width() >= minSize.width()) { | 193 | if (maxSizePx.width() >= minSizePx.width()) { |
365 | 187 | mir_surface_spec_set_max_width(spec, maxSize.width()); | 194 | mir_surface_spec_set_max_width(spec, maxSizePx.width()); |
366 | 188 | } | 195 | } |
367 | 189 | if (maxSize.height() >= minSize.height()) { | 196 | if (maxSizePx.height() >= minSizePx.height()) { |
368 | 190 | mir_surface_spec_set_max_height(spec, maxSize.height()); | 197 | mir_surface_spec_set_max_height(spec, maxSizePx.height()); |
369 | 191 | } | 198 | } |
370 | 192 | if (increment.width() > 0) { | 199 | if (incrementPx.width() > 0) { |
371 | 193 | mir_surface_spec_set_width_increment(spec, increment.width()); | 200 | mir_surface_spec_set_width_increment(spec, incrementPx.width()); |
372 | 194 | } | 201 | } |
373 | 195 | if (increment.height() > 0) { | 202 | if (incrementPx.height() > 0) { |
374 | 196 | mir_surface_spec_set_height_increment(spec, increment.height()); | 203 | mir_surface_spec_set_height_increment(spec, incrementPx.height()); |
375 | 197 | } | 204 | } |
376 | 198 | } | 205 | } |
377 | 199 | 206 | ||
378 | @@ -233,6 +240,10 @@ | |||
379 | 233 | 240 | ||
380 | 234 | } //namespace | 241 | } //namespace |
381 | 235 | 242 | ||
382 | 243 | /* | ||
383 | 244 | * UbuntuSurface - wraps a MirSurface | ||
384 | 245 | * All units are in pixels only (no device pixels). | ||
385 | 246 | */ | ||
386 | 236 | class UbuntuSurface | 247 | class UbuntuSurface |
387 | 237 | { | 248 | { |
388 | 238 | public: | 249 | public: |
389 | @@ -248,7 +259,6 @@ | |||
390 | 248 | , mNeedsRepaint(false) | 259 | , mNeedsRepaint(false) |
391 | 249 | , mParented(mWindow->transientParent() || mWindow->parent()) | 260 | , mParented(mWindow->transientParent() || mWindow->parent()) |
392 | 250 | , mWindowState(mWindow->windowState()) | 261 | , mWindowState(mWindow->windowState()) |
393 | 251 | |||
394 | 252 | { | 262 | { |
395 | 253 | mir_surface_set_event_handler(mMirSurface, surfaceEventCallback, this); | 263 | mir_surface_set_event_handler(mMirSurface, surfaceEventCallback, this); |
396 | 254 | 264 | ||
397 | @@ -257,22 +267,13 @@ | |||
398 | 257 | MirSurfaceParameters parameters; | 267 | MirSurfaceParameters parameters; |
399 | 258 | mir_surface_get_parameters(mMirSurface, ¶meters); | 268 | mir_surface_get_parameters(mMirSurface, ¶meters); |
400 | 259 | 269 | ||
401 | 260 | auto geom = mWindow->geometry(); | ||
402 | 261 | geom.setWidth(parameters.width); | ||
403 | 262 | geom.setHeight(parameters.height); | ||
404 | 263 | if (mWindowState == Qt::WindowFullScreen) { | ||
405 | 264 | geom.setY(0); | ||
406 | 265 | } else { | ||
407 | 266 | geom.setY(panelHeight()); | ||
408 | 267 | } | ||
409 | 268 | |||
410 | 269 | // Assume that the buffer size matches the surface size at creation time | 270 | // Assume that the buffer size matches the surface size at creation time |
414 | 270 | mBufferSize = geom.size(); | 271 | mBufferSizePx.rwidth() = parameters.width; |
415 | 271 | platformWindow->QPlatformWindow::setGeometry(geom); | 272 | mBufferSizePx.rheight() = parameters.height; |
413 | 272 | QWindowSystemInterface::handleGeometryChange(mWindow, geom); | ||
416 | 273 | 273 | ||
419 | 274 | qCDebug(ubuntumirclient, "created surface at (%d, %d) with size (%d, %d), title '%s', role: '%d'\n", | 274 | qCDebug(ubuntumirclient, "created surface with size=(%dx%d)px, title='%s', role=%d", |
420 | 275 | geom.x(), geom.y(), geom.width(), geom.height(), mWindow->title().toUtf8().constData(), roleFor(mWindow)); | 275 | parameters.width, parameters.height, qPrintable(mWindow->title()), roleFor(mWindow)); |
421 | 276 | mPlatformWindow->updateWindowSize(parameters.width, parameters.height); | ||
422 | 276 | } | 277 | } |
423 | 277 | 278 | ||
424 | 278 | ~UbuntuSurface() | 279 | ~UbuntuSurface() |
425 | @@ -283,24 +284,24 @@ | |||
426 | 283 | mir_surface_release_sync(mMirSurface); | 284 | mir_surface_release_sync(mMirSurface); |
427 | 284 | } | 285 | } |
428 | 285 | 286 | ||
431 | 286 | UbuntuSurface(UbuntuSurface const&) = delete; | 287 | UbuntuSurface(const UbuntuSurface &) = delete; |
432 | 287 | UbuntuSurface& operator=(UbuntuSurface const&) = delete; | 288 | UbuntuSurface& operator=(const UbuntuSurface &) = delete; |
433 | 288 | 289 | ||
435 | 289 | void resize(const QSize& newSize); | 290 | void resize(const QSize &newSizePx); |
436 | 290 | void setState(Qt::WindowState newState); | 291 | void setState(Qt::WindowState newState); |
437 | 291 | void setVisible(bool state); | 292 | void setVisible(bool state); |
440 | 292 | void updateTitle(const QString& title); | 293 | void updateTitle(const QString &title); |
441 | 293 | void setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment); | 294 | void setSizingConstraints(const QSize &minSizePx, const QSize &maxSizePx, const QSize &incrementPx); |
442 | 294 | 295 | ||
443 | 295 | void onSwapBuffersDone(); | 296 | void onSwapBuffersDone(); |
445 | 296 | void handleSurfaceResized(int width, int height); | 297 | void handleSurfaceResized(int widthPx, int heightPx); |
446 | 297 | int needsRepaint() const; | 298 | int needsRepaint() const; |
447 | 298 | 299 | ||
448 | 299 | EGLSurface eglSurface() const { return mEglSurface; } | 300 | EGLSurface eglSurface() const { return mEglSurface; } |
449 | 300 | MirSurface *mirSurface() const { return mMirSurface; } | 301 | MirSurface *mirSurface() const { return mMirSurface; } |
450 | 301 | 302 | ||
451 | 302 | private: | 303 | private: |
453 | 303 | static void surfaceEventCallback(MirSurface* surface, const MirEvent *event, void* context); | 304 | static void surfaceEventCallback(MirSurface *surface, const MirEvent *event, void *context); |
454 | 304 | void postEvent(const MirEvent *event); | 305 | void postEvent(const MirEvent *event); |
455 | 305 | void updateSurface(); | 306 | void updateSurface(); |
456 | 306 | 307 | ||
457 | @@ -317,29 +318,29 @@ | |||
458 | 317 | bool mNeedsRepaint; | 318 | bool mNeedsRepaint; |
459 | 318 | bool mParented; | 319 | bool mParented; |
460 | 319 | Qt::WindowState mWindowState; | 320 | Qt::WindowState mWindowState; |
462 | 320 | QSize mBufferSize; | 321 | QSize mBufferSizePx; |
463 | 321 | 322 | ||
464 | 322 | QMutex mTargetSizeMutex; | 323 | QMutex mTargetSizeMutex; |
466 | 323 | QSize mTargetSize; | 324 | QSize mTargetSizePx; |
467 | 324 | }; | 325 | }; |
468 | 325 | 326 | ||
470 | 326 | void UbuntuSurface::resize(const QSize& size) | 327 | void UbuntuSurface::resize(const QSize &sizePx) |
471 | 327 | { | 328 | { |
473 | 328 | qCDebug(ubuntumirclient,"resize(window=%p, width=%d, height=%d)", mWindow, size.width(), size.height()); | 329 | qCDebug(ubuntumirclient,"resize(window=%p) to (%dx%d)px", mWindow, sizePx.width(), sizePx.height()); |
474 | 329 | 330 | ||
475 | 330 | if (mWindowState == Qt::WindowFullScreen || mWindowState == Qt::WindowMaximized) { | 331 | if (mWindowState == Qt::WindowFullScreen || mWindowState == Qt::WindowMaximized) { |
476 | 331 | qCDebug(ubuntumirclient, "resize(window=%p) - not resizing, window is maximized or fullscreen", mWindow); | 332 | qCDebug(ubuntumirclient, "resize(window=%p) - not resizing, window is maximized or fullscreen", mWindow); |
477 | 332 | return; | 333 | return; |
478 | 333 | } | 334 | } |
479 | 334 | 335 | ||
481 | 335 | if (size.isEmpty()) { | 336 | if (sizePx.isEmpty()) { |
482 | 336 | qCDebug(ubuntumirclient, "resize(window=%p) - not resizing, size is empty", mWindow); | 337 | qCDebug(ubuntumirclient, "resize(window=%p) - not resizing, size is empty", mWindow); |
483 | 337 | return; | 338 | return; |
484 | 338 | } | 339 | } |
485 | 339 | 340 | ||
486 | 340 | Spec spec{mir_connection_create_spec_for_changes(mConnection)}; | 341 | Spec spec{mir_connection_create_spec_for_changes(mConnection)}; |
489 | 341 | mir_surface_spec_set_width(spec.get(), size.width()); | 342 | mir_surface_spec_set_width(spec.get(), sizePx.width()); |
490 | 342 | mir_surface_spec_set_height(spec.get(), size.height()); | 343 | mir_surface_spec_set_height(spec.get(), sizePx.height()); |
491 | 343 | mir_surface_apply_spec(mMirSurface, spec.get()); | 344 | mir_surface_apply_spec(mMirSurface, spec.get()); |
492 | 344 | } | 345 | } |
493 | 345 | 346 | ||
494 | @@ -365,7 +366,7 @@ | |||
495 | 365 | mir_wait_for(mir_surface_set_state(mMirSurface, newState)); | 366 | mir_wait_for(mir_surface_set_state(mMirSurface, newState)); |
496 | 366 | } | 367 | } |
497 | 367 | 368 | ||
499 | 368 | void UbuntuSurface::updateTitle(const QString& newTitle) | 369 | void UbuntuSurface::updateTitle(const QString &newTitle) |
500 | 369 | { | 370 | { |
501 | 370 | const auto title = newTitle.toUtf8(); | 371 | const auto title = newTitle.toUtf8(); |
502 | 371 | Spec spec{mir_connection_create_spec_for_changes(mConnection)}; | 372 | Spec spec{mir_connection_create_spec_for_changes(mConnection)}; |
503 | @@ -373,14 +374,14 @@ | |||
504 | 373 | mir_surface_apply_spec(mMirSurface, spec.get()); | 374 | mir_surface_apply_spec(mMirSurface, spec.get()); |
505 | 374 | } | 375 | } |
506 | 375 | 376 | ||
508 | 376 | void UbuntuSurface::setSizingConstraints(const QSize& minSize, const QSize& maxSize, const QSize& increment) | 377 | void UbuntuSurface::setSizingConstraints(const QSize &minSizePx, const QSize &maxSizePx, const QSize &incrementPx) |
509 | 377 | { | 378 | { |
510 | 378 | Spec spec{mir_connection_create_spec_for_changes(mConnection)}; | 379 | Spec spec{mir_connection_create_spec_for_changes(mConnection)}; |
512 | 379 | ::setSizingConstraints(spec.get(), minSize, maxSize, increment); | 380 | ::setSizingConstraints(spec.get(), minSizePx, maxSizePx, incrementPx); |
513 | 380 | mir_surface_apply_spec(mMirSurface, spec.get()); | 381 | mir_surface_apply_spec(mMirSurface, spec.get()); |
514 | 381 | } | 382 | } |
515 | 382 | 383 | ||
517 | 383 | void UbuntuSurface::handleSurfaceResized(int width, int height) | 384 | void UbuntuSurface::handleSurfaceResized(int widthPx, int heightPx) |
518 | 384 | { | 385 | { |
519 | 385 | QMutexLocker lock(&mTargetSizeMutex); | 386 | QMutexLocker lock(&mTargetSizeMutex); |
520 | 386 | 387 | ||
521 | @@ -390,13 +391,13 @@ | |||
522 | 390 | // see TODO in postEvent as the ideal way we should handle this. | 391 | // see TODO in postEvent as the ideal way we should handle this. |
523 | 391 | // The actual buffer size may or may have not changed at this point, so let the rendering | 392 | // The actual buffer size may or may have not changed at this point, so let the rendering |
524 | 392 | // thread drive the window geometry updates. | 393 | // thread drive the window geometry updates. |
526 | 393 | mNeedsRepaint = mTargetSize.width() == width && mTargetSize.height() == height; | 394 | mNeedsRepaint = mTargetSizePx.width() == widthPx && mTargetSizePx.height() == heightPx; |
527 | 394 | } | 395 | } |
528 | 395 | 396 | ||
529 | 396 | int UbuntuSurface::needsRepaint() const | 397 | int UbuntuSurface::needsRepaint() const |
530 | 397 | { | 398 | { |
531 | 398 | if (mNeedsRepaint) { | 399 | if (mNeedsRepaint) { |
533 | 399 | if (mTargetSize != mBufferSize) { | 400 | if (mTargetSizePx != mBufferSizePx) { |
534 | 400 | //If the buffer hasn't changed yet, we need at least two redraws, | 401 | //If the buffer hasn't changed yet, we need at least two redraws, |
535 | 401 | //once to get the new buffer size and propagate the geometry changes | 402 | //once to get the new buffer size and propagate the geometry changes |
536 | 402 | //and the second to redraw the content at the new size | 403 | //and the second to redraw the content at the new size |
537 | @@ -415,29 +416,25 @@ | |||
538 | 415 | static int sFrameNumber = 0; | 416 | static int sFrameNumber = 0; |
539 | 416 | ++sFrameNumber; | 417 | ++sFrameNumber; |
540 | 417 | 418 | ||
561 | 418 | EGLint eglSurfaceWidth = -1; | 419 | EGLint eglSurfaceWidthPx = -1; |
562 | 419 | EGLint eglSurfaceHeight = -1; | 420 | EGLint eglSurfaceHeightPx = -1; |
563 | 420 | eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &eglSurfaceWidth); | 421 | eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &eglSurfaceWidthPx); |
564 | 421 | eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &eglSurfaceHeight); | 422 | eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &eglSurfaceHeightPx); |
565 | 422 | 423 | ||
566 | 423 | const bool validSize = eglSurfaceWidth > 0 && eglSurfaceHeight > 0; | 424 | const bool validSize = eglSurfaceWidthPx > 0 && eglSurfaceHeightPx > 0; |
567 | 424 | 425 | ||
568 | 425 | if (validSize && (mBufferSize.width() != eglSurfaceWidth || mBufferSize.height() != eglSurfaceHeight)) { | 426 | if (validSize && (mBufferSizePx.width() != eglSurfaceWidthPx || mBufferSizePx.height() != eglSurfaceHeightPx)) { |
569 | 426 | 427 | ||
570 | 427 | qCDebug(ubuntumirclientBufferSwap, "onSwapBuffersDone(window=%p) [%d] - size changed (%d, %d) => (%d, %d)", | 428 | qCDebug(ubuntumirclientBufferSwap, "onSwapBuffersDone(window=%p) [%d] - buffer size changed (%dx%d)px => (%dx%d)px", |
571 | 428 | mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height(), eglSurfaceWidth, eglSurfaceHeight); | 429 | mWindow, sFrameNumber, mBufferSizePx.width(), mBufferSizePx.height(), eglSurfaceWidthPx, eglSurfaceHeightPx); |
572 | 429 | 430 | ||
573 | 430 | mBufferSize.rwidth() = eglSurfaceWidth; | 431 | mBufferSizePx.rwidth() = eglSurfaceWidthPx; |
574 | 431 | mBufferSize.rheight() = eglSurfaceHeight; | 432 | mBufferSizePx.rheight() = eglSurfaceHeightPx; |
575 | 432 | 433 | ||
576 | 433 | QRect newGeometry = mPlatformWindow->geometry(); | 434 | mPlatformWindow->updateWindowSize(eglSurfaceWidthPx, eglSurfaceHeightPx); |
557 | 434 | newGeometry.setSize(mBufferSize); | ||
558 | 435 | |||
559 | 436 | mPlatformWindow->QPlatformWindow::setGeometry(newGeometry); | ||
560 | 437 | QWindowSystemInterface::handleGeometryChange(mWindow, newGeometry); | ||
577 | 438 | } else { | 435 | } else { |
580 | 439 | qCDebug(ubuntumirclientBufferSwap, "onSwapBuffersDone(window=%p) [%d] - buffer size (%d,%d)", | 436 | qCDebug(ubuntumirclientBufferSwap, "onSwapBuffersDone(window=%p) [%d] - buffer size=(%dx%d)px", |
581 | 440 | mWindow, sFrameNumber, mBufferSize.width(), mBufferSize.height()); | 437 | mWindow, sFrameNumber, mBufferSizePx.width(), mBufferSizePx.height()); |
582 | 441 | } | 438 | } |
583 | 442 | } | 439 | } |
584 | 443 | 440 | ||
585 | @@ -458,13 +455,13 @@ | |||
586 | 458 | // As a workaround, we use the width/height as an identifier of this latest event | 455 | // As a workaround, we use the width/height as an identifier of this latest event |
587 | 459 | // so the event handler (handleSurfaceResized) can discard/ignore old ones. | 456 | // so the event handler (handleSurfaceResized) can discard/ignore old ones. |
588 | 460 | const auto resizeEvent = mir_event_get_resize_event(event); | 457 | const auto resizeEvent = mir_event_get_resize_event(event); |
592 | 461 | const auto width = mir_resize_event_get_width(resizeEvent); | 458 | const auto widthPx = mir_resize_event_get_width(resizeEvent); |
593 | 462 | const auto height = mir_resize_event_get_height(resizeEvent); | 459 | const auto heightPx = mir_resize_event_get_height(resizeEvent); |
594 | 463 | qCDebug(ubuntumirclient, "resizeEvent(window=%p, width=%d, height=%d)", mWindow, width, height); | 460 | qCDebug(ubuntumirclient, "resizeEvent(window=%p, size=(%dx%d)px", mWindow, widthPx, heightPx); |
595 | 464 | 461 | ||
596 | 465 | QMutexLocker lock(&mTargetSizeMutex); | 462 | QMutexLocker lock(&mTargetSizeMutex); |
599 | 466 | mTargetSize.rwidth() = width; | 463 | mTargetSizePx.rwidth() = widthPx; |
600 | 467 | mTargetSize.rheight() = height; | 464 | mTargetSizePx.rheight() = heightPx; |
601 | 468 | } | 465 | } |
602 | 469 | 466 | ||
603 | 470 | mInput->postEvent(mPlatformWindow, event); | 467 | mInput->postEvent(mPlatformWindow, event); |
604 | @@ -496,7 +493,10 @@ | |||
605 | 496 | , mClipboard(clipboard) | 493 | , mClipboard(clipboard) |
606 | 497 | , mSurface(new UbuntuSurface{this, screen, input, connection}) | 494 | , mSurface(new UbuntuSurface{this, screen, input, connection}) |
607 | 498 | { | 495 | { |
609 | 499 | qCDebug(ubuntumirclient, "UbuntuWindow(window=%p, screen=%p, input=%p, surf=%p)", w, screen, input, mSurface.get()); | 496 | qCDebug(ubuntumirclient, "UbuntuWindow(window=%p, screen=%p, input=%p, surf=%p) with title '%s', role: '%d'", |
610 | 497 | w, screen, input, mSurface.get(), qPrintable(window()->title()), roleFor(window())); | ||
611 | 498 | |||
612 | 499 | enablePanelHeightHack(w->windowState() != Qt::WindowFullScreen); | ||
613 | 500 | } | 500 | } |
614 | 501 | 501 | ||
615 | 502 | UbuntuWindow::~UbuntuWindow() | 502 | UbuntuWindow::~UbuntuWindow() |
616 | @@ -504,12 +504,25 @@ | |||
617 | 504 | qCDebug(ubuntumirclient, "~UbuntuWindow(window=%p)", this); | 504 | qCDebug(ubuntumirclient, "~UbuntuWindow(window=%p)", this); |
618 | 505 | } | 505 | } |
619 | 506 | 506 | ||
621 | 507 | void UbuntuWindow::handleSurfaceResized(int width, int height) | 507 | void UbuntuWindow::updateWindowSize(int widthPx, int heightPx) // after when Mir has resized the surface |
622 | 508 | { | ||
623 | 509 | const float dpr = devicePixelRatio(); | ||
624 | 510 | auto geom = geometry(); | ||
625 | 511 | geom.setWidth(divideAndRoundUp(widthPx, dpr)); | ||
626 | 512 | geom.setHeight(divideAndRoundUp(heightPx, dpr)); | ||
627 | 513 | |||
628 | 514 | QPlatformWindow::setGeometry(geom); | ||
629 | 515 | QWindowSystemInterface::handleGeometryChange(window(), geom); | ||
630 | 516 | |||
631 | 517 | qCDebug(ubuntumirclient) << "Surface geometry updated:" << geom; | ||
632 | 518 | } | ||
633 | 519 | |||
634 | 520 | void UbuntuWindow::handleSurfaceResized(int widthPx, int heightPx) | ||
635 | 508 | { | 521 | { |
636 | 509 | QMutexLocker lock(&mMutex); | 522 | QMutexLocker lock(&mMutex); |
638 | 510 | qCDebug(ubuntumirclient, "handleSurfaceResize(window=%p, width=%d, height=%d)", window(), width, height); | 523 | qCDebug(ubuntumirclient, "handleSurfaceResize(window=%p, size=(%dx%d)px", window(), widthPx, heightPx); |
639 | 511 | 524 | ||
641 | 512 | mSurface->handleSurfaceResized(width, height); | 525 | mSurface->handleSurfaceResized(widthPx, heightPx); |
642 | 513 | 526 | ||
643 | 514 | // This resize event could have occurred just after the last buffer swap for this window. | 527 | // This resize event could have occurred just after the last buffer swap for this window. |
644 | 515 | // This means the client may still be holding a buffer with the older size. The first redraw call | 528 | // This means the client may still be holding a buffer with the older size. The first redraw call |
645 | @@ -519,7 +532,7 @@ | |||
646 | 519 | auto const numRepaints = mSurface->needsRepaint(); | 532 | auto const numRepaints = mSurface->needsRepaint(); |
647 | 520 | qCDebug(ubuntumirclient, "handleSurfaceResize(window=%p) redraw %d times", window(), numRepaints); | 533 | qCDebug(ubuntumirclient, "handleSurfaceResize(window=%p) redraw %d times", window(), numRepaints); |
648 | 521 | for (int i = 0; i < numRepaints; i++) { | 534 | for (int i = 0; i < numRepaints; i++) { |
650 | 522 | qCDebug(ubuntumirclient, "handleSurfaceResize(window=%p) repainting width=%d, height=%d", window(), geometry().size().width(), geometry().size().height()); | 535 | qCDebug(ubuntumirclient, "handleSurfaceResize(window=%p) repainting size=(%dx%d)dp", window(), geometry().size().width(), geometry().size().height()); |
651 | 523 | QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); | 536 | QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); |
652 | 524 | } | 537 | } |
653 | 525 | } | 538 | } |
654 | @@ -543,7 +556,7 @@ | |||
655 | 543 | qCDebug(ubuntumirclient, "setWindowState(window=%p, %s)", this, qtWindowStateToStr(state)); | 556 | qCDebug(ubuntumirclient, "setWindowState(window=%p, %s)", this, qtWindowStateToStr(state)); |
656 | 544 | mSurface->setState(state); | 557 | mSurface->setState(state); |
657 | 545 | 558 | ||
659 | 546 | updatePanelHeightHack(state); | 559 | enablePanelHeightHack(state != Qt::WindowFullScreen); |
660 | 547 | } | 560 | } |
661 | 548 | 561 | ||
662 | 549 | /* | 562 | /* |
663 | @@ -552,34 +565,34 @@ | |||
664 | 552 | window is always on the top-left corner, right below the indicators panel if not | 565 | window is always on the top-left corner, right below the indicators panel if not |
665 | 553 | in fullscreen. | 566 | in fullscreen. |
666 | 554 | */ | 567 | */ |
668 | 555 | void UbuntuWindow::updatePanelHeightHack(Qt::WindowState state) | 568 | void UbuntuWindow::enablePanelHeightHack(bool enable) |
669 | 556 | { | 569 | { |
672 | 557 | if (state == Qt::WindowFullScreen && geometry().y() != 0) { | 570 | QRect newGeometry = geometry(); |
673 | 558 | QRect newGeometry = geometry(); | 571 | if (enable) { |
674 | 572 | newGeometry.setY(::panelHeight()); | ||
675 | 573 | } else { | ||
676 | 559 | newGeometry.setY(0); | 574 | newGeometry.setY(0); |
682 | 560 | QPlatformWindow::setGeometry(newGeometry); | 575 | } |
683 | 561 | QWindowSystemInterface::handleGeometryChange(window(), newGeometry); | 576 | |
684 | 562 | } else if (geometry().y() == 0) { | 577 | if (newGeometry != geometry()) { |
680 | 563 | QRect newGeometry = geometry(); | ||
681 | 564 | newGeometry.setY(panelHeight()); | ||
685 | 565 | QPlatformWindow::setGeometry(newGeometry); | 578 | QPlatformWindow::setGeometry(newGeometry); |
686 | 566 | QWindowSystemInterface::handleGeometryChange(window(), newGeometry); | 579 | QWindowSystemInterface::handleGeometryChange(window(), newGeometry); |
687 | 567 | } | 580 | } |
688 | 568 | } | 581 | } |
689 | 569 | 582 | ||
691 | 570 | void UbuntuWindow::setGeometry(const QRect& rect) | 583 | void UbuntuWindow::setGeometry(const QRect &rect) |
692 | 571 | { | 584 | { |
693 | 572 | QMutexLocker lock(&mMutex); | 585 | QMutexLocker lock(&mMutex); |
696 | 573 | qCDebug(ubuntumirclient, "setGeometry (window=%p, x=%d, y=%d, width=%d, height=%d)", | 586 | qCDebug(ubuntumirclient, "setGeometry (window=%p, position=(%d, %d)dp, size=(%dx%d)dp)", |
697 | 574 | window(), rect.x(), rect.y(), rect.width(), rect.height()); | 587 | window(), rect.x(), rect.y(), rect.width(), rect.height()); |
698 | 575 | 588 | ||
699 | 576 | //NOTE: mir surfaces cannot be moved by the client so ignore the topLeft coordinates | 589 | //NOTE: mir surfaces cannot be moved by the client so ignore the topLeft coordinates |
700 | 577 | const auto newSize = rect.size(); | 590 | const auto newSize = rect.size(); |
701 | 578 | auto newGeometry = geometry(); | 591 | auto newGeometry = geometry(); |
702 | 579 | newGeometry.setSize(newSize); | 592 | newGeometry.setSize(newSize); |
703 | 580 | QPlatformWindow::setGeometry(newGeometry); | ||
704 | 581 | 593 | ||
706 | 582 | mSurface->resize(newSize); | 594 | mSurface->resize(newSize * devicePixelRatio()); |
707 | 595 | // Note: don't call handleGeometryChange here, wait to see what Mir replies with. | ||
708 | 583 | } | 596 | } |
709 | 584 | 597 | ||
710 | 585 | void UbuntuWindow::setVisible(bool visible) | 598 | void UbuntuWindow::setVisible(bool visible) |
711 | @@ -595,7 +608,7 @@ | |||
712 | 595 | QWindowSystemInterface::flushWindowSystemEvents(); | 608 | QWindowSystemInterface::flushWindowSystemEvents(); |
713 | 596 | } | 609 | } |
714 | 597 | 610 | ||
716 | 598 | void UbuntuWindow::setWindowTitle(const QString& title) | 611 | void UbuntuWindow::setWindowTitle(const QString &title) |
717 | 599 | { | 612 | { |
718 | 600 | QMutexLocker lock(&mMutex); | 613 | QMutexLocker lock(&mMutex); |
719 | 601 | qCDebug(ubuntumirclient, "setWindowTitle(window=%p) title=%s)", window(), title.toUtf8().constData()); | 614 | qCDebug(ubuntumirclient, "setWindowTitle(window=%p) title=%s)", window(), title.toUtf8().constData()); |
720 | @@ -606,11 +619,17 @@ | |||
721 | 606 | { | 619 | { |
722 | 607 | QMutexLocker lock(&mMutex); | 620 | QMutexLocker lock(&mMutex); |
723 | 608 | const auto win = window(); | 621 | const auto win = window(); |
729 | 609 | qCDebug(ubuntumirclient, "propagateSizeHints(window=%p) min(%d,%d), max(%d,%d) increment(%d, %d)", | 622 | const float dpr = devicePixelRatio(); |
730 | 610 | win, win->minimumSize().width(), win->minimumSize().height(), | 623 | qCDebug(ubuntumirclient, "propagateSizeHints(window=%p) min(%dx%d)dp; max(%dx%d)dp; increment(%dx%d)dp", |
731 | 611 | win->maximumSize().width(), win->maximumSize().height(), | 624 | win, win->minimumSize().width(), win->minimumSize().height(), |
732 | 612 | win->sizeIncrement().width(), win->sizeIncrement().height()); | 625 | win->maximumSize().width(), win->maximumSize().height(), |
733 | 613 | mSurface->setSizingConstraints(win->minimumSize(), win->maximumSize(), win->sizeIncrement()); | 626 | win->sizeIncrement().width(), win->sizeIncrement().height()); |
734 | 627 | mSurface->setSizingConstraints(win->minimumSize() * dpr, win->maximumSize() * dpr, win->sizeIncrement() * dpr); | ||
735 | 628 | } | ||
736 | 629 | |||
737 | 630 | qreal UbuntuWindow::devicePixelRatio() const | ||
738 | 631 | { | ||
739 | 632 | return screen() ? screen()->devicePixelRatio() : 1.0; // not impossible a Window has no attached Screen | ||
740 | 614 | } | 633 | } |
741 | 615 | 634 | ||
742 | 616 | void* UbuntuWindow::eglSurface() const | 635 | void* UbuntuWindow::eglSurface() const |
743 | 617 | 636 | ||
744 | === modified file 'src/ubuntumirclient/window.h' | |||
745 | --- src/ubuntumirclient/window.h 2016-01-06 13:29:28 +0000 | |||
746 | +++ src/ubuntumirclient/window.h 2016-01-06 13:29:28 +0000 | |||
747 | @@ -46,16 +46,18 @@ | |||
748 | 46 | void setVisible(bool visible) override; | 46 | void setVisible(bool visible) override; |
749 | 47 | void setWindowTitle(const QString &title) override; | 47 | void setWindowTitle(const QString &title) override; |
750 | 48 | void propagateSizeHints() override; | 48 | void propagateSizeHints() override; |
751 | 49 | qreal devicePixelRatio() const override; | ||
752 | 49 | 50 | ||
753 | 50 | // New methods. | 51 | // New methods. |
754 | 51 | void *eglSurface() const; | 52 | void *eglSurface() const; |
755 | 52 | MirSurface *mirSurface() const; | 53 | MirSurface *mirSurface() const; |
757 | 53 | void handleSurfaceResized(int width, int height); | 54 | void updateWindowSize(int widthPx, int heightPx); |
758 | 55 | void handleSurfaceResized(int widthPx, int heightPx); | ||
759 | 54 | void handleSurfaceFocused(); | 56 | void handleSurfaceFocused(); |
760 | 55 | void onSwapBuffersDone(); | 57 | void onSwapBuffersDone(); |
761 | 56 | 58 | ||
762 | 57 | private: | 59 | private: |
764 | 58 | void updatePanelHeightHack(Qt::WindowState); | 60 | void enablePanelHeightHack(bool enable); |
765 | 59 | mutable QMutex mMutex; | 61 | mutable QMutex mMutex; |
766 | 60 | const WId mId; | 62 | const WId mId; |
767 | 61 | const QSharedPointer<UbuntuClipboard> mClipboard; | 63 | const QSharedPointer<UbuntuClipboard> mClipboard; |
PASSED: Continuous integration, rev:261 jenkins. qa.ubuntu. com/job/ qtubuntu- ci/204/ jenkins. qa.ubuntu. com/job/ qtubuntu- vivid-armhf- ci/30 jenkins. qa.ubuntu. com/job/ qtubuntu- vivid-armhf- ci/30/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/qtubuntu- ci/204/ rebuild
http://