Merge lp:~alan-griffiths/miral/mru-list-of-active-windows into lp:miral

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: 176
Merged at revision: 175
Proposed branch: lp:~alan-griffiths/miral/mru-list-of-active-windows
Merge into: lp:miral
Diff against target: 708 lines (+422/-68)
15 files modified
CMakeLists.txt (+7/-1)
building_and_using_miral.md (+7/-4)
cmake/FindGtestGmock.cmake (+71/-0)
include/miral/mru_window_list.h (+46/-0)
miral-kiosk/kiosk_window_manager.cpp (+0/-1)
miral-shell/canonical_window_manager.cpp (+45/-46)
miral-shell/canonical_window_manager.h (+4/-2)
miral-shell/spinner/CMakeLists.txt (+0/-2)
miral-shell/tiling_window_manager.cpp (+0/-1)
miral/CMakeLists.txt (+1/-0)
miral/basic_window_manager.cpp (+37/-11)
miral/basic_window_manager.h (+2/-0)
miral/mru_window_list.cpp (+44/-0)
test/CMakeLists.txt (+19/-0)
test/mru_window_list.cpp (+139/-0)
To merge this branch: bzr merge lp:~alan-griffiths/miral/mru-list-of-active-windows
Reviewer Review Type Date Requested Status
Mir development team Pending
Review via email: mp+295345@code.launchpad.net

Commit message

Track the active window in an MRU list and use that to select the new active Window when the old one closes

Description of the change

Introduces the first unit test case to MirAL, and starts the rework of selecting the active Window.

The main behavioural difference here is that BasicWindowManager tracks the active window in an MRU list and uses that to select the new active Window when the old one closes.

There's still too much logic in the "policy" that needs moving to the BasicWindowManager, but that can come in a follow-up.

To post a comment you must log in.
174. By Daniel d'Andrada

Fix build failure

175. By Alan Griffiths

merge lp:~alan-griffiths/miral/mru-list-of-active-windows

176. By Alan Griffiths

