Merge lp:~alan-griffiths/miral/rework-titlebars into lp:miral

Proposed by Alan Griffiths on 2016-06-29
Status: Merged
Approved by: Alan Griffiths on 2016-07-08
Approved revision: 224
Merge reported by: Alan Griffiths
Merged at revision: not available
Proposed branch: lp:~alan-griffiths/miral/rework-titlebars
Merge into: lp:miral
Diff against target: 1059 lines (+461/-355)
13 files modified
include/miral/window_manager_tools.h (+0/-3)
miral-shell/CMakeLists.txt (+4/-4)
miral-shell/shell_main.cpp (+1/-1)
miral-shell/titlebar/CMakeLists.txt (+0/-13)
miral-shell/titlebar/titlebar_user_data.cpp (+0/-171)
miral-shell/titlebar/titlebar_user_data.h (+0/-41)
miral-shell/titlebar_provider.cpp (+276/-0)
miral-shell/titlebar_provider.h (+102/-0)
miral-shell/titlebar_window_manager.cpp (+57/-98)
miral-shell/titlebar_window_manager.h (+10/-1)
miral/application.cpp (+8/-1)
miral/basic_window_manager.cpp (+1/-17)
miral/basic_window_manager.h (+2/-5)
To merge this branch: bzr merge lp:~alan-griffiths/miral/rework-titlebars
Reviewer Review Type Date Requested Status
Alexandros Frantzis (community) 2016-06-29 Approve on 2016-07-08
Alan Griffiths Abstain on 2016-07-07
Review via email: mp+298637@code.launchpad.net

Commit Message

Re-implement dummy titlebars without using mirserver APIs

Description of the Change

Re-implement dummy titlebars without using mirserver APIs

This completes the separation of miral-shell from libmirserver and allows the removal of the awkward build_window() and destroy() methods from the WindowManagerTools interface.

To post a comment you must log in.
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve
Alan Griffiths (alan-griffiths) wrote :

I've found a broken usecase:

Running Xenial + recent build of lp:mir (I should update this and retest).

Run miral-shell on mesa.

Launch all the mir_demo_client_* together.

The titlebars do not get associated correctly with the apps:

