Merge lp:~alan-griffiths/miral/mru-list-of-active-windows into lp:miral
- mru-list-of-active-windows
- Merge into trunk
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 |
Related bugs: |
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
- 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 | +} |