std::rbegin()/rend() not available on vivid

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-04-25 11:59:43 +0000
3+++ CMakeLists.txt 2016-05-22 15:01:57 +0000
4@@ -14,7 +14,7 @@
5 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
6
7 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -g -Werror -Wall -pedantic -Wextra -fPIC -fuse-ld=gold")
8-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -g -std=c++14 -Werror -Wall -fno-strict-aliasing -pedantic -Wnon-virtual-dtor -Wextra -fPIC -fuse-ld=gold")
9+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -g -std=c++14 -Werror -Wall -pedantic -Wnon-virtual-dtor -Wextra -fPIC -fuse-ld=gold")
10 set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs -fuse-ld=gold")
11 set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,defs -fuse-ld=gold")
12 set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,defs -fuse-ld=gold")
13@@ -34,3 +34,9 @@
14 add_subdirectory(miral)
15 add_subdirectory(miral-shell)
16 add_subdirectory(miral-kiosk)
17+
18+# building the tests is disabled by default because the mirtest-dev package is broken on xenial (lp:1583536)
19+option(MIRAL_ENABLE_TESTS "Build tests" OFF)
20+if (MIRAL_ENABLE_TESTS)
21+add_subdirectory(test)
22+endif()
23
24=== modified file 'building_and_using_miral.md'
25--- building_and_using_miral.md 2016-04-13 13:46:35 +0000
26+++ building_and_using_miral.md 2016-05-22 15:01:57 +0000
27@@ -5,12 +5,15 @@
28 earlier Ubuntu versions or other distributions.
29
30 You’ll need a few development and utility packages installed, along with the
31-Mir development packages (if you’re working on a phone or tablet use
32-mir-graphics-drivers-android in place of mir-graphics-drivers-desktop):
33+Mir development packages:
34
35 $ sudo apt-get install cmake g++ make bzr python-pil
36- $ sudo apt-get install mir-graphics-drivers-desktop libmirserver-dev libmirclient-dev
37-
38+ $ sudo apt-get install libmirserver-dev libmirclient-dev mirtest-dev
39+ $ sudo apt-get install mir-graphics-drivers-desktop
40+
41+(If you’re working on a phone or tablet use mir-graphics-drivers-android in
42+place of mir-graphics-drivers-desktop.)
43+
44 With these installed you can checkout and build miral:
45
46 $ bzr branch lp:miral
47
48=== added file 'cmake/FindGtestGmock.cmake'
49--- cmake/FindGtestGmock.cmake 1970-01-01 00:00:00 +0000
50+++ cmake/FindGtestGmock.cmake 2016-05-22 15:01:57 +0000
51@@ -0,0 +1,71 @@
52+include(ExternalProject)
53+include(FindPackageHandleStandardArgs)
54+
55+#gtest
56+set(GTEST_INSTALL_DIR /usr/src/gmock/gtest/include)
57+find_path(GTEST_INCLUDE_DIR gtest/gtest.h
58+ HINTS ${GTEST_INSTALL_DIR})
59+
60+#gmock
61+find_path(GMOCK_INSTALL_DIR gmock/CMakeLists.txt
62+ HINTS /usr/src)
63+if(${GMOCK_INSTALL_DIR} STREQUAL "GMOCK_INSTALL_DIR-NOTFOUND")
64+ message(FATAL_ERROR "google-mock package not found")
65+endif()
66+
67+set(GMOCK_INSTALL_DIR ${GMOCK_INSTALL_DIR}/gmock)
68+find_path(GMOCK_INCLUDE_DIR gmock/gmock.h)
69+
70+set(GMOCK_PREFIX gmock)
71+set(GMOCK_BINARY_DIR ${CMAKE_BINARY_DIR}/${GMOCK_PREFIX}/libs)
72+set(GTEST_BINARY_DIR ${GMOCK_BINARY_DIR}/gtest)
73+
74+set(GTEST_CXX_FLAGS "-fPIC -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64")
75+if (cmake_build_type_lower MATCHES "threadsanitizer")
76+ set(GTEST_CXX_FLAGS "${GTEST_CXX_FLAGS} -fsanitize=thread")
77+elseif (cmake_build_type_lower MATCHES "ubsanitizer")
78+ set(GTEST_CXX_FLAGS "${GTEST_CXX_FLAGS} -fsanitize=undefined")
79+endif()
80+
81+set(GTEST_CMAKE_ARGS "-DCMAKE_CXX_FLAGS=${GTEST_CXX_FLAGS}")
82+list(APPEND GTEST_CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER})
83+list(APPEND GTEST_CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER})
84+if (cmake_build_type_lower MATCHES "threadsanitizer")
85+ #Skip compiler check, since if GCC is the compiler, we need to link against -ltsan
86+ #explicitly; specifying additional linker flags doesn't seem possible for external projects
87+ list(APPEND GTEST_CMAKE_ARGS -DCMAKE_CXX_COMPILER_WORKS=1)
88+endif()
89+if (${CMAKE_CROSSCOMPILING})
90+ if(DEFINED MIR_NDK_PATH)
91+ list(APPEND GTEST_CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_MODULE_PATH}/LinuxCrossCompile.cmake)
92+ else()
93+ list(APPEND GTEST_CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER})
94+ list(APPEND GTEST_CMAKE_ARGS -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER})
95+ endif()
96+endif()
97+
98+ExternalProject_Add(
99+ GMock
100+ #where to build in source tree
101+ PREFIX ${GMOCK_PREFIX}
102+ #where the source is external to the project
103+ SOURCE_DIR ${GMOCK_INSTALL_DIR}
104+ #forward the compilers to the subproject so cross-arch builds work
105+ CMAKE_ARGS ${GTEST_CMAKE_ARGS}
106+ BINARY_DIR ${GMOCK_BINARY_DIR}
107+
108+ #we don't need to install, so skip
109+ INSTALL_COMMAND ""
110+)
111+
112+set(GMOCK_LIBRARY ${GMOCK_BINARY_DIR}/libgmock.a)
113+set(GMOCK_MAIN_LIBRARY ${GMOCK_BINARY_DIR}/libgmock_main.a)
114+set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY})
115+set(GTEST_LIBRARY ${GTEST_BINARY_DIR}/libgtest.a)
116+set(GTEST_MAIN_LIBRARY ${GTEST_BINARY_DIR}/libgtest_main.a)
117+set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY})
118+set(GTEST_ALL_LIBRARIES ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES})
119+
120+find_package_handle_standard_args(GTest DEFAULT_MSG
121+ GMOCK_INCLUDE_DIR
122+ GTEST_INCLUDE_DIR)
123
124=== added file 'include/miral/mru_window_list.h'
125--- include/miral/mru_window_list.h 1970-01-01 00:00:00 +0000
126+++ include/miral/mru_window_list.h 2016-05-22 15:01:57 +0000
127@@ -0,0 +1,46 @@
128+/*
129+ * Copyright © 2016 Canonical Ltd.
130+ *
131+ * This program is free software: you can redistribute it and/or modify it
132+ * under the terms of the GNU General Public License version 3,
133+ * as published by the Free Software Foundation.
134+ *
135+ * This program is distributed in the hope that it will be useful,
136+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
137+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
138+ * GNU General Public License for more details.
139+ *
140+ * You should have received a copy of the GNU General Public License
141+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
142+ *
143+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
144+ */
145+
146+#ifndef MIRAL_MRU_WINDOW_LIST_H
147+#define MIRAL_MRU_WINDOW_LIST_H
148+
149+#include <miral/window.h>
150+
151+#include <functional>
152+#include <vector>
153+
154+namespace miral
155+{
156+class MRUWindowList
157+{
158+public:
159+
160+ void push(Window const& window);
161+ void erase(Window const& window);
162+ auto top() const -> Window;
163+
164+ using Enumerator = std::function<bool(Window& window)>;
165+
166+ void enumerate(Enumerator const& enumerator) const;
167+
168+private:
169+ std::vector<Window> surfaces;
170+};
171+}
172+
173+#endif //MIRAL_MRU_WINDOW_LIST_H
174
175=== modified file 'miral-kiosk/kiosk_window_manager.cpp'
176--- miral-kiosk/kiosk_window_manager.cpp 2016-05-18 11:49:26 +0000
177+++ miral-kiosk/kiosk_window_manager.cpp 2016-05-22 15:01:57 +0000
178@@ -161,7 +161,6 @@
179 scan_code == KEY_TAB)
180 {
181 tools->focus_next_application();
182- select_active_window(tools->focused_window());
183
184 return true;
185 }
186
187=== modified file 'miral-shell/canonical_window_manager.cpp'
188--- miral-shell/canonical_window_manager.cpp 2016-05-20 21:08:57 +0000
189+++ miral-shell/canonical_window_manager.cpp 2016-05-22 15:01:57 +0000
190@@ -20,9 +20,9 @@
191 #include "titlebar/canonical_window_management_policy_data.h"
192 #include "spinner/splash.h"
193
194-#include "miral/application_info.h"
195-#include "miral/window_info.h"
196-#include "miral/window_manager_tools.h"
197+#include <miral/application_info.h>
198+#include <miral/window_info.h>
199+#include <miral/window_manager_tools.h>
200
201 #include <linux/input.h>
202 #include <algorithm>
203@@ -565,8 +565,6 @@
204 scan_code == KEY_TAB)
205 {
206 tools->focus_next_application();
207- if (auto const window = tools->focused_window())
208- select_active_window(window);
209
210 return true;
211 }
212@@ -717,69 +715,70 @@
213
214 auto CanonicalWindowManagerPolicy::select_active_window(Window const& hint) -> miral::Window
215 {
216- if (hint == active_window_)
217- return hint ;
218+ auto const prev_window = active_window();
219+
220+ if (hint == prev_window)
221+ return hint;
222
223 if (!hint)
224 {
225- if (auto const active_surface = active_window_)
226+ if (prev_window)
227 {
228- auto& info = tools->info_for(active_surface);
229- if (auto const titlebar = std::static_pointer_cast<CanonicalWindowManagementPolicyData>(info.userdata()))
230- {
231- titlebar->paint_titlebar(0x3F);
232- }
233- }
234-
235- if (active_window_)
236 tools->set_focus_to({});
237+ handle_focus_lost(tools->info_for(prev_window));
238+ }
239
240- active_window_.reset();
241 return hint;
242 }
243
244- auto const& info_for = tools->info_for(hint);
245+ auto const& info_for_hint = tools->info_for(hint);
246
247- if (info_for.can_be_active())
248+ if (info_for_hint.can_be_active())
249 {
250- if (auto const active_surface = active_window_)
251- {
252- auto& info = tools->info_for(active_surface);
253- if (auto const titlebar = std::static_pointer_cast<CanonicalWindowManagementPolicyData>(info.userdata()))
254- {
255- titlebar->paint_titlebar(0x3F);
256- }
257- }
258- auto& info = tools->info_for(hint);
259- if (auto const titlebar = std::static_pointer_cast<CanonicalWindowManagementPolicyData>(info.userdata()))
260- {
261- titlebar->paint_titlebar(0xFF);
262- }
263- tools->set_focus_to(info_for.window());
264- tools->raise_tree(info_for.window());
265- active_window_ = info_for.window();
266-
267- // Frig to force the spinner to the top
268- if (auto const spinner_session = spinner.session())
269- {
270- auto const& spinner_info = tools->info_for(spinner_session);
271-
272- if (spinner_info.windows().size() > 0)
273- tools->raise_tree(spinner_info.windows()[0]);
274- }
275-
276+ tools->set_focus_to(hint);
277+ tools->raise_tree(hint);
278+
279+ if (prev_window)
280+ handle_focus_lost(tools->info_for(prev_window));
281+
282+ handle_focus_gained(info_for_hint);
283 return hint;
284 }
285 else
286 {
287 // Cannot have input focus - try the parent
288- if (auto const parent = info_for.parent())
289+ if (auto const parent = info_for_hint.parent())
290 return select_active_window(parent);
291 }
292
293 return {};
294 }
295
296+void CanonicalWindowManagerPolicy::handle_focus_gained(WindowInfo const& info)
297+{
298+ if (auto const titlebar = std::static_pointer_cast<CanonicalWindowManagementPolicyData>(info.userdata()))
299+ titlebar->paint_titlebar(0xFF);
300+
301+ active_window_ = info.window();
302+
303+ // Frig to force the spinner to the top
304+ if (auto const spinner_session = spinner.session())
305+ {
306+ auto const& spinner_info = tools->info_for(spinner_session);
307+
308+ if (spinner_info.windows().size() > 0)
309+ tools->raise_tree(spinner_info.windows()[0]);
310+ }
311+}
312+
313+void CanonicalWindowManagerPolicy::handle_focus_lost(WindowInfo const& info)
314+{
315+ if (auto const titlebar = std::static_pointer_cast<CanonicalWindowManagementPolicyData>(info.userdata()))
316+ titlebar->paint_titlebar(0x3F);
317+
318+ active_window_.reset();
319+}
320+
321 auto CanonicalWindowManagerPolicy::active_window() const
322 -> Window
323 {
324
325=== modified file 'miral-shell/canonical_window_manager.h'
326--- miral-shell/canonical_window_manager.h 2016-05-06 14:38:26 +0000
327+++ miral-shell/canonical_window_manager.h 2016-05-22 15:01:57 +0000
328@@ -21,8 +21,8 @@
329
330 #include "spinner/splash.h"
331
332-#include "miral/window.h"
333-#include "miral/window_management_policy.h"
334+#include <miral/window.h>
335+#include <miral/window_management_policy.h>
336
337 #include <mir/geometry/displacement.h>
338
339@@ -90,6 +90,8 @@
340 void toggle(MirSurfaceState state);
341
342 auto active_window() const -> miral::Window;
343+ void handle_focus_lost(miral::WindowInfo const& info);
344+ void handle_focus_gained(miral::WindowInfo const& info);
345
346 bool resize(miral::Window const& window, Point cursor, Point old_cursor);
347 bool drag(miral::Window window, Point to, Point from, Rectangle bounds);
348
349=== modified file 'miral-shell/spinner/CMakeLists.txt'
350--- miral-shell/spinner/CMakeLists.txt 2016-04-15 17:16:53 +0000
351+++ miral-shell/spinner/CMakeLists.txt 2016-05-22 15:01:57 +0000
352@@ -47,8 +47,6 @@
353 ${CMAKE_CURRENT_BINARY_DIR}
354 )
355
356-link_directories(${MIRCLIENT_LIBRARY_DIRS})
357-
358 add_library(miral-spinner STATIC
359 eglapp.cpp
360 eglapp.h
361
362=== modified file 'miral-shell/tiling_window_manager.cpp'
363--- miral-shell/tiling_window_manager.cpp 2016-05-18 11:51:27 +0000
364+++ miral-shell/tiling_window_manager.cpp 2016-05-22 15:01:57 +0000
365@@ -304,7 +304,6 @@
366 scan_code == KEY_TAB)
367 {
368 tools->focus_next_application();
369- select_active_window(tools->focused_window());
370
371 return true;
372 }
373
374=== modified file 'miral/CMakeLists.txt'
375--- miral/CMakeLists.txt 2016-05-18 11:49:26 +0000
376+++ miral/CMakeLists.txt 2016-05-22 15:01:57 +0000
377@@ -13,6 +13,7 @@
378 window_management_options.cpp ${CMAKE_SOURCE_DIR}/include/miral/window_management_options.h
379 window_specification.cpp ${CMAKE_SOURCE_DIR}/include/miral/window_specification.h
380 startup_internal_client.cpp ${CMAKE_SOURCE_DIR}/include/miral/startup_internal_client.h
381+ mru_window_list.cpp ${CMAKE_SOURCE_DIR}/include/miral/mru_window_list.h
382 basic_window_manager.cpp basic_window_manager.h
383 ${CMAKE_SOURCE_DIR}/include/miral/window_management_policy.h
384 ${CMAKE_SOURCE_DIR}/include/miral/window_manager_tools.h
385
386=== modified file 'miral/basic_window_manager.cpp'
387--- miral/basic_window_manager.cpp 2016-05-18 11:49:26 +0000
388+++ miral/basic_window_manager.cpp 2016-05-22 15:01:57 +0000
389@@ -127,16 +127,17 @@
390 std::weak_ptr<scene::Surface> const& surface)
391 {
392 std::lock_guard<decltype(mutex)> lock(mutex);
393- bool const is_active_window{surface.lock() == focus_controller->focused_surface()};
394-
395 auto& info = info_for(surface);
396
397+ bool const is_active_window{mru_active_windows.top() == info.window()};
398+
399 if (auto const parent = info.parent())
400 info_for(parent).remove_child(info.window());
401
402 auto& session_info = info_for(session);
403
404 session_info.remove_window(info.window());
405+ mru_active_windows.erase(info.window());
406
407 policy->handle_delete_window(info);
408
409@@ -156,16 +157,39 @@
410 return;
411 }
412
413- // Ought to find top window of same application, but we don't
414- // have the API (yet), so find any suitable top-level-window
415- for (auto const& tlw : session_info.windows())
416- {
417- if (policy->select_active_window(tlw))
418- return;
419- }
420-
421+ // TODO the policy for choosing a window is mixed with implementing the choice.
422+ // I.e. select_active_window() calls set_focus_to() which updates mru_active_windows
423+ // during the iteration. There must be a better way to sequence the logic.
424+ // Until then we copy the list.
425+ auto const copy_mru_windows = mru_active_windows;
426+
427+ // Try to activate to recently active window of same application
428+ {
429+ Window new_focus;
430+
431+ copy_mru_windows.enumerate([&](Window& window)
432+ {
433+ return window.application() != session ||
434+ !(new_focus = policy->select_active_window(window));
435+ });
436+
437+ if (new_focus) return;
438+ }
439+
440+ // Try to activate to recently active window of any application
441+ {
442+ Window new_focus;
443+
444+ copy_mru_windows.enumerate([&](Window& window)
445+ {
446+ return !(new_focus = policy->select_active_window(window));
447+ });
448+
449+ if (new_focus) return;
450+ }
451+
452+ // Fallback to cycling through applications
453 focus_next_application();
454- policy->select_active_window(focused_window());
455 }
456 }
457
458@@ -307,10 +331,12 @@
459 void miral::BasicWindowManager::focus_next_application()
460 {
461 focus_controller->focus_next_session();
462+ policy->select_active_window(focused_window());
463 }
464
465 void miral::BasicWindowManager::set_focus_to(Window const& window)
466 {
467+ if (window) mru_active_windows.push(window);
468 focus_controller->set_focus_to(window.application(), window);
469 }
470
471
472=== modified file 'miral/basic_window_manager.h'
473--- miral/basic_window_manager.h 2016-05-06 16:34:33 +0000
474+++ miral/basic_window_manager.h 2016-05-22 15:01:57 +0000
475@@ -24,6 +24,7 @@
476 #include "miral/window_info.h"
477 #include "miral/application.h"
478 #include "miral/application_info.h"
479+#include "miral/mru_window_list.h"
480
481 #include "mir/geometry/rectangles.h"
482 #include "mir/shell/abstract_shell.h"
483@@ -147,6 +148,7 @@
484 mir::geometry::Rectangles displays;
485 mir::geometry::Point cursor;
486 uint64_t last_input_event_timestamp{0};
487+ miral::MRUWindowList mru_active_windows;
488
489 // Cache the builder functor for the convenience of policies - this should become unnecessary
490 std::function<Window(std::shared_ptr<mir::scene::Session> const& session, WindowSpecification const& params)> surface_builder;
491
492=== added file 'miral/mru_window_list.cpp'
493--- miral/mru_window_list.cpp 1970-01-01 00:00:00 +0000
494+++ miral/mru_window_list.cpp 2016-05-22 15:01:57 +0000
495@@ -0,0 +1,44 @@
496+/*
497+ * Copyright © 2016 Canonical Ltd.
498+ *
499+ * This program is free software: you can redistribute it and/or modify it
500+ * under the terms of the GNU General Public License version 3,
501+ * as published by the Free Software Foundation.
502+ *
503+ * This program is distributed in the hope that it will be useful,
504+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
505+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
506+ * GNU General Public License for more details.
507+ *
508+ * You should have received a copy of the GNU General Public License
509+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
510+ *
511+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
512+ */
513+
514+#include <miral/mru_window_list.h>
515+
516+#include <algorithm>
517+
518+void miral::MRUWindowList::push(Window const& window)
519+{
520+ surfaces.erase(remove(begin(surfaces), end(surfaces), window), end(surfaces));
521+ surfaces.push_back(window);
522+}
523+
524+void miral::MRUWindowList::erase(Window const& window)
525+{
526+ surfaces.erase(remove(begin(surfaces), end(surfaces), window), end(surfaces));
527+}
528+
529+auto miral::MRUWindowList::top() const -> Window
530+{
531+ return (!surfaces.empty()) ? surfaces.back() : Window{};
532+}
533+
534+void miral::MRUWindowList::enumerate(Enumerator const& enumerator) const
535+{
536+ for (auto i = surfaces.rbegin(); i != surfaces.rend(); ++i)
537+ if (!enumerator(const_cast<Window&>(*i)))
538+ break;
539+}
540
541=== added directory 'test'
542=== added file 'test/CMakeLists.txt'
543--- test/CMakeLists.txt 1970-01-01 00:00:00 +0000
544+++ test/CMakeLists.txt 2016-05-22 15:01:57 +0000
545@@ -0,0 +1,19 @@
546+find_package(GtestGmock REQUIRED)
547+pkg_check_modules(MIRTEST REQUIRED mirtest)
548+include_directories(
549+ ${MIRTEST_INCLUDE_DIRS}
550+ ${GMOCK_INCLUDE_DIR}
551+ ${GTEST_INCLUDE_DIR}
552+)
553+
554+add_executable(miral-test
555+ mru_window_list.cpp
556+)
557+
558+target_link_libraries(miral-test
559+ ${MIRTEST_LDFLAGS}
560+ ${GTEST_BOTH_LIBRARIES}
561+ ${GMOCK_LIBRARY}
562+ ${GMOCK_MAIN_LIBRARY}
563+ miral
564+)
565
566=== added file 'test/mru_window_list.cpp'
567--- test/mru_window_list.cpp 1970-01-01 00:00:00 +0000
568+++ test/mru_window_list.cpp 2016-05-22 15:01:57 +0000
569@@ -0,0 +1,139 @@
570+/*
571+ * Copyright © 2016 Canonical Ltd.
572+ *
573+ * This program is free software: you can redistribute it and/or modify it
574+ * under the terms of the GNU General Public License version 3,
575+ * as published by the Free Software Foundation.
576+ *
577+ * This program is distributed in the hope that it will be useful,
578+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
579+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
580+ * GNU General Public License for more details.
581+ *
582+ * You should have received a copy of the GNU General Public License
583+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
584+ *
585+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
586+ */
587+
588+#include <miral/mru_window_list.h>
589+
590+#include <mir/test/doubles/stub_surface.h>
591+#include <mir/test/doubles/stub_session.h>
592+
593+#include <gtest/gtest.h>
594+#include <gmock/gmock.h>
595+
596+using StubSurface = mir::test::doubles::StubSurface;
597+using namespace testing;
598+
599+namespace
600+{
601+struct StubSession : mir::test::doubles::StubSession
602+{
603+ StubSession(int number_of_surfaces)
604+ {
605+ for (auto i = 0; i != number_of_surfaces; ++i)
606+ surfaces.push_back(std::make_shared<mir::test::doubles::StubSurface>());
607+ }
608+
609+ std::shared_ptr<mir::scene::Surface> surface(mir::frontend::SurfaceId surface) const override
610+ {
611+ return surfaces.at(surface.as_value());
612+ }
613+
614+ std::vector<std::shared_ptr<StubSurface>> surfaces;
615+};
616+
617+MATCHER(IsNullWindow, std::string("is not null"))
618+{
619+ return !arg;
620+}
621+}
622+
623+struct MRUWindowList : testing::Test
624+{
625+ miral::MRUWindowList mru_list;
626+
627+ std::shared_ptr<StubSession> const stub_session{std::make_shared<StubSession>(3)};
628+ miral::Application app{stub_session};
629+ miral::Window window_a{app, mir::frontend::SurfaceId{0}};
630+ miral::Window window_b{app, mir::frontend::SurfaceId{1}};
631+ miral::Window window_c{app, mir::frontend::SurfaceId{2}};
632+};
633+
634+TEST_F(MRUWindowList, when_created_is_empty)
635+{
636+ EXPECT_THAT(mru_list.top(), IsNullWindow());
637+}
638+
639+TEST_F(MRUWindowList, given_empty_list_when_a_window_pushed_that_window_is_top)
640+{
641+ mru_list.push(window_a);
642+ EXPECT_THAT(mru_list.top(), Eq(window_a));
643+}
644+
645+TEST_F(MRUWindowList, given_non_empty_list_when_a_window_pushed_that_window_is_top)
646+{
647+ mru_list.push(window_a);
648+ mru_list.push(window_b);
649+ mru_list.push(window_c);
650+ EXPECT_THAT(mru_list.top(), Eq(window_c));
651+}
652+
653+TEST_F(MRUWindowList, given_non_empty_list_when_top_window_is_erased_that_window_is_no_longer_on_top)
654+{
655+ mru_list.push(window_a);
656+ mru_list.push(window_b);
657+ mru_list.push(window_c);
658+ mru_list.erase(window_c);
659+ EXPECT_THAT(mru_list.top(), Ne(window_c));
660+}
661+
662+TEST_F(MRUWindowList, a_window_pushed_twice_is_not_enumerated_twice)
663+{
664+ mru_list.push(window_a);
665+ mru_list.push(window_b);
666+ mru_list.push(window_a);
667+
668+ int count{0};
669+
670+ mru_list.enumerate([&](miral::Window& window)
671+ { if (window == window_a) ++count; return true; });
672+
673+ EXPECT_THAT(count, Eq(1));
674+}
675+
676+TEST_F(MRUWindowList, after_multiple_pushes_windows_are_enumerated_in_mru_order)
677+{
678+ mru_list.push(window_a);
679+ mru_list.push(window_b);
680+ mru_list.push(window_c);
681+ mru_list.push(window_a);
682+ mru_list.push(window_b);
683+ mru_list.push(window_a);
684+
685+ mru_list.push(window_c);
686+ mru_list.push(window_b);
687+ mru_list.push(window_a);
688+
689+ std::vector<miral::Window> as_enumerated;
690+
691+ mru_list.enumerate([&](miral::Window& window)
692+ { as_enumerated.push_back(window); return true; });
693+
694+ EXPECT_THAT(as_enumerated, ElementsAre(window_a, window_b, window_c));
695+}
696+
697+TEST_F(MRUWindowList, when_enumerator_returns_false_enumeration_is_short_circuited)
698+{
699+ mru_list.push(window_a);
700+ mru_list.push(window_b);
701+ mru_list.push(window_c);
702+
703+ int count{0};
704+
705+ mru_list.enumerate([&](miral::Window&) { ++count; return false; });
706+
707+ EXPECT_THAT(count, Eq(1));
708+}

Subscribers

People subscribed via source and target branches