1. They all appear grey.
2. The apps can be closed (Alt+F4), but titlebars remain.
3. When all apps can are closed Alt+F4 closes the server. (I guess a titlebar gets focus and the internal client PID is kill(SIGTERM)'d).

review: Needs Fixing
Alan Griffiths (alan-griffiths) wrote :

> I've found a broken usecase:
>
> Running Xenial + recent build of lp:mir (I should update this and retest).

Tested with current lp:mir tip - behaviour is less bad (E.g. there's a bright "focused" titlebar and focus is indicated correctly.)

But, Z-order is wrong as unrelated are sometimes placed between surface and titlebar resulting in the titlebar overlaying a surface above the main window.

And after closing all apps there can be an "orphan" titlebar that gets focus and kills the server on Alt+F4.

review: Needs Fixing
222. By Alan Griffiths on 2016-07-07

Avoid suicide

223. By Alan Griffiths on 2016-07-07

Don't overspecified function name

224. By Alan Griffiths on 2016-07-07

Ensure that titlebars are together with main surface in z-order

Alan Griffiths (alan-griffiths) wrote :

> > I've found a broken usecase:

fixed

review: Abstain
Alexandros Frantzis (afrantzis) wrote :

Still looks good (with mir 0.23.1 packages on xenial).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/miral/window_manager_tools.h'
2--- include/miral/window_manager_tools.h 2016-06-17 16:50:29 +0000
3+++ include/miral/window_manager_tools.h 2016-07-07 15:12:19 +0000
4@@ -49,8 +49,6 @@
5 * I.e. they should only be used by a thread that has called the WindowManagementPolicy methods
6 * (where any necessary locks are held) or via a invoke_under_lock() callback.
7 * @{ */
8- virtual auto build_window(Application const& application, WindowSpecification const& parameters)
9- -> WindowInfo& = 0;
10 virtual auto count_applications() const -> unsigned int = 0;
11 virtual void for_each_application(std::function<void(ApplicationInfo& info)> const& functor) = 0;
12 virtual auto find_application(std::function<bool(ApplicationInfo const& info)> const& predicate)
13@@ -66,7 +64,6 @@
14 virtual void focus_next_within_application() = 0;
15 virtual auto window_at(mir::geometry::Point cursor) const -> Window = 0;
16 virtual auto active_display() -> mir::geometry::Rectangle const = 0;
17- virtual void destroy(Window& window) = 0;
18 virtual void raise_tree(Window const& root) = 0;
19 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
20 virtual void place_and_size(WindowInfo& window_info, Point const& new_pos, Size const& new_size) = 0;
21
22=== modified file 'miral-shell/CMakeLists.txt'
23--- miral-shell/CMakeLists.txt 2016-06-17 16:50:29 +0000
24+++ miral-shell/CMakeLists.txt 2016-07-07 15:12:19 +0000
25@@ -1,6 +1,5 @@
26 add_subdirectory(spinner)
27 add_subdirectory(desktop)
28-add_subdirectory(titlebar)
29
30 add_custom_target(miral-run ALL
31 cp ${CMAKE_CURRENT_SOURCE_DIR}/miral-run.sh ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/miral-run
32@@ -12,12 +11,13 @@
33
34 add_executable(miral-shell
35 shell_main.cpp
36- tiling_window_manager.cpp tiling_window_manager.h
37- titlebar_window_manager.cpp titlebar_window_manager.h)
38+ tiling_window_manager.cpp tiling_window_manager.h
39+ titlebar_window_manager.cpp titlebar_window_manager.h
40+ titlebar_provider.cpp titlebar_provider.h
41+)
42
43 target_link_libraries(miral-shell
44 miral-spinner
45- miral-titlebar
46 miral
47 )
48
49
50=== modified file 'miral-shell/shell_main.cpp'
51--- miral-shell/shell_main.cpp 2016-06-17 16:50:29 +0000
52+++ miral-shell/shell_main.cpp 2016-07-07 15:12:19 +0000
53@@ -35,7 +35,7 @@
54 InternalClientLauncher launcher;
55 WindowManagerOptions window_managers
56 {
57- add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner),
58+ add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher),
59 add_window_manager_policy<CanonicalWindowManagerPolicy>("canonical"),
60 add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher),
61 };
62
63=== removed directory 'miral-shell/titlebar'
64=== removed file 'miral-shell/titlebar/CMakeLists.txt'
65--- miral-shell/titlebar/CMakeLists.txt 2016-06-07 09:24:55 +0000
66+++ miral-shell/titlebar/CMakeLists.txt 1970-01-01 00:00:00 +0000
67@@ -1,13 +0,0 @@
68-# TODO remove dependency on libmirserver-dev
69-# (Only needed by awkward painting code)
70-pkg_check_modules(MIRSERVER mirserver>=0.20 REQUIRED)
71-include_directories(include SYSTEM ${MIRSERVER_INCLUDE_DIRS})
72-
73-
74-add_library(miral-titlebar STATIC
75- titlebar_user_data.cpp titlebar_user_data.h
76-)
77-
78-target_link_libraries(miral-titlebar
79- miral
80-)
81
82=== removed file 'miral-shell/titlebar/titlebar_user_data.cpp'
83--- miral-shell/titlebar/titlebar_user_data.cpp 2016-06-07 19:05:10 +0000
84+++ miral-shell/titlebar/titlebar_user_data.cpp 1970-01-01 00:00:00 +0000
85@@ -1,171 +0,0 @@
86-/*
87- * Copyright © 2016 Canonical Ltd.
88- *
89- * This program is free software: you can redistribute it and/or modify it
90- * under the terms of the GNU General Public License version 3,
91- * as published by the Free Software Foundation.
92- *
93- * This program is distributed in the hope that it will be useful,
94- * but WITHOUT ANY WARRANTY; without even the implied warranty of
95- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
96- * GNU General Public License for more details.
97- *
98- * You should have received a copy of the GNU General Public License
99- * along with this program. If not, see <http://www.gnu.org/licenses/>.
100- *
101- * Authored by: Alan Griffiths <alan@octopull.co.uk>
102- */
103-
104-#include "titlebar_user_data.h"
105-
106-// TODO We need a better way to support painting stuff inside the window manager
107-#include <mir/frontend/buffer_stream.h>
108-#include <mir/graphics/buffer.h>
109-#include <mir/graphics/buffer_properties.h>
110-#include <mir/scene/session.h>
111-#include <mir/scene/surface.h>
112-
113-#include <mir/version.h>
114-
115-#include <atomic>
116-
117-using namespace mir::geometry;
118-
119-struct TitlebarUserData::StreamPainter
120-{
121- virtual void paint(int) = 0;
122- virtual ~StreamPainter() = default;
123- StreamPainter() = default;
124- StreamPainter(StreamPainter const&) = delete;
125- StreamPainter& operator=(StreamPainter const&) = delete;
126-};
127-
128-struct TitlebarUserData::SwappingPainter
129- : TitlebarUserData::StreamPainter
130-{
131- SwappingPainter(std::shared_ptr<mir::frontend::BufferStream> const& buffer_stream) :
132- buffer_stream{buffer_stream}, buffer{nullptr}
133- {
134- swap_buffers(nullptr);
135- }
136-
137- void swap_buffers(mir::graphics::Buffer* buf)
138- {
139- auto const callback = [this](mir::graphics::Buffer* new_buffer)
140- {
141- buffer.store(new_buffer);
142- };
143-
144- buffer_stream->swap_buffers(buf, callback);
145- }
146-
147- void paint(int intensity) override
148- {
149- if (auto const buf = buffer.exchange(nullptr))
150- {
151- auto const format = buffer_stream->pixel_format();
152- auto const sz = buf->size().height.as_int() *
153- buf->size().width.as_int() * MIR_BYTES_PER_PIXEL(format);
154- std::vector<unsigned char> pixels(sz, intensity);
155- buf->write(pixels.data(), sz);
156- swap_buffers(buf);
157- }
158- }
159-
160- std::shared_ptr<mir::frontend::BufferStream> const buffer_stream;
161- std::atomic<mir::graphics::Buffer*> buffer;
162-};
163-
164-struct TitlebarUserData::AllocatingPainter
165- : TitlebarUserData::StreamPainter
166-{
167- AllocatingPainter(
168- std::shared_ptr<mir::frontend::BufferStream> const& buffer_stream,
169-#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 24, 0)
170- std::shared_ptr<mir::scene::Session> const& session,
171-#endif
172- Size size) :
173- buffer_stream(buffer_stream),
174-#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 24, 0)
175- session(session),
176-#endif
177- properties({
178- size,
179- buffer_stream->pixel_format(),
180- mir::graphics::BufferUsage::software
181- }),
182-#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 24, 0)
183- front_buffer(session->create_buffer(properties)),
184- back_buffer(session->create_buffer(properties))
185-#else
186- front_buffer(buffer_stream->allocate_buffer(properties)),
187- back_buffer(buffer_stream->allocate_buffer(properties))
188-#endif
189- {
190- }
191-
192- void paint(int intensity) override
193- {
194-#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 24, 0)
195- auto buffer = session->get_buffer(back_buffer);
196-
197- auto const format = buffer->pixel_format();
198- auto const sz = buffer->size().height.as_int() *
199- buffer->size().width.as_int() * MIR_BYTES_PER_PIXEL(format);
200- std::vector<unsigned char> pixels(sz, intensity);
201- buffer->write(pixels.data(), sz);
202- buffer_stream->swap_buffers(buffer.get(), [](mir::graphics::Buffer*){});
203-#else
204- buffer_stream->with_buffer(back_buffer,
205- [this, intensity](mir::graphics::Buffer& buffer)
206- {
207- auto const format = buffer.pixel_format();
208- auto const sz = buffer.size().height.as_int() * buffer.size().width.as_int() * MIR_BYTES_PER_PIXEL(format);
209- std::vector<unsigned char> pixels(sz, intensity);
210- buffer.write(pixels.data(), sz);
211- buffer_stream->swap_buffers(&buffer, [](mir::graphics::Buffer*){});
212- });
213-#endif
214- std::swap(front_buffer, back_buffer);
215- }
216-
217- ~AllocatingPainter()
218- {
219-#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 24, 0)
220- session->destroy_buffer(front_buffer);
221- session->destroy_buffer(back_buffer);
222-#else
223- buffer_stream->remove_buffer(front_buffer);
224- buffer_stream->remove_buffer(back_buffer);
225-#endif
226- }
227-
228- std::shared_ptr<mir::frontend::BufferStream> const buffer_stream;
229- std::shared_ptr<mir::scene::Session> const session;
230- mir::graphics::BufferProperties properties;
231- mir::graphics::BufferID front_buffer;
232- mir::graphics::BufferID back_buffer;
233-};
234-
235-void TitlebarUserData::paint_titlebar(int intensity)
236-{
237- if (!stream_painter)
238- {
239- auto stream = std::shared_ptr<mir::scene::Surface>(window)->primary_buffer_stream();
240- try
241- {
242- stream_painter = std::make_shared<AllocatingPainter>(
243- stream,
244-#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 24, 0)
245- window.application(),
246-#endif
247- window.size());
248- }
249- catch (...)
250- {
251- stream_painter = std::make_shared<SwappingPainter>(stream);
252- }
253- }
254-
255- stream_painter->paint(intensity);
256-}
257
258=== removed file 'miral-shell/titlebar/titlebar_user_data.h'
259--- miral-shell/titlebar/titlebar_user_data.h 2016-06-07 09:24:55 +0000
260+++ miral-shell/titlebar/titlebar_user_data.h 1970-01-01 00:00:00 +0000
261@@ -1,41 +0,0 @@
262-/*
263- * Copyright © 2016 Canonical Ltd.
264- *
265- * This program is free software: you can redistribute it and/or modify it
266- * under the terms of the GNU General Public License version 3,
267- * as published by the Free Software Foundation.
268- *
269- * This program is distributed in the hope that it will be useful,
270- * but WITHOUT ANY WARRANTY; without even the implied warranty of
271- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
272- * GNU General Public License for more details.
273- *
274- * You should have received a copy of the GNU General Public License
275- * along with this program. If not, see <http://www.gnu.org/licenses/>.
276- *
277- * Authored by: Alan Griffiths <alan@octopull.co.uk>
278- */
279-
280-#ifndef MIRAL_SHELL_TITLEBAR_USER_DATA_H
281-#define MIRAL_SHELL_TITLEBAR_USER_DATA_H
282-
283-#include <miral/window.h>
284-
285-class TitlebarUserData
286-{
287-public:
288- TitlebarUserData(miral::Window window) : window{window} { }
289-
290- void paint_titlebar(int intensity);
291-
292- miral::Window window;
293-
294-private:
295- struct StreamPainter;
296- struct AllocatingPainter;
297- struct SwappingPainter;
298-
299- std::shared_ptr<StreamPainter> stream_painter;
300-};
301-
302-#endif //MIRAL_SHELL_TITLEBAR_USER_DATA_H
303
304=== added file 'miral-shell/titlebar_provider.cpp'
305--- miral-shell/titlebar_provider.cpp 1970-01-01 00:00:00 +0000
306+++ miral-shell/titlebar_provider.cpp 2016-07-07 15:12:19 +0000
307@@ -0,0 +1,276 @@
308+/*
309+ * Copyright © 2016 Canonical Ltd.
310+ *
311+ * This program is free software: you can redistribute it and/or modify it
312+ * under the terms of the GNU General Public License version 3,
313+ * as published by the Free Software Foundation.
314+ *
315+ * This program is distributed in the hope that it will be useful,
316+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
317+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
318+ * GNU General Public License for more details.
319+ *
320+ * You should have received a copy of the GNU General Public License
321+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
322+ *
323+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
324+ */
325+
326+#include "titlebar_provider.h"
327+
328+#include <miral/toolkit/surface_spec.h>
329+
330+#include <mir_toolkit/mir_buffer_stream.h>
331+
332+#include <cstring>
333+#include <sstream>
334+
335+namespace
336+{
337+int const title_bar_height = 10;
338+
339+void null_surface_callback(MirSurface*, void*) {}
340+
341+void paint_surface(MirSurface* surface, int const intensity)
342+{
343+ MirBufferStream* buffer_stream = mir_surface_get_buffer_stream(surface);
344+
345+ // TODO sometimes buffer_stream is nullptr - find out why (and fix).
346+ // (Only observed when creating a lot of clients at once)
347+ if (!buffer_stream)
348+ return;
349+
350+ MirGraphicsRegion region;
351+ mir_buffer_stream_get_graphics_region(buffer_stream, &region);
352+
353+ char* row = region.vaddr;
354+
355+ for (int j = 0; j != region.height; ++j)
356+ {
357+ memset(row, intensity, 4*region.width);
358+ row += region.stride;
359+ }
360+
361+ mir_buffer_stream_swap_buffers_sync(buffer_stream);
362+}
363+}
364+
365+using namespace miral::toolkit;
366+using namespace mir::geometry;
367+
368+TitlebarProvider::TitlebarProvider(miral::WindowManagerTools* const tools) : tools{tools}
369+{
370+
371+}
372+
373+TitlebarProvider::~TitlebarProvider()
374+{
375+ stop();
376+}
377+
378+void TitlebarProvider::stop()
379+{
380+ enqueue_work([this]
381+ {
382+ std::lock_guard<decltype(mutex)> lock{mutex};
383+ window_to_titlebar.clear();
384+ connection.reset();
385+ stop_work();
386+ });
387+}
388+
389+void TitlebarProvider::operator()(miral::toolkit::Connection connection)
390+{
391+ this->connection = connection;
392+ start_work();
393+}
394+
395+void TitlebarProvider::operator()(std::weak_ptr<mir::scene::Session> const& session)
396+{
397+ std::lock_guard<decltype(mutex)> lock{mutex};
398+ this->weak_session = session;
399+}
400+
401+auto TitlebarProvider::session() const -> std::shared_ptr<mir::scene::Session>
402+{
403+ std::lock_guard<decltype(mutex)> lock{mutex};
404+ return weak_session.lock();
405+}
406+
407+void TitlebarProvider::create_titlebar_for(miral::Window const& window)
408+{
409+ enqueue_work([this, window]
410+ {
411+ std::ostringstream buffer;
412+
413+ buffer << std::shared_ptr<mir::scene::Surface>(window).get();
414+
415+ auto const spec = SurfaceSpec::for_normal_surface(
416+ connection, window.size().width.as_int(), title_bar_height, mir_pixel_format_xrgb_8888)
417+ .set_buffer_usage(mir_buffer_usage_software)
418+ .set_type(mir_surface_type_gloss)
419+ .set_name(buffer.str().c_str());
420+
421+ std::lock_guard<decltype(mutex)> lock{mutex};
422+ spec.create_surface(insert, &window_to_titlebar[window]);
423+ });
424+}
425+
426+void TitlebarProvider::paint_titlebar_for(miral::Window const& window, int intensity)
427+{
428+ if (auto data = find_titlebar_data(window))
429+ {
430+ if (auto surface = data->titlebar.load())
431+ {
432+ enqueue_work([this, surface, intensity]{ paint_surface(surface, intensity); });
433+ }
434+ else
435+ {
436+ data->on_create = [this, intensity](MirSurface* surface)
437+ { enqueue_work([this, surface, intensity]{ paint_surface(surface, intensity); }); };
438+ }
439+ }
440+}
441+
442+void TitlebarProvider::destroy_titlebar_for(miral::Window const& window)
443+{
444+ enqueue_work([this, window]
445+ {
446+ std::lock_guard<decltype(mutex)> lock{mutex};
447+ window_to_titlebar.erase(window);
448+ });
449+}
450+
451+void TitlebarProvider::resize_titlebar_for(miral::Window const& window, Size const& size)
452+{
453+ if (window.size().width == size.width)
454+ return;
455+
456+ if (auto titlebar_window = find_titlebar_window(window))
457+ {
458+ titlebar_window.resize({size.width, title_bar_height});
459+ }
460+}
461+
462+void TitlebarProvider::advise_new_titlebar(miral::WindowInfo& window_info)
463+{
464+ std::istringstream buffer{window_info.name()};
465+
466+ void* parent = nullptr;
467+ buffer >> parent;
468+
469+ std::lock_guard<decltype(mutex)> lock{mutex};
470+
471+ for (auto& element : window_to_titlebar)
472+ {
473+ auto scene_surface = std::shared_ptr<mir::scene::Surface>(element.first);
474+ if (scene_surface.get() == parent)
475+ {
476+ auto window = window_info.window();
477+ element.second.window = window;
478+ auto& parent_info = tools->info_for(scene_surface);
479+ parent_info.add_child(window);
480+ window_info.parent(parent_info.window());
481+ window.move_to(parent_info.window().top_left() - Displacement{0, title_bar_height});
482+ break;
483+ }
484+ }
485+}
486+
487+void TitlebarProvider::advise_state_change(miral::WindowInfo const& window_info, MirSurfaceState state, Rectangle const& display_area)
488+{
489+ if (auto window = find_titlebar_window(window_info.window()))
490+ {
491+ switch (state)
492+ {
493+ case mir_surface_state_restored:
494+ window.resize({window_info.restore_rect().size.width, title_bar_height});
495+ window.show();
496+ break;
497+
498+ case mir_surface_state_maximized:
499+ case mir_surface_state_vertmaximized:
500+ case mir_surface_state_hidden:
501+ case mir_surface_state_minimized:
502+ window.hide();
503+ break;
504+
505+ case mir_surface_state_horizmaximized:
506+ window.resize({display_area.size.width, title_bar_height});
507+ window.show();
508+ break;
509+
510+ case mir_surface_state_fullscreen:
511+ default:
512+ break;
513+ }
514+ }
515+}
516+
517+TitlebarProvider::Data::~Data()
518+{
519+ if (auto const surface = titlebar.load())
520+ mir_surface_release(surface, &null_surface_callback, nullptr);
521+}
522+
523+void TitlebarProvider::insert(MirSurface* surface, Data* data)
524+{
525+ data->on_create(surface);
526+ data->titlebar = surface;
527+}
528+
529+TitlebarProvider::Data* TitlebarProvider::find_titlebar_data(miral::Window const& window)
530+{
531+ std::lock_guard<decltype(mutex)> lock{mutex};
532+
533+ auto const find = window_to_titlebar.find(window);
534+
535+ return (find != window_to_titlebar.end()) ? &find->second : nullptr;
536+}
537+
538+miral::Window TitlebarProvider::find_titlebar_window(miral::Window const& window) const
539+{
540+ std::lock_guard<decltype(mutex)> lock{mutex};
541+
542+ auto const find = window_to_titlebar.find(window);
543+
544+ return (find != window_to_titlebar.end()) ? find->second.window : miral::Window{};
545+}
546+
547+Worker::~Worker()
548+{
549+ if (worker.joinable()) worker.join();
550+}
551+
552+void Worker::do_work()
553+{
554+ while (!work_done)
555+ {
556+ WorkQueue::value_type work;
557+ {
558+ std::unique_lock<decltype(work_mutex)> lock{work_mutex};
559+ work_cv.wait(lock, [this] { return !work_queue.empty(); });
560+ work = work_queue.front();
561+ work_queue.pop();
562+ }
563+
564+ work();
565+ }
566+}
567+
568+void Worker::enqueue_work(std::function<void()> const& functor)
569+{
570+ std::lock_guard<decltype(work_mutex)> lock{work_mutex};
571+ work_queue.push(functor);
572+ work_cv.notify_one();
573+}
574+
575+void Worker::start_work()
576+{
577+ worker = std::thread{[this] { do_work(); }};
578+}
579+
580+void Worker::stop_work()
581+{
582+ enqueue_work([this] { work_done = true; });
583+}
584\ No newline at end of file
585
586=== added file 'miral-shell/titlebar_provider.h'
587--- miral-shell/titlebar_provider.h 1970-01-01 00:00:00 +0000
588+++ miral-shell/titlebar_provider.h 2016-07-07 15:12:19 +0000
589@@ -0,0 +1,102 @@
590+/*
591+ * Copyright © 2016 Canonical Ltd.
592+ *
593+ * This program is free software: you can redistribute it and/or modify it
594+ * under the terms of the GNU General Public License version 3,
595+ * as published by the Free Software Foundation.
596+ *
597+ * This program is distributed in the hope that it will be useful,
598+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
599+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
600+ * GNU General Public License for more details.
601+ *
602+ * You should have received a copy of the GNU General Public License
603+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
604+ *
605+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
606+ */
607+
608+#ifndef MIRAL_SHELL_TITLEBAR_PROVIDER_H
609+#define MIRAL_SHELL_TITLEBAR_PROVIDER_H
610+
611+
612+#include <miral/window_manager_tools.h>
613+
614+#include <mir/geometry/rectangle.h>
615+#include <mir_toolkit/client_types.h>
616+
617+#include <atomic>
618+#include <map>
619+#include <mutex>
620+#include <miral/toolkit/connection.h>
621+#include <thread>
622+#include <condition_variable>
623+#include <queue>
624+
625+class Worker
626+{
627+public:
628+ ~Worker();
629+
630+ void start_work();
631+ void enqueue_work(std::function<void()> const& functor);
632+ void stop_work();
633+
634+private:
635+ using WorkQueue = std::queue<std::function<void()>>;
636+
637+ std::mutex mutable work_mutex;
638+ std::condition_variable work_cv;
639+ WorkQueue work_queue;
640+ bool work_done = false;
641+ std::thread worker;
642+
643+ void do_work();
644+};
645+
646+class TitlebarProvider : Worker
647+{
648+public:
649+ TitlebarProvider(miral::WindowManagerTools* const tools);
650+ ~TitlebarProvider();
651+
652+ void operator()(miral::toolkit::Connection connection);
653+ void operator()(std::weak_ptr<mir::scene::Session> const& session);
654+
655+ auto session() const -> std::shared_ptr<mir::scene::Session>;
656+
657+ void create_titlebar_for(miral::Window const& window);
658+ void paint_titlebar_for(miral::Window const& window, int intensity);
659+ void destroy_titlebar_for(miral::Window const& window);
660+ void resize_titlebar_for(miral::Window const& window, mir::geometry::Size const& size);
661+ void advise_new_titlebar(miral::WindowInfo& window_info);
662+ void advise_state_change(miral::WindowInfo const& window_info, MirSurfaceState state, mir::geometry::Rectangle const& display_area);
663+
664+ void stop();
665+
666+private:
667+ struct Data
668+ {
669+ std::atomic<MirSurface*> titlebar{nullptr};
670+ std::function<void(MirSurface* surface)> on_create{[](MirSurface*){}};
671+ miral::Window window;
672+
673+ ~Data();
674+ };
675+
676+ using SurfaceMap = std::map<std::weak_ptr<mir::scene::Surface>, Data, std::owner_less<std::weak_ptr<mir::scene::Surface>>>;
677+
678+ miral::WindowManagerTools* const tools;
679+ std::mutex mutable mutex;
680+ miral::toolkit::Connection connection;
681+ std::weak_ptr<mir::scene::Session> weak_session;
682+
683+ SurfaceMap window_to_titlebar;
684+
685+ static void insert(MirSurface* surface, Data* data);
686+ Data* find_titlebar_data(miral::Window const& window);
687+ miral::Window find_titlebar_window(miral::Window const& window) const;
688+};
689+
690+
691+#endif //MIRAL_SHELL_TITLEBAR_PROVIDER_H
692
693=== modified file 'miral-shell/titlebar_window_manager.cpp'
694--- miral-shell/titlebar_window_manager.cpp 2016-06-17 16:50:29 +0000
695+++ miral-shell/titlebar_window_manager.cpp 2016-07-07 15:12:19 +0000
696@@ -17,38 +17,32 @@
697 */
698
699 #include "titlebar_window_manager.h"
700-#include "titlebar/titlebar_user_data.h"
701+#include "titlebar_provider.h"
702
703 #include <miral/application_info.h>
704+#include <miral/internal_client.h>
705 #include <miral/window_info.h>
706 #include <miral/window_manager_tools.h>
707
708+#include <linux/input.h>
709+
710 using namespace miral;
711
712-namespace
713-{
714-int const title_bar_height = 10;
715-Size titlebar_size_for_window(Size window_size)
716-{
717- return {window_size.width, Height{title_bar_height}};
718-}
719-
720-Point titlebar_position_for_window(Point window_position)
721-{
722- return {
723- window_position.x,
724- window_position.y - DeltaY(title_bar_height)
725- };
726-}
727-}
728-
729-TitlebarWindowManagerPolicy::TitlebarWindowManagerPolicy(WindowManagerTools* const tools, SpinnerSplash const& spinner) :
730+
731+TitlebarWindowManagerPolicy::TitlebarWindowManagerPolicy(
732+ WindowManagerTools* const tools,
733+ SpinnerSplash const& spinner,
734+ miral::InternalClientLauncher const& launcher) :
735 CanonicalWindowManagerPolicy(tools),
736 tools(tools),
737- spinner{spinner}
738+ spinner{spinner},
739+ titlebar_provider{std::make_unique<TitlebarProvider>(tools)}
740 {
741+ launcher.launch("decorations", *titlebar_provider);
742 }
743
744+TitlebarWindowManagerPolicy::~TitlebarWindowManagerPolicy() = default;
745+
746 bool TitlebarWindowManagerPolicy::handle_pointer_event(MirPointerEvent const* event)
747 {
748 auto consumes_event = CanonicalWindowManagerPolicy::handle_pointer_event(event);
749@@ -64,24 +58,14 @@
750 {
751 if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
752 {
753- // TODO this is a rather roundabout way to detect a titlebar
754 if (auto const possible_titlebar = tools->window_at(old_cursor))
755 {
756- if (auto const parent = tools->info_for(possible_titlebar).parent())
757+ if (possible_titlebar.application() == titlebar_provider->session())
758 {
759- if (auto const& parent_userdata =
760- std::static_pointer_cast<TitlebarUserData>(tools->info_for(parent).userdata()))
761- {
762- if (possible_titlebar == parent_userdata->window)
763- {
764- if (auto const target = tools->window_at(old_cursor))
765- {
766- tools->select_active_window(target);
767- tools->drag_active_window(cursor - old_cursor);
768- }
769- consumes_event = true;
770- }
771- }
772+ auto const& info = tools->info_for(possible_titlebar);
773+ tools->select_active_window(info.parent());
774+ tools->drag_active_window(cursor - old_cursor);
775+ consumes_event = true;
776 }
777 }
778 }
779@@ -96,47 +80,33 @@
780 {
781 CanonicalWindowManagerPolicy::advise_new_window(window_info);
782
783- if (!window_info.needs_titlebar(window_info.type()))
784- return;
785-
786- Window const& window = window_info.window();
787-
788- auto format = mir_pixel_format_xrgb_8888;
789- WindowSpecification params;
790- params.size() = titlebar_size_for_window(window.size());
791- params.name() = "decoration";
792- params.pixel_format() = format;
793- params.buffer_usage() = WindowSpecification::BufferUsage::software;
794- params.top_left() = titlebar_position_for_window(window.top_left());
795- params.type() = mir_surface_type_gloss;
796-
797- auto& titlebar_info = tools->build_window(window.application(), params);
798- titlebar_info.window().set_alpha(0.9);
799- titlebar_info.parent(window);
800-
801- auto data = std::make_shared<TitlebarUserData>(titlebar_info.window());
802- window_info.userdata(data);
803- window_info.add_child(titlebar_info.window());
804+ auto const application = window_info.window().application();
805+
806+ if (application == titlebar_provider->session())
807+ {
808+ titlebar_provider->advise_new_titlebar(window_info);
809+ tools->raise_tree(window_info.parent());
810+ return;
811+ }
812+
813+ if (application == spinner.session() || !window_info.needs_titlebar(window_info.type()))
814+ return;
815+
816+ titlebar_provider->create_titlebar_for(window_info.window());
817 }
818
819 void TitlebarWindowManagerPolicy::advise_focus_lost(WindowInfo const& info)
820 {
821 CanonicalWindowManagerPolicy::advise_focus_lost(info);
822
823- if (auto const titlebar = std::static_pointer_cast<TitlebarUserData>(info.userdata()))
824- {
825- titlebar->paint_titlebar(0x3F);
826- }
827+ titlebar_provider->paint_titlebar_for(info.window(), 0x3F);
828 }
829
830 void TitlebarWindowManagerPolicy::advise_focus_gained(WindowInfo const& info)
831 {
832 CanonicalWindowManagerPolicy::advise_focus_gained(info);
833
834- if (auto const titlebar = std::static_pointer_cast<TitlebarUserData>(info.userdata()))
835- {
836- titlebar->paint_titlebar(0xFF);
837- }
838+ titlebar_provider->paint_titlebar_for(info.window(), 0xFF);
839
840 // Frig to force the spinner to the top
841 if (auto const spinner_session = spinner.session())
842@@ -152,52 +122,21 @@
843 {
844 CanonicalWindowManagerPolicy::advise_state_change(window_info, state);
845
846- if (auto const titlebar = std::static_pointer_cast<TitlebarUserData>(window_info.userdata()))
847- {
848- switch (state)
849- {
850- case mir_surface_state_restored:
851- titlebar->window.resize(titlebar_size_for_window(window_info.restore_rect().size));
852- titlebar->window.show();
853- break;
854-
855- case mir_surface_state_maximized:
856- case mir_surface_state_vertmaximized:
857- case mir_surface_state_hidden:
858- case mir_surface_state_minimized:
859- titlebar->window.hide();
860- break;
861-
862- case mir_surface_state_horizmaximized:
863- titlebar->window.resize(titlebar_size_for_window({display_area.size.width, window_info.restore_rect().size.height}));
864- titlebar->window.show();
865- break;
866-
867- case mir_surface_state_fullscreen:
868- default:
869- break;
870- }
871- }
872+ titlebar_provider->advise_state_change(window_info, state, display_area);
873 }
874
875 void TitlebarWindowManagerPolicy::advise_resize(WindowInfo const& window_info, Size const& new_size)
876 {
877 CanonicalWindowManagerPolicy::advise_resize(window_info, new_size);
878
879- if (auto const titlebar = std::static_pointer_cast<TitlebarUserData>(window_info.userdata()))
880- {
881- titlebar->window.resize({new_size.width, Height{title_bar_height}});
882- }
883+ titlebar_provider->resize_titlebar_for(window_info.window(), new_size);
884 }
885
886 void TitlebarWindowManagerPolicy::advise_delete_window(WindowInfo const& window_info)
887 {
888 CanonicalWindowManagerPolicy::advise_delete_window(window_info);
889
890- if (auto const titlebar = std::static_pointer_cast<TitlebarUserData>(window_info.userdata()))
891- {
892- tools->destroy(titlebar->window);
893- }
894+ titlebar_provider->destroy_titlebar_for(window_info.window());
895 }
896
897 void TitlebarWindowManagerPolicy::handle_displays_updated(Rectangles const& displays)
898@@ -206,3 +145,23 @@
899
900 display_area = displays.bounding_rectangle();
901 }
902+
903+bool TitlebarWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
904+{
905+ if (miral::CanonicalWindowManagerPolicy::handle_keyboard_event(event))
906+ return true;
907+
908+ // TODO this is a workaround for the lack of a way to detect server exit (Mir bug lp:1593655)
909+ // We need to exit the titlebar_provider "client" thread before the server exits
910+ auto const action = mir_keyboard_event_action(event);
911+ auto const scan_code = mir_keyboard_event_scan_code(event);
912+ auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask;
913+
914+ if (action == mir_keyboard_action_down && scan_code == KEY_BACKSPACE &&
915+ (modifiers == (mir_input_event_modifier_alt | mir_input_event_modifier_ctrl)))
916+ {
917+ titlebar_provider->stop();
918+ }
919+
920+ return false;
921+}
922
923=== modified file 'miral-shell/titlebar_window_manager.h'
924--- miral-shell/titlebar_window_manager.h 2016-06-17 16:50:29 +0000
925+++ miral-shell/titlebar_window_manager.h 2016-07-07 15:12:19 +0000
926@@ -23,14 +23,20 @@
927
928 #include <miral/canonical_window_manager.h>
929
930+namespace miral { class InternalClientLauncher; }
931+
932 using namespace mir::geometry;
933
934+class TitlebarProvider;
935+
936 class TitlebarWindowManagerPolicy : public miral::CanonicalWindowManagerPolicy
937 {
938 public:
939- TitlebarWindowManagerPolicy(miral::WindowManagerTools* const tools, SpinnerSplash const& spinner);
940+ TitlebarWindowManagerPolicy(miral::WindowManagerTools* const tools, SpinnerSplash const& spinner, miral::InternalClientLauncher const& launcher);
941+ ~TitlebarWindowManagerPolicy();
942
943 bool handle_pointer_event(MirPointerEvent const* event) override;
944+ bool handle_keyboard_event(MirKeyboardEvent const* event) override;
945
946 void advise_new_window(miral::WindowInfo& window_info) override;
947 void advise_focus_lost(miral::WindowInfo const& info) override;
948@@ -40,10 +46,13 @@
949 void advise_delete_window(miral::WindowInfo const& window_info) override;
950
951 void handle_displays_updated(Rectangles const& displays) override;
952+
953 private:
954 miral::WindowManagerTools* const tools;
955 SpinnerSplash const spinner;
956
957+ std::unique_ptr<TitlebarProvider> const titlebar_provider;
958+
959 Rectangle display_area;
960 Point old_cursor{};
961 };
962
963=== modified file 'miral/application.cpp'
964--- miral/application.cpp 2016-04-22 09:24:24 +0000
965+++ miral/application.cpp 2016-07-07 15:12:19 +0000
966@@ -20,10 +20,17 @@
967
968 #include <mir/scene/session.h>
969
970+#include <unistd.h>
971+
972 #include <csignal>
973
974 void miral::kill(Application const& application, int sig)
975 {
976 if (application)
977- ::kill(application->process_id(), sig);
978+ {
979+ auto const pid = application->process_id();
980+
981+ if (pid != getpid())
982+ ::kill(pid, sig);
983+ }
984 }
985
986=== modified file 'miral/basic_window_manager.cpp'
987--- miral/basic_window_manager.cpp 2016-06-17 16:50:29 +0000
988+++ miral/basic_window_manager.cpp 2016-07-07 15:12:19 +0000
989@@ -69,15 +69,6 @@
990 {
991 auto spec = spec_;
992
993-#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 22, 0)
994- // Quick, dirty hack to support titlebar creation - really need an API for buffer stream creation
995- if (!spec.content_id().is_set() && !spec.streams().is_set())
996- {
997- mir::graphics::BufferProperties properties(spec.size().value(), spec.pixel_format().value(), mir::graphics::BufferUsage::software);
998- spec.content_id() = BufferStreamId{application->create_buffer_stream(properties).as_value()};
999- }
1000-#endif
1001-
1002 auto result = surface_builder(application, spec);
1003 auto& info = window_info.emplace(result, WindowInfo{result, spec}).first->second;
1004 if (spec.parent().is_set() && spec.parent().value().lock())
1005@@ -202,13 +193,6 @@
1006 }
1007 }
1008
1009-void miral::BasicWindowManager::destroy(Window& window)
1010-{
1011- erase(info_for(window));
1012-
1013- window.application()->destroy_surface(window);
1014-}
1015-
1016 void miral::BasicWindowManager::erase(miral::WindowInfo const& info)
1017 {
1018 if (auto const parent = info.parent())
1019@@ -901,7 +885,7 @@
1020 {
1021 // select_active_window() calls set_focus_to() which updates mru_active_windows and changes window
1022 auto const w = window;
1023- return w.application() != session || !(new_focus = miral::BasicWindowManager::select_active_window(w));
1024+ return w.application() != session || !(new_focus = select_active_window(w));
1025 });
1026
1027 return new_focus;
1028
1029=== modified file 'miral/basic_window_manager.h'
1030--- miral/basic_window_manager.h 2016-06-17 16:50:29 +0000
1031+++ miral/basic_window_manager.h 2016-07-07 15:12:19 +0000
1032@@ -55,9 +55,6 @@
1033 std::shared_ptr<mir::shell::DisplayLayout> const& display_layout,
1034 WindowManagementPolicyBuilder const& build);
1035
1036- auto build_window(Application const& application, WindowSpecification const& spec)
1037- -> WindowInfo& override;
1038-
1039 void add_session(std::shared_ptr<mir::scene::Session> const& session) override;
1040
1041 void remove_session(std::shared_ptr<mir::scene::Session> const& session) override;
1042@@ -77,8 +74,6 @@
1043 std::shared_ptr<mir::scene::Session> const& session,
1044 std::weak_ptr<mir::scene::Surface> const& surface) override;
1045
1046- void destroy(Window& window) override;
1047-
1048 void add_display(mir::geometry::Rectangle const& area) override;
1049
1050 void remove_display(mir::geometry::Rectangle const& area) override;
1051@@ -168,6 +163,8 @@
1052
1053 auto place_new_surface(ApplicationInfo const& app_info, WindowSpecification parameters)
1054 -> WindowSpecification;
1055+ auto build_window(Application const& application, WindowSpecification const& spec)
1056+ -> WindowInfo&;
1057 void move_tree(miral::WindowInfo& root, mir::geometry::Displacement movement);
1058 void erase(miral::WindowInfo const& info);
1059 };

Subscribers

People subscribed via source and target branches