Merge lp:~3v1n0/unity/decoration-edges-improvements into lp:unity
- decoration-edges-improvements
- Merge into trunk
Status: | Merged | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Marco Trevisan (Treviño) | ||||||||||||||||||||||||
Approved revision: | no longer in the source branch. | ||||||||||||||||||||||||
Merged at revision: | 3882 | ||||||||||||||||||||||||
Proposed branch: | lp:~3v1n0/unity/decoration-edges-improvements | ||||||||||||||||||||||||
Merge into: | lp:unity | ||||||||||||||||||||||||
Diff against target: |
1018 lines (+333/-177) 13 files modified
UnityCore/DesktopUtilities.cpp (+0/-2) decorations/DecoratedWindow.cpp (+139/-64) decorations/DecorationsEdgeBorders.cpp (+32/-19) decorations/DecorationsForceQuitDialog.cpp (+17/-3) decorations/DecorationsManager.cpp (+16/-25) decorations/DecorationsPriv.h (+11/-3) decorations/DecorationsTitle.cpp (+1/-0) decorations/DecorationsWidgets.cpp (+13/-1) decorations/DecorationsWidgets.h (+5/-0) plugins/unityshell/src/unityshell.cpp (+1/-0) unity-shared/CompizUtils.cpp (+73/-54) unity-shared/CompizUtils.h (+24/-5) unity-shared/XWindowManager.cpp (+1/-1) |
||||||||||||||||||||||||
To merge this branch: | bzr merge lp:~3v1n0/unity/decoration-edges-improvements | ||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Brandon Schaefer (community) | Approve | ||
Review via email: mp+236744@code.launchpad.net |
Commit message
DecoratedWindow: make edges independent from borders and properly update them on actions changed
Now windows can have edges (handles) also if they are not fully decorated, and
the other way around.
Improved the logic to detect which kind of decorations are supported by windows
(so now we add shadows to windows with borders, and we do not to shaped ones).
Removed a lot of duplicated matrix/region computations.
Properly rebuild decorations when _NET_WM_
Fixes to the ForceQuit dialog shadows (and support for backdrop mode).
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
Brandon Schaefer (brandontschaefer) wrote : | # |
LGTM
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3836
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3837
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'UnityCore/DesktopUtilities.cpp' |
2 | --- UnityCore/DesktopUtilities.cpp 2014-08-26 22:59:57 +0000 |
3 | +++ UnityCore/DesktopUtilities.cpp 2014-10-10 14:56:03 +0000 |
4 | @@ -71,8 +71,6 @@ |
5 | const char *config_dir = g_get_user_config_dir(); |
6 | auto unity_config = glib::gchar_to_string(config_dir).append(G_DIR_SEPARATOR_S "unity" G_DIR_SEPARATOR_S); |
7 | |
8 | - printf("CONFIG %s\n", unity_config.c_str()); |
9 | - |
10 | if (g_mkdir_with_parents(unity_config.c_str(), 0700) >= 0) |
11 | return unity_config; |
12 | |
13 | |
14 | === modified file 'decorations/DecoratedWindow.cpp' |
15 | --- decorations/DecoratedWindow.cpp 2014-07-30 00:49:35 +0000 |
16 | +++ decorations/DecoratedWindow.cpp 2014-10-10 14:56:03 +0000 |
17 | @@ -1,6 +1,6 @@ |
18 | // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- |
19 | /* |
20 | - * Copyright (C) 2013 Canonical Ltd |
21 | + * Copyright (C) 2013-2014 Canonical Ltd |
22 | * |
23 | * This program is free software: you can redistribute it and/or modify |
24 | * it under the terms of the GNU General Public License version 3 as |
25 | @@ -44,8 +44,12 @@ |
26 | , cwin_(CompositeWindow::get(win_)) |
27 | , glwin_(GLWindow::get(win_)) |
28 | , frame_(0) |
29 | + , monitor_(0) |
30 | , dirty_geo_(true) |
31 | - , monitor_(0) |
32 | + , dirty_frame_(false) |
33 | + , deco_elements_(cu::DecorationElement::NONE) |
34 | + , last_mwm_decor_(win_->mwmDecor()) |
35 | + , last_actions_(win_->actions()) |
36 | , cv_(Settings::Instance().em()) |
37 | { |
38 | active.changed.connect([this] (bool active) { |
39 | @@ -105,14 +109,27 @@ |
40 | |
41 | void Window::Impl::Update() |
42 | { |
43 | - ShouldBeDecorated() ? Decorate() : Undecorate(); |
44 | + UpdateElements(); |
45 | + (deco_elements_ & (cu::DecorationElement::EDGE | cu::DecorationElement::BORDER)) ? Decorate() : Undecorate(); |
46 | + last_mwm_decor_ = win_->mwmDecor(); |
47 | + last_actions_ = win_->actions(); |
48 | } |
49 | |
50 | void Window::Impl::Decorate() |
51 | { |
52 | SetupExtents(); |
53 | UpdateFrame(); |
54 | - SetupWindowControls(); |
55 | + SetupWindowEdges(); |
56 | + |
57 | + if (deco_elements_ & cu::DecorationElement::BORDER) |
58 | + { |
59 | + SetupWindowControls(); |
60 | + } |
61 | + else |
62 | + { |
63 | + CleanupWindowControls(); |
64 | + bg_textures_.clear(); |
65 | + } |
66 | } |
67 | |
68 | void Window::Impl::Undecorate() |
69 | @@ -120,6 +137,7 @@ |
70 | UnsetExtents(); |
71 | UnsetFrame(); |
72 | CleanupWindowControls(); |
73 | + CleanupWindowEdges(); |
74 | bg_textures_.clear(); |
75 | } |
76 | |
77 | @@ -139,17 +157,27 @@ |
78 | if (win_->hasUnmapReference()) |
79 | return; |
80 | |
81 | - auto const& sb = Style::Get()->Border(); |
82 | - CompWindowExtents border(cv_->CP(sb.left), |
83 | - cv_->CP(sb.right), |
84 | - cv_->CP(sb.top), |
85 | - cv_->CP(sb.bottom)); |
86 | - |
87 | - auto const& ib = Style::Get()->InputBorder(); |
88 | - CompWindowExtents input(cv_->CP(sb.left + ib.left), |
89 | - cv_->CP(sb.right + ib.right), |
90 | - cv_->CP(sb.top + ib.top), |
91 | - cv_->CP(sb.bottom + ib.bottom)); |
92 | + CompWindowExtents border; |
93 | + |
94 | + if (deco_elements_ & cu::DecorationElement::BORDER) |
95 | + { |
96 | + auto const& sb = Style::Get()->Border(); |
97 | + border.left = cv_->CP(sb.left); |
98 | + border.right = cv_->CP(sb.right); |
99 | + border.top = cv_->CP(sb.top); |
100 | + border.bottom = cv_->CP(sb.bottom); |
101 | + } |
102 | + |
103 | + CompWindowExtents input(border); |
104 | + |
105 | + if (deco_elements_ & cu::DecorationElement::EDGE) |
106 | + { |
107 | + auto const& ib = Style::Get()->InputBorder(); |
108 | + input.left += cv_->CP(ib.left); |
109 | + input.right += cv_->CP(ib.right); |
110 | + input.top += cv_->CP(ib.top); |
111 | + input.bottom += cv_->CP(ib.bottom); |
112 | + } |
113 | |
114 | if (win_->border() != border || win_->input() != input) |
115 | win_->setWindowFrameExtents(&border, &input); |
116 | @@ -183,6 +211,15 @@ |
117 | UpdateFrameGeo(frame_geo); |
118 | } |
119 | |
120 | +void Window::Impl::UpdateFrameActions() |
121 | +{ |
122 | + if (!dirty_frame_ && (win_->mwmDecor() != last_mwm_decor_ || win_->actions() != last_actions_)) |
123 | + { |
124 | + dirty_frame_ = true; |
125 | + Damage(); |
126 | + } |
127 | +} |
128 | + |
129 | void Window::Impl::CreateFrame(nux::Geometry const& frame_geo) |
130 | { |
131 | /* Since we're reparenting windows here, we need to grab the server |
132 | @@ -280,6 +317,42 @@ |
133 | win_->updateFrameRegion(); |
134 | } |
135 | |
136 | +void Window::Impl::SetupWindowEdges() |
137 | +{ |
138 | + if (input_mixer_) |
139 | + return; |
140 | + |
141 | + dpi_changed_ = Settings::Instance().dpi_changed.connect([this] { |
142 | + Update(); |
143 | + edge_borders_->scale = cv_->DPIScale(); |
144 | + if (top_layout_) top_layout_->scale = cv_->DPIScale(); |
145 | + }); |
146 | + |
147 | + input_mixer_ = std::make_shared<InputMixer>(); |
148 | + edge_borders_ = std::make_shared<EdgeBorders>(win_); |
149 | + edge_borders_->scale = cv_->DPIScale(); |
150 | + input_mixer_->PushToFront(edge_borders_); |
151 | + |
152 | + UpdateWindowEdgesGeo(); |
153 | +} |
154 | + |
155 | +void Window::Impl::UpdateWindowEdgesGeo() |
156 | +{ |
157 | + if (!edge_borders_) |
158 | + return; |
159 | + |
160 | + auto const& input = win_->inputRect(); |
161 | + edge_borders_->SetCoords(input.x(), input.y()); |
162 | + edge_borders_->SetSize(input.width(), input.height()); |
163 | +} |
164 | + |
165 | +void Window::Impl::CleanupWindowEdges() |
166 | +{ |
167 | + input_mixer_.reset(); |
168 | + edge_borders_.reset(); |
169 | + dpi_changed_->disconnect(); |
170 | +} |
171 | + |
172 | void Window::Impl::SetupWindowControls() |
173 | { |
174 | if (top_layout_) |
175 | @@ -291,26 +364,7 @@ |
176 | Decorate(); |
177 | }); |
178 | |
179 | - dpi_changed_ = Settings::Instance().dpi_changed.connect([this] { |
180 | - Update(); |
181 | - top_layout_->scale = cv_->DPIScale(); |
182 | - }); |
183 | - |
184 | - input_mixer_ = std::make_shared<InputMixer>(); |
185 | - |
186 | - if (win_->actions() & CompWindowActionResizeMask) |
187 | - { |
188 | - auto edges = std::make_shared<EdgeBorders>(win_); |
189 | - grab_edge_ = edges->GetEdge(Edge::Type::GRAB); |
190 | - edge_borders_ = edges; |
191 | - } |
192 | - else /*if (win_->actions() & CompWindowActionMoveMask)*/ |
193 | - { |
194 | - edge_borders_ = std::make_shared<GrabEdge>(win_); |
195 | - grab_edge_ = edge_borders_; |
196 | - } |
197 | - |
198 | - input_mixer_->PushToFront(edge_borders_); |
199 | + grab_edge_ = std::static_pointer_cast<EdgeBorders>(edge_borders_)->GetEdge(Edge::Type::GRAB); |
200 | |
201 | auto padding = style->Padding(Side::TOP); |
202 | top_layout_ = std::make_shared<Layout>(); |
203 | @@ -345,6 +399,7 @@ |
204 | top_layout_->Append(title_layout); |
205 | |
206 | input_mixer_->PushToFront(top_layout_); |
207 | + dirty_frame_ = false; |
208 | |
209 | SetupAppMenu(); |
210 | RedrawDecorations(); |
211 | @@ -355,12 +410,12 @@ |
212 | if (title_) |
213 | last_title_ = title_->text(); |
214 | |
215 | + if (input_mixer_) |
216 | + input_mixer_->Remove(top_layout_); |
217 | + |
218 | UnsetAppMenu(); |
219 | theme_changed_->disconnect(); |
220 | - dpi_changed_->disconnect(); |
221 | top_layout_.reset(); |
222 | - input_mixer_.reset(); |
223 | - edge_borders_.reset(); |
224 | } |
225 | |
226 | bool Window::Impl::IsMaximized() const |
227 | @@ -368,26 +423,25 @@ |
228 | return (win_->state() & MAXIMIZE_STATE) == MAXIMIZE_STATE; |
229 | } |
230 | |
231 | +void Window::Impl::UpdateElements() |
232 | +{ |
233 | + if (!parent_->scaled() && IsMaximized()) |
234 | + { |
235 | + deco_elements_ = cu::DecorationElement::NONE; |
236 | + return; |
237 | + } |
238 | + |
239 | + deco_elements_ = cu::WindowDecorationElements(win_); |
240 | +} |
241 | + |
242 | bool Window::Impl::ShadowDecorated() const |
243 | { |
244 | - if (!parent_->scaled() && IsMaximized()) |
245 | - return false; |
246 | - |
247 | - if (!cu::IsWindowShadowDecorable(win_)) |
248 | - return false; |
249 | - |
250 | - return true; |
251 | + return deco_elements_ & cu::DecorationElement::SHADOW; |
252 | } |
253 | |
254 | bool Window::Impl::FullyDecorated() const |
255 | { |
256 | - if (!parent_->scaled() && IsMaximized()) |
257 | - return false; |
258 | - |
259 | - if (!cu::IsWindowFullyDecorable(win_)) |
260 | - return false; |
261 | - |
262 | - return true; |
263 | + return deco_elements_ & cu::DecorationElement::BORDER; |
264 | } |
265 | |
266 | bool Window::Impl::ShouldBeDecorated() const |
267 | @@ -429,6 +483,7 @@ |
268 | } |
269 | |
270 | deco_tex.SetCoords(geo.x, geo.y); |
271 | + deco_tex.quad.region = deco_tex.quad.box; |
272 | } |
273 | |
274 | void Window::Impl::UpdateDecorationTextures() |
275 | @@ -440,7 +495,6 @@ |
276 | } |
277 | |
278 | auto const& geo = win_->borderRect(); |
279 | - auto const& input = win_->inputRect(); |
280 | auto const& border = win_->border(); |
281 | |
282 | bg_textures_.resize(4); |
283 | @@ -452,19 +506,18 @@ |
284 | top_layout_->SetCoords(geo.x(), geo.y()); |
285 | top_layout_->SetSize(geo.width(), border.top); |
286 | |
287 | - if (edge_borders_) |
288 | - { |
289 | - edge_borders_->SetCoords(input.x(), input.y()); |
290 | - edge_borders_->SetSize(input.width(), input.height()); |
291 | - } |
292 | - |
293 | SyncMenusGeometries(); |
294 | } |
295 | |
296 | void Window::Impl::ComputeShadowQuads() |
297 | { |
298 | - if (last_shadow_rect_.isEmpty() && !ShadowDecorated()) |
299 | + if (!(deco_elements_ & cu::DecorationElement::SHADOW)) |
300 | + { |
301 | + if (!last_shadow_rect_.isEmpty()) |
302 | + last_shadow_rect_.setGeometry(0, 0, 0, 0); |
303 | + |
304 | return; |
305 | + } |
306 | |
307 | const auto* texture = ShadowTexture(); |
308 | |
309 | @@ -552,6 +605,12 @@ |
310 | |
311 | if (shadows_rect != last_shadow_rect_) |
312 | { |
313 | + auto const& win_region = win_->region(); |
314 | + quads[Quads::Pos::TOP_LEFT].region = CompRegion(quads[Quads::Pos::TOP_LEFT].box) - win_region; |
315 | + quads[Quads::Pos::TOP_RIGHT].region = CompRegion(quads[Quads::Pos::TOP_RIGHT].box) - win_region; |
316 | + quads[Quads::Pos::BOTTOM_LEFT].region = CompRegion(quads[Quads::Pos::BOTTOM_LEFT].box) - win_region; |
317 | + quads[Quads::Pos::BOTTOM_RIGHT].region = CompRegion(quads[Quads::Pos::BOTTOM_RIGHT].box) - win_region; |
318 | + |
319 | last_shadow_rect_ = shadows_rect; |
320 | win_->updateWindowOutputExtents(); |
321 | } |
322 | @@ -561,15 +620,26 @@ |
323 | GLWindowPaintAttrib const& attrib, |
324 | CompRegion const& region, unsigned mask) |
325 | { |
326 | + if (win_->defaultViewport() != screen->vp()) |
327 | + return; |
328 | + |
329 | if (dirty_geo_) |
330 | parent_->UpdateDecorationPosition(); |
331 | + |
332 | + if (dirty_frame_) |
333 | + { |
334 | + dirty_frame_ = false; |
335 | + CleanupWindowControls(); |
336 | + CleanupWindowEdges(); |
337 | + Update(); |
338 | + } |
339 | } |
340 | |
341 | void Window::Impl::Draw(GLMatrix const& transformation, |
342 | GLWindowPaintAttrib const& attrib, |
343 | CompRegion const& region, unsigned mask) |
344 | { |
345 | - if (last_shadow_rect_.isEmpty()) |
346 | + if (last_shadow_rect_.isEmpty() || win_->defaultViewport() != screen->vp()) |
347 | return; |
348 | |
349 | auto const& clip_region = (mask & PAINT_WINDOW_TRANSFORMED_MASK) ? infiniteRegion : region; |
350 | @@ -580,7 +650,7 @@ |
351 | for (unsigned i = 0; i < shadow_quads_.size(); ++i) |
352 | { |
353 | auto& quad = shadow_quads_[Quads::Pos(i)]; |
354 | - glwin_->glAddGeometry({quad.matrix}, CompRegion(quad.box) - win_->region(), clip_region); |
355 | + glwin_->glAddGeometry(quad.matrices, quad.region, clip_region); |
356 | } |
357 | |
358 | if (glwin_->vertexBuffer()->end()) |
359 | @@ -592,7 +662,7 @@ |
360 | continue; |
361 | |
362 | glwin_->vertexBuffer()->begin(); |
363 | - glwin_->glAddGeometry({dtex.quad.matrix}, dtex.quad.box, clip_region); |
364 | + glwin_->glAddGeometry(dtex.quad.matrices, dtex.quad.region, clip_region); |
365 | |
366 | if (glwin_->vertexBuffer()->end()) |
367 | glwin_->glDrawTexture(dtex, transformation, attrib, mask); |
368 | @@ -705,6 +775,9 @@ |
369 | |
370 | if (top_layout_) |
371 | top_layout_->scale = cv_->DPIScale(); |
372 | + |
373 | + if (edge_borders_) |
374 | + edge_borders_->scale = cv_->DPIScale(); |
375 | } |
376 | } |
377 | |
378 | @@ -787,6 +860,7 @@ |
379 | { |
380 | impl_->UpdateMonitor(); |
381 | impl_->ComputeShadowQuads(); |
382 | + impl_->UpdateWindowEdgesGeo(); |
383 | impl_->UpdateDecorationTextures(); |
384 | impl_->UpdateForceQuitDialogPosition(); |
385 | impl_->dirty_geo_ = false; |
386 | @@ -806,7 +880,8 @@ |
387 | { |
388 | data.add(impl_->win_->borderRect()) |
389 | .add("input_geo", impl_->win_->inputRect()) |
390 | - .add("content_geo", impl_->win_->region().boundingRect()) |
391 | + .add("content_geo", impl_->win_->geometry()) |
392 | + .add("region", impl_->win_->region().boundingRect()) |
393 | .add("title", title()) |
394 | .add("active", impl_->active()) |
395 | .add("scaled", scaled()) |
396 | |
397 | === modified file 'decorations/DecorationsEdgeBorders.cpp' |
398 | --- decorations/DecorationsEdgeBorders.cpp 2014-02-18 23:01:33 +0000 |
399 | +++ decorations/DecorationsEdgeBorders.cpp 2014-10-10 14:56:03 +0000 |
400 | @@ -26,21 +26,31 @@ |
401 | { |
402 | namespace |
403 | { |
404 | -const int MIN_CORNER_EDGE = 10; |
405 | +const RawPixel MIN_CORNER_EDGE = 10_em; |
406 | } |
407 | |
408 | EdgeBorders::EdgeBorders(CompWindow* win) |
409 | { |
410 | - items_.resize(size_t(Edge::Type::Size)); |
411 | - |
412 | - for (unsigned i = 0; i < unsigned(Edge::Type::Size); ++i) |
413 | - { |
414 | - auto type = Edge::Type(i); |
415 | - |
416 | - if (type == Edge::Type::GRAB) |
417 | - items_[i] = std::make_shared<GrabEdge>(win); |
418 | - else |
419 | - items_[i] = std::make_shared<Edge>(win, type); |
420 | + scale.changed.connect(sigc::hide(sigc::mem_fun(this, &EdgeBorders::Relayout))); |
421 | + |
422 | + if (win->actions() & CompWindowActionResizeMask) |
423 | + { |
424 | + items_.resize(size_t(Edge::Type::Size)); |
425 | + |
426 | + for (unsigned i = 0; i < unsigned(Edge::Type::Size); ++i) |
427 | + { |
428 | + auto type = Edge::Type(i); |
429 | + |
430 | + if (type == Edge::Type::GRAB) |
431 | + items_[i] = std::make_shared<GrabEdge>(win); |
432 | + else |
433 | + items_[i] = std::make_shared<Edge>(win, type); |
434 | + } |
435 | + } |
436 | + else /*if (win->actions() & CompWindowActionMoveMask)*/ |
437 | + { |
438 | + items_.resize(size_t(Edge::Type::GRAB) + 1); |
439 | + items_[unsigned(Edge::Type::GRAB)] = std::make_shared<GrabEdge>(win); |
440 | } |
441 | |
442 | Relayout(); |
443 | @@ -54,10 +64,17 @@ |
444 | auto const& ib = win->input(); |
445 | |
446 | using namespace compiz::window::extents; |
447 | - Extents edges(std::max(ib.left, MIN_CORNER_EDGE), |
448 | - std::max(ib.right, MIN_CORNER_EDGE), |
449 | - std::max(ib.top, MIN_CORNER_EDGE), |
450 | - std::max(ib.bottom, MIN_CORNER_EDGE)); |
451 | + int min_corner_edge = MIN_CORNER_EDGE.CP(scale); |
452 | + Extents edges(std::max(ib.left, min_corner_edge), |
453 | + std::max(ib.right, min_corner_edge), |
454 | + std::max(ib.top, min_corner_edge), |
455 | + std::max(ib.bottom, min_corner_edge)); |
456 | + |
457 | + grab_edge->SetCoords(rect_.x() + ib.left, rect_.y() + ib.top - b.top); |
458 | + grab_edge->SetSize(rect_.width() - ib.left - ib.right, b.top); |
459 | + |
460 | + if (items_.size() != size_t(Edge::Type::Size)) |
461 | + return; |
462 | |
463 | auto item = items_[unsigned(Edge::Type::TOP)]; |
464 | item->SetCoords(rect_.x() + edges.left, rect_.y()); |
465 | @@ -90,10 +107,6 @@ |
466 | item = items_[unsigned(Edge::Type::BOTTOM_RIGHT)]; |
467 | item->SetCoords(rect_.x2() - edges.right, rect_.y2() - edges.bottom); |
468 | item->SetSize(edges.right, edges.bottom); |
469 | - |
470 | - item = items_[unsigned(Edge::Type::GRAB)]; |
471 | - item->SetCoords(rect_.x() + ib.left, rect_.y() + ib.top - b.top); |
472 | - item->SetSize(rect_.width() - ib.left - ib.right, b.top); |
473 | } |
474 | |
475 | Item::Ptr const& EdgeBorders::GetEdge(Edge::Type type) const |
476 | |
477 | === modified file 'decorations/DecorationsForceQuitDialog.cpp' |
478 | --- decorations/DecorationsForceQuitDialog.cpp 2014-06-13 15:23:12 +0000 |
479 | +++ decorations/DecorationsForceQuitDialog.cpp 2014-10-10 14:56:03 +0000 |
480 | @@ -141,7 +141,7 @@ |
481 | |
482 | auto const& deco_style = decoration::Style::Get(); |
483 | auto const& offset = deco_style->ShadowOffset(); |
484 | - int max_offset = std::max(std::abs(offset.x), std::abs(offset.y)); |
485 | + int max_offset = std::max(std::abs(offset.x * 4), std::abs(offset.y * 4)); |
486 | gtk_container_set_border_width(GTK_CONTAINER(self), deco_style->ActiveShadowRadius()+max_offset); |
487 | |
488 | auto* screen = gtk_window_get_screen(self); |
489 | @@ -259,18 +259,32 @@ |
490 | auto const& radius = deco_style->CornerRadius(); |
491 | auto const& offset = deco_style->ShadowOffset(); |
492 | auto const& color = deco_style->ActiveShadowColor(); |
493 | + auto const& backcolor = deco_style->InactiveShadowColor(); |
494 | int decoration_radius = std::max({radius.top, radius.left, radius.right, radius.bottom}); |
495 | |
496 | - gtk_css_provider_load_from_data(style, (R"(SheetStyleDialog { |
497 | + gtk_css_provider_load_from_data(style, (R"( |
498 | + SheetStyleDialog { |
499 | background-color: #f7f6f5; |
500 | color: #4a4a4a; |
501 | border-radius: )"+std::to_string(decoration_radius)+R"(px; |
502 | - box-shadow: )"+std::to_string(offset.x)+"px "+std::to_string(offset.y)+"px "+ |
503 | + box-shadow: )"+std::to_string(2 * offset.x)+"px "+std::to_string(2 * offset.y)+"px "+ |
504 | std::to_string(deco_style->ActiveShadowRadius())+"px "+ |
505 | "rgba("+std::to_string(int(color.red * 255.0))+", "+ |
506 | std::to_string(int(color.green * 255.0))+", "+ |
507 | std::to_string(int(color.blue * 255.0))+", "+ |
508 | std::to_string(int(color.alpha))+'.'+std::to_string(int(color.alpha*10000.0))+')'+R"(; |
509 | + } |
510 | + |
511 | + SheetStyleDialog:backdrop { |
512 | + background-color: shade(#f7f6f5, 1.2); |
513 | + color: shade(#4a4a4a, 1.5); |
514 | + border-radius: )"+std::to_string(decoration_radius)+R"(px; |
515 | + box-shadow: )"+std::to_string(2 * offset.x)+"px "+std::to_string(2 * offset.y)+"px "+ |
516 | + std::to_string(deco_style->InactiveShadowRadius())+"px "+ |
517 | + "rgba("+std::to_string(int(backcolor.red * 255.0))+", "+ |
518 | + std::to_string(int(backcolor.green * 255.0))+", "+ |
519 | + std::to_string(int(backcolor.blue * 255.0))+", "+ |
520 | + std::to_string(int(backcolor.alpha))+'.'+std::to_string(int(backcolor.alpha*10000.0))+')'+R"(; |
521 | })").c_str(), -1, nullptr); |
522 | |
523 | auto* style_ctx = gtk_widget_get_style_context(self); |
524 | |
525 | === modified file 'decorations/DecorationsManager.cpp' |
526 | --- decorations/DecorationsManager.cpp 2014-07-30 00:49:35 +0000 |
527 | +++ decorations/DecorationsManager.cpp 2014-10-10 14:56:03 +0000 |
528 | @@ -41,8 +41,7 @@ |
529 | } |
530 | |
531 | Manager::Impl::Impl(decoration::Manager* parent, menu::Manager::Ptr const& menu) |
532 | - : active_window_(0) |
533 | - , enable_add_supported_atoms_(true) |
534 | + : enable_add_supported_atoms_(true) |
535 | , data_pool_(DataPool::Get()) |
536 | , menu_manager_(menu) |
537 | { |
538 | @@ -52,7 +51,6 @@ |
539 | Display* dpy = screen->dpy(); |
540 | atom::_NET_REQUEST_FRAME_EXTENTS = XInternAtom(dpy, "_NET_REQUEST_FRAME_EXTENTS", False); |
541 | atom::_NET_WM_VISIBLE_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", False); |
542 | - screen->updateSupportedWmHints(); |
543 | |
544 | auto rebuild_cb = sigc::mem_fun(this, &Impl::OnShadowOptionsChanged); |
545 | manager_->active_shadow_color.changed.connect(sigc::hide(sigc::bind(rebuild_cb, true))); |
546 | @@ -205,8 +203,6 @@ |
547 | |
548 | bool Manager::Impl::HandleEventBefore(XEvent* event) |
549 | { |
550 | - active_window_ = screen->activeWindow(); |
551 | - |
552 | switch (event->type) |
553 | { |
554 | case ClientMessage: |
555 | @@ -251,31 +247,26 @@ |
556 | |
557 | bool Manager::Impl::HandleEventAfter(XEvent* event) |
558 | { |
559 | - if (screen->activeWindow() != active_window_) |
560 | - { |
561 | - // Do this when _NET_ACTIVE_WINDOW changes on root! |
562 | - if (active_deco_win_) |
563 | - active_deco_win_->impl_->active = false; |
564 | - |
565 | - active_window_ = screen->activeWindow(); |
566 | - auto const& new_active = GetWindowByXid(active_window_); |
567 | - active_deco_win_ = new_active; |
568 | - |
569 | - if (new_active) |
570 | - new_active->impl_->active = true; |
571 | - } |
572 | - |
573 | switch (event->type) |
574 | { |
575 | case PropertyNotify: |
576 | { |
577 | - if (event->xproperty.atom == Atoms::mwmHints) |
578 | + if (event->xproperty.atom == Atoms::winActive) |
579 | + { |
580 | + if (active_deco_win_) |
581 | + active_deco_win_->impl_->active = false; |
582 | + |
583 | + auto const& new_active = GetWindowByXid(screen->activeWindow()); |
584 | + active_deco_win_ = new_active; |
585 | + |
586 | + if (new_active) |
587 | + new_active->impl_->active = true; |
588 | + } |
589 | + else if (event->xproperty.atom == Atoms::mwmHints || |
590 | + event->xproperty.atom == Atoms::wmAllowedActions) |
591 | { |
592 | if (Window::Ptr const& win = GetWindowByXid(event->xproperty.window)) |
593 | - { |
594 | - win->impl_->CleanupWindowControls(); |
595 | - win->Update(); |
596 | - } |
597 | + win->impl_->UpdateFrameActions(); |
598 | } |
599 | else if (event->xproperty.atom == XA_WM_NAME || |
600 | event->xproperty.atom == Atoms::wmName || |
601 | @@ -442,7 +433,7 @@ |
602 | .add("active_shadow_radius", active_shadow_radius()) |
603 | .add("inactive_shadow_color", inactive_shadow_color()) |
604 | .add("inactive_shadow_radius", inactive_shadow_radius()) |
605 | - .add("active_window", impl_->active_window_); |
606 | + .add("active_window", screen->activeWindow()); |
607 | } |
608 | |
609 | debug::Introspectable::IntrospectableList Manager::GetIntrospectableChildren() |
610 | |
611 | === modified file 'decorations/DecorationsPriv.h' |
612 | --- decorations/DecorationsPriv.h 2014-04-02 09:05:59 +0000 |
613 | +++ decorations/DecorationsPriv.h 2014-10-10 14:56:03 +0000 |
614 | @@ -1,6 +1,6 @@ |
615 | // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- |
616 | /* |
617 | - * Copyright (C) 2013 Canonical Ltd |
618 | + * Copyright (C) 2013-2014 Canonical Ltd |
619 | * |
620 | * This program is free software: you can redistribute it and/or modify |
621 | * it under the terms of the GNU General Public License version 3 as |
622 | @@ -92,11 +92,15 @@ |
623 | private: |
624 | void UnsetExtents(); |
625 | void SetupExtents(); |
626 | + void UpdateElements(); |
627 | void UpdateMonitor(); |
628 | void UpdateFrame(); |
629 | void CreateFrame(nux::Geometry const&); |
630 | void UpdateFrameGeo(nux::Geometry const&); |
631 | + void UpdateFrameActions(); |
632 | void UnsetFrame(); |
633 | + void SetupWindowEdges(); |
634 | + void CleanupWindowEdges(); |
635 | void SetupWindowControls(); |
636 | void CleanupWindowControls(); |
637 | void UnsetAppMenu(); |
638 | @@ -109,6 +113,7 @@ |
639 | |
640 | void ComputeShadowQuads(); |
641 | void UpdateDecorationTextures(); |
642 | + void UpdateWindowEdgesGeo(); |
643 | void UpdateForceQuitDialogPosition(); |
644 | void RenderDecorationTexture(Side, nux::Geometry const&); |
645 | void Paint(GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask); |
646 | @@ -122,8 +127,12 @@ |
647 | ::CompositeWindow* cwin_; |
648 | ::GLWindow* glwin_; |
649 | ::Window frame_; |
650 | + int monitor_; |
651 | bool dirty_geo_; |
652 | - int monitor_; |
653 | + bool dirty_frame_; |
654 | + unsigned deco_elements_; |
655 | + unsigned last_mwm_decor_; |
656 | + unsigned last_actions_; |
657 | |
658 | CompRect last_shadow_rect_; |
659 | Quads shadow_quads_; |
660 | @@ -176,7 +185,6 @@ |
661 | friend class Manager; |
662 | friend struct Window::Impl; |
663 | |
664 | - ::Window active_window_; |
665 | bool enable_add_supported_atoms_; |
666 | |
667 | DataPool::Ptr data_pool_; |
668 | |
669 | === modified file 'decorations/DecorationsTitle.cpp' |
670 | --- decorations/DecorationsTitle.cpp 2014-03-31 18:36:07 +0000 |
671 | +++ decorations/DecorationsTitle.cpp 2014-10-10 14:56:03 +0000 |
672 | @@ -80,6 +80,7 @@ |
673 | |
674 | Style::Get()->DrawTitle(text(), state, text_ctx, texture_size_.width / scale(), texture_size_.height / scale(), bg_geo * (1.0/scale)); |
675 | SetTexture(text_ctx); |
676 | + texture_.UpdateMatrix(); |
677 | } |
678 | |
679 | void Title::SetX(int x) |
680 | |
681 | === modified file 'decorations/DecorationsWidgets.cpp' |
682 | --- decorations/DecorationsWidgets.cpp 2014-02-27 07:10:31 +0000 |
683 | +++ decorations/DecorationsWidgets.cpp 2014-10-10 14:56:03 +0000 |
684 | @@ -226,6 +226,12 @@ |
685 | |
686 | // |
687 | |
688 | +TexturedItem::TexturedItem() |
689 | + : dirty_region_(false) |
690 | +{ |
691 | + geo_parameters_changed.connect([this] { dirty_region_ = true; }); |
692 | +} |
693 | + |
694 | void TexturedItem::SetTexture(cu::SimpleTexture::Ptr const& tex) |
695 | { |
696 | auto prev_geo = Geometry(); |
697 | @@ -254,8 +260,14 @@ |
698 | if (!visible || Geometry().isEmpty() || !texture_) |
699 | return; |
700 | |
701 | + if (dirty_region_) |
702 | + { |
703 | + texture_.quad.region = texture_.quad.box; |
704 | + dirty_region_ = false; |
705 | + } |
706 | + |
707 | ctx->vertexBuffer()->begin(); |
708 | - ctx->glAddGeometry({texture_.quad.matrix}, texture_.quad.box, clip); |
709 | + ctx->glAddGeometry(texture_.quad.matrices, texture_.quad.region, clip); |
710 | |
711 | if (ctx->vertexBuffer()->end()) |
712 | ctx->glDrawTexture(texture_, transformation, attrib, mask); |
713 | |
714 | === modified file 'decorations/DecorationsWidgets.h' |
715 | --- decorations/DecorationsWidgets.h 2014-02-27 07:10:31 +0000 |
716 | +++ decorations/DecorationsWidgets.h 2014-10-10 14:56:03 +0000 |
717 | @@ -121,6 +121,8 @@ |
718 | public: |
719 | typedef std::shared_ptr<TexturedItem> Ptr; |
720 | |
721 | + TexturedItem(); |
722 | + |
723 | void SetTexture(cu::SimpleTexture::Ptr const&); |
724 | void Draw(GLWindow*, GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask); |
725 | void SetCoords(int x, int y); |
726 | @@ -133,6 +135,9 @@ |
727 | |
728 | CompRect& InternalGeo(); |
729 | cu::SimpleTextureQuad texture_; |
730 | + |
731 | +private: |
732 | + bool dirty_region_; |
733 | }; |
734 | |
735 | |
736 | |
737 | === modified file 'plugins/unityshell/src/unityshell.cpp' |
738 | --- plugins/unityshell/src/unityshell.cpp 2014-09-04 22:12:01 +0000 |
739 | +++ plugins/unityshell/src/unityshell.cpp 2014-10-10 14:56:03 +0000 |
740 | @@ -281,6 +281,7 @@ |
741 | CompositeScreenInterface::setHandler(cScreen); |
742 | GLScreenInterface::setHandler(gScreen); |
743 | ScaleScreenInterface::setHandler(sScreen); |
744 | + screen->updateSupportedWmHints(); |
745 | |
746 | PluginAdapter::Initialize(screen); |
747 | AddChild(&WindowManager::Default()); |
748 | |
749 | === modified file 'unity-shared/CompizUtils.cpp' |
750 | --- unity-shared/CompizUtils.cpp 2014-04-14 13:51:58 +0000 |
751 | +++ unity-shared/CompizUtils.cpp 2014-10-10 14:56:03 +0000 |
752 | @@ -1,6 +1,6 @@ |
753 | // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- |
754 | /* |
755 | -* Copyright (C) 2013 Canonical Ltd |
756 | +* Copyright (C) 2013-2014 Canonical Ltd |
757 | * |
758 | * This program is free software: you can redistribute it and/or modify |
759 | * it under the terms of the GNU General Public License version 3 as |
760 | @@ -30,6 +30,11 @@ |
761 | { |
762 | const unsigned PIXMAP_DEPTH = 32; |
763 | const float DEFAULT_SCALE = 1.0f; |
764 | + const unsigned DECORABLE_WINDOW_TYPES = CompWindowTypeDialogMask | |
765 | + CompWindowTypeModalDialogMask | |
766 | + CompWindowTypeUtilMask | |
767 | + CompWindowTypeMenuMask | |
768 | + CompWindowTypeNormalMask; |
769 | } |
770 | |
771 | SimpleTexture::SimpleTexture(GLTexture::List const& tex) |
772 | @@ -39,7 +44,7 @@ |
773 | // |
774 | |
775 | SimpleTextureQuad::SimpleTextureQuad() |
776 | - : scale(DEFAULT_SCALE) |
777 | + : scale_(DEFAULT_SCALE) |
778 | {} |
779 | |
780 | bool SimpleTextureQuad::SetTexture(SimpleTexture::Ptr const& simple_texture) |
781 | @@ -52,24 +57,27 @@ |
782 | if (st && st->texture()) |
783 | { |
784 | auto* tex = st->texture(); |
785 | - CompPoint old_coords(quad.box.x(), quad.box.y()); |
786 | - short invalid = std::numeric_limits<short>::min(); |
787 | - quad.box.setGeometry(invalid, invalid, tex->width() * scale, tex->height() * scale); |
788 | - SetCoords(old_coords.x(), old_coords.y()); |
789 | + CompSize size(tex->width() * scale_, tex->height() * scale_); |
790 | + |
791 | + if (quad.box.width() != size.width() || quad.box.height() != size.height()) |
792 | + { |
793 | + quad.box.setSize(size); |
794 | + UpdateMatrix(); |
795 | + } |
796 | } |
797 | |
798 | return true; |
799 | } |
800 | |
801 | -bool SimpleTextureQuad::SetScale(float s) |
802 | +bool SimpleTextureQuad::SetScale(double s) |
803 | { |
804 | - if (!st || scale == s) |
805 | + if (!st || scale_ == s) |
806 | return false; |
807 | |
808 | - scale = s; |
809 | + scale_ = s; |
810 | auto* tex = st->texture(); |
811 | - quad.box.setWidth(tex->width() * scale); |
812 | - quad.box.setHeight(tex->height() * scale); |
813 | + quad.box.setWidth(tex->width() * scale_); |
814 | + quad.box.setHeight(tex->height() * scale_); |
815 | UpdateMatrix(); |
816 | return true; |
817 | } |
818 | @@ -91,8 +99,8 @@ |
819 | int y = quad.box.y(); |
820 | |
821 | quad.matrix = (st && st->texture()) ? st->texture()->matrix() : GLTexture::Matrix(); |
822 | - quad.matrix.xx /= scale; |
823 | - quad.matrix.yy /= scale; |
824 | + quad.matrix.xx /= scale_; |
825 | + quad.matrix.yy /= scale_; |
826 | quad.matrix.x0 = 0.0f - COMP_TEX_COORD_X(quad.matrix, x); |
827 | quad.matrix.y0 = 0.0f - COMP_TEX_COORD_Y(quad.matrix, y); |
828 | } |
829 | @@ -166,57 +174,68 @@ |
830 | return cairo_xlib_surface_get_height(surface_); |
831 | } |
832 | |
833 | -bool IsWindowShadowDecorable(CompWindow* win) |
834 | +// |
835 | +// |
836 | + |
837 | +unsigned WindowDecorationElements(CompWindow* win) |
838 | { |
839 | + unsigned elements = DecorationElement::NONE; |
840 | + |
841 | if (!win) |
842 | - return false; |
843 | + return elements; |
844 | |
845 | if (!win->isViewable()) |
846 | - return false; |
847 | + return elements; |
848 | |
849 | if (win->wmType() & (CompWindowTypeDockMask | CompWindowTypeDesktopMask)) |
850 | - return false; |
851 | - |
852 | - if (win->region().numRects() != 1) // Non rectangular windows |
853 | - return false; |
854 | - |
855 | - if (win->alpha()) |
856 | - return WindowHasMotifDecorations(win); |
857 | - |
858 | - return true; |
859 | + return elements; |
860 | + |
861 | + if (win->inShowDesktopMode()) |
862 | + return elements; |
863 | + |
864 | + auto const& region = win->region(); |
865 | + bool rectangular = (region.numRects() == 1); |
866 | + bool alpha = win->alpha(); |
867 | + |
868 | + if (!rectangular && alpha) // Non-rectangular windows with alpha channel |
869 | + return elements; |
870 | + |
871 | + if (region.boundingRect() != win->geometry()) // Shaped windows |
872 | + return elements; |
873 | + |
874 | + if (rectangular) |
875 | + elements |= DecorationElement::SHADOW; |
876 | + |
877 | + if (!win->overrideRedirect() && |
878 | + (win->type() & DECORABLE_WINDOW_TYPES) && |
879 | + (win->frame() || win->hasUnmapReference())) |
880 | + { |
881 | + if (win->actions() & CompWindowActionResizeMask) |
882 | + elements |= DecorationElement::EDGE; |
883 | + |
884 | + if (rectangular && (win->mwmDecor() & (MwmDecorAll | MwmDecorTitle))) |
885 | + elements |= DecorationElement::BORDER; |
886 | + } |
887 | + |
888 | + if (alpha && !(elements & DecorationElement::BORDER) && !(win->mwmDecor() & MwmDecorBorder)) |
889 | + elements &= ~DecorationElement::SHADOW; |
890 | + |
891 | + return elements; |
892 | +} |
893 | + |
894 | +bool IsWindowEdgeDecorable(CompWindow* win) |
895 | +{ |
896 | + return WindowDecorationElements(win) & DecorationElement::EDGE; |
897 | +} |
898 | + |
899 | +bool IsWindowShadowDecorable(CompWindow* win) |
900 | +{ |
901 | + return WindowDecorationElements(win) & DecorationElement::SHADOW; |
902 | } |
903 | |
904 | bool IsWindowFullyDecorable(CompWindow* win) |
905 | { |
906 | - if (!win) |
907 | - return false; |
908 | - |
909 | - if (!IsWindowShadowDecorable(win)) |
910 | - return false; |
911 | - |
912 | - return WindowHasMotifDecorations(win); |
913 | -} |
914 | - |
915 | -bool WindowHasMotifDecorations(CompWindow* win) |
916 | -{ |
917 | - if (!win) |
918 | - return false; |
919 | - |
920 | - if (win->overrideRedirect()) |
921 | - return false; |
922 | - |
923 | - switch (win->type()) |
924 | - { |
925 | - case CompWindowTypeDialogMask: |
926 | - case CompWindowTypeModalDialogMask: |
927 | - case CompWindowTypeUtilMask: |
928 | - case CompWindowTypeMenuMask: |
929 | - case CompWindowTypeNormalMask: |
930 | - if (win->mwmDecor() & (MwmDecorAll | MwmDecorTitle)) |
931 | - return true; |
932 | - } |
933 | - |
934 | - return false; |
935 | + return WindowDecorationElements(win) & DecorationElement::BORDER; |
936 | } |
937 | |
938 | } // compiz_utils namespace |
939 | |
940 | === modified file 'unity-shared/CompizUtils.h' |
941 | --- unity-shared/CompizUtils.h 2014-04-14 13:51:58 +0000 |
942 | +++ unity-shared/CompizUtils.h 2014-10-10 14:56:03 +0000 |
943 | @@ -31,8 +31,15 @@ |
944 | |
945 | struct TextureQuad |
946 | { |
947 | + TextureQuad() |
948 | + : matrices(1) |
949 | + , matrix(matrices[0]) |
950 | + {} |
951 | + |
952 | CompRect box; |
953 | - GLTexture::Matrix matrix; |
954 | + CompRegion region; |
955 | + GLTexture::MatrixList matrices; |
956 | + GLTexture::Matrix& matrix; |
957 | }; |
958 | |
959 | struct SimpleTexture |
960 | @@ -59,11 +66,13 @@ |
961 | { |
962 | SimpleTextureQuad(); |
963 | bool SetTexture(SimpleTexture::Ptr const&); |
964 | - bool SetScale(float scale); |
965 | + bool SetScale(double scale); |
966 | bool SetCoords(int x, int y); |
967 | bool SetX(int x); |
968 | bool SetY(int y); |
969 | |
970 | + void UpdateMatrix(); |
971 | + |
972 | operator SimpleTexture::Ptr() const { return st; } |
973 | operator bool() const { return st && st->texture(); } |
974 | operator GLTexture*() const { return st ? st->texture() : nullptr; } |
975 | @@ -73,8 +82,7 @@ |
976 | TextureQuad quad; |
977 | |
978 | private: |
979 | - void UpdateMatrix(); |
980 | - float scale; |
981 | + double scale_; |
982 | }; |
983 | |
984 | struct PixmapTexture : SimpleTexture |
985 | @@ -111,9 +119,20 @@ |
986 | cairo_t *cr_; |
987 | }; |
988 | |
989 | +enum DecorationElement |
990 | +{ |
991 | + NONE = 0, |
992 | + EDGE = (1 << 0), |
993 | + SHADOW = (1 << 1), |
994 | + BORDER = (1 << 2), |
995 | + FULL = EDGE|SHADOW|BORDER |
996 | +}; |
997 | + |
998 | +unsigned WindowDecorationElements(CompWindow*); |
999 | + |
1000 | +bool IsWindowEdgeDecorable(CompWindow*); |
1001 | bool IsWindowShadowDecorable(CompWindow*); |
1002 | bool IsWindowFullyDecorable(CompWindow*); |
1003 | -bool WindowHasMotifDecorations(CompWindow*); |
1004 | |
1005 | } // compiz_utils namespace |
1006 | } // unity namespace |
1007 | |
1008 | === modified file 'unity-shared/XWindowManager.cpp' |
1009 | --- unity-shared/XWindowManager.cpp 2014-02-14 03:05:02 +0000 |
1010 | +++ unity-shared/XWindowManager.cpp 2014-10-10 14:56:03 +0000 |
1011 | @@ -83,7 +83,7 @@ |
1012 | { |
1013 | LOG_ERROR(logger) << "Impossible to get the property " << gdk_x11_get_xatom_name(atom) |
1014 | << " for window " << window_id << ": invalid string type: " |
1015 | - << gdk_x11_get_xatom_name(Atoms::utf8String); |
1016 | + << gdk_x11_get_xatom_name(type); |
1017 | return std::string(); |
1018 | } |
1019 |
PASSED: Continuous integration, rev:3835 jenkins. qa.ubuntu. com/job/ unity-ci/ 1088/ jenkins. qa.ubuntu. com/job/ unity-utopic- amd64-ci/ 175 jenkins. qa.ubuntu. com/job/ unity-utopic- armhf-ci/ 175 jenkins. qa.ubuntu. com/job/ unity-utopic- i386-ci/ 175
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- ci/1088/ rebuild
http://