~alan-griffiths/miral/workaround-1667645

« back to all changes in this revision

Viewing changes to miral/basic_window_manager.cpp

  • Committer: Alan Griffiths
  • Date: 2017-02-17 12:46:18 UTC
  • mfrom: (497.7.45 miral4)
  • Revision ID: alan@octopull.co.uk-20170217124618-vqlhp26kejycwtz6
[libmiral] Support for shells implementing workspaces

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright © 2015-2016 Canonical Ltd.
 
2
 * Copyright © 2015-2017 Canonical Ltd.
3
3
 *
4
4
 * This program is free software: you can redistribute it and/or modify it
5
5
 * under the terms of the GNU General Public License version 3,
18
18
 
19
19
#include "basic_window_manager.h"
20
20
#include "miral/window_manager_tools.h"
 
21
#include "miral/workspace_policy.h"
21
22
 
22
23
#include <mir/scene/session.h>
23
24
#include <mir/scene/surface.h>
37
38
namespace
38
39
{
39
40
int const title_bar_height = 12;
 
41
}
40
42
 
41
 
struct Locker
 
43
struct miral::BasicWindowManager::Locker
42
44
{
43
 
    Locker(std::mutex& mutex, std::unique_ptr<miral::WindowManagementPolicy> const& policy) :
44
 
        lock{mutex},
45
 
        policy{policy.get()}
46
 
    {
47
 
        policy->advise_begin();
48
 
    }
 
45
    explicit Locker(miral::BasicWindowManager* self);
49
46
 
50
47
    ~Locker()
51
48
    {
53
50
    }
54
51
 
55
52
    std::lock_guard<std::mutex> const lock;
56
 
    miral::WindowManagementPolicy* const policy;
 
53
    WindowManagementPolicy* const policy;
57
54
};
58
 
}
 
55
 
 
56
miral::BasicWindowManager::Locker::Locker(BasicWindowManager* self) :
 
57
    lock{self->mutex},
 
58
    policy{self->policy.get()}
 
59
{
 
60
    policy->advise_begin();
 
61
    std::vector<std::weak_ptr<Workspace>> workspaces;
 
62
    {
 
63
        std::lock_guard<std::mutex> const lock{self->dead_workspaces.dead_workspaces_mutex};
 
64
        workspaces.swap(self->dead_workspaces.workspaces);
 
65
    }
 
66
 
 
67
    for (auto const& workspace : workspaces)
 
68
        self->workspaces_to_windows.left.erase(workspace);
 
69
}
 
70
 
 
71
namespace
 
72
{
 
73
 
 
74
auto find_workspace_policy(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WorkspacePolicy*
 
75
{
 
76
    miral::WorkspacePolicy* result = dynamic_cast<miral::WorkspacePolicy*>(policy.get());
 
77
 
 
78
    if (result)
 
79
        return result;
 
80
 
 
81
    struct NullWorkspacePolicy : miral::WorkspacePolicy
 
82
    {
 
83
        void advise_adding_to_workspace(
 
84
            std::shared_ptr<miral::Workspace> const&, std::vector<miral::Window> const&) override
 
85
        {
 
86
        }
 
87
 
 
88
        void advise_removing_from_workspace(
 
89
            std::shared_ptr<miral::Workspace> const&, std::vector<miral::Window> const&) override
 
90
        {
 
91
        }
 
92
    };
 
93
 
 
94
    static NullWorkspacePolicy null_workspace_policy;
 
95
 
 
96
    return &null_workspace_policy;
 
97
}
 
98
}
 
99
 
59
100
 
60
101
miral::BasicWindowManager::BasicWindowManager(
61
102
    shell::FocusController* focus_controller,
65
106
    focus_controller(focus_controller),
66
107
    display_layout(display_layout),
67
108
    persistent_surface_store{persistent_surface_store},
68
 
    policy(build(WindowManagerTools{this}))
 
109
    policy(build(WindowManagerTools{this})),
 
110
    workspace_policy{find_workspace_policy(policy)}
69
111
{
70
112
}
71
113
 
72
114
void miral::BasicWindowManager::add_session(std::shared_ptr<scene::Session> const& session)
73
115
{
74
 
    Locker lock{mutex, policy};
 
116
    Locker lock{this};
75
117
    policy->advise_new_app(app_info[session] = ApplicationInfo(session));
76
118
}
77
119
 
78
120
void miral::BasicWindowManager::remove_session(std::shared_ptr<scene::Session> const& session)
79
121
{
80
 
    Locker lock{mutex, policy};
 
122
    Locker lock{this};
81
123
    policy->advise_delete_app(app_info[session]);
82
124
    app_info.erase(session);
83
125
}
88
130
    std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build)
89
131
-> frontend::SurfaceId
90
132
{
91
 
    Locker lock{mutex, policy};
 
133
    Locker lock{this};
92
134
 
93
135
    auto& session_info = info_for(session);
94
136
 
112
154
    if (parent)
113
155
        info_for(parent).add_child(window);
114
156
 
 
157
    for_each_workspace_containing(parent,
 
158
        [&](std::shared_ptr<miral::Workspace> const& workspace) { add_tree_to_workspace(window, workspace); });
 
159
 
115
160
    if (window_info.state() == mir_window_state_fullscreen)
116
161
        fullscreen_surfaces.insert(window_info.window());
117
162
 
120
165
    std::shared_ptr<scene::Surface> const scene_surface = window_info.window();
121
166
    scene_surface->add_observer(std::make_shared<shell::SurfaceReadyObserver>(
122
167
        [this, &window_info](std::shared_ptr<scene::Session> const&, std::shared_ptr<scene::Surface> const&)
123
 
            { Locker lock{mutex, policy}; policy->handle_window_ready(window_info); },
 
168
            { Locker lock{this}; policy->handle_window_ready(window_info); },
124
169
        session,
125
170
        scene_surface));
126
171
 
141
186
    std::shared_ptr<scene::Surface> const& surface,
142
187
    shell::SurfaceSpecification const& modifications)
143
188
{
144
 
    Locker lock{mutex, policy};
 
189
    Locker lock{this};
145
190
    auto& info = info_for(surface);
146
191
    WindowSpecification mods{modifications};
147
192
    validate_modification_request(mods, info);
153
198
    std::shared_ptr<scene::Session> const& session,
154
199
    std::weak_ptr<scene::Surface> const& surface)
155
200
{
156
 
    Locker lock{mutex, policy};
 
201
    Locker lock{this};
157
202
    remove_window(session, info_for(surface));
158
203
}
159
204
 
160
205
void miral::BasicWindowManager::remove_window(Application const& application, miral::WindowInfo const& info)
161
206
{
 
207
    bool const is_active_window{mru_active_windows.top() == info.window()};
 
208
    auto const workspaces_containing_window = workspaces_containing(info.window());
 
209
 
 
210
    {
 
211
        std::vector<Window> const windows_removed{info.window()};
 
212
 
 
213
        for (auto const& workspace : workspaces_containing_window)
 
214
        {
 
215
            workspace_policy->advise_removing_from_workspace(workspace, windows_removed);
 
216
        }
 
217
 
 
218
        workspaces_to_windows.right.erase(info.window());
 
219
    }
 
220
 
162
221
    policy->advise_delete_window(info);
163
222
 
164
 
    bool const is_active_window{mru_active_windows.top() == info.window()};
165
 
 
166
223
    info_for(application).remove_window(info.window());
167
224
    mru_active_windows.erase(info.window());
168
225
    fullscreen_surfaces.erase(info.window());
175
232
 
176
233
    if (is_active_window)
177
234
    {
178
 
        // Try to make the parent active
179
 
        if (parent && select_active_window(parent))
180
 
            return;
181
 
 
182
 
        if (can_activate_window_for_session(application))
183
 
            return;
184
 
 
185
 
        // Try to activate to recently active window of any application
186
 
        {
187
 
            miral::Window new_focus;
188
 
 
189
 
            mru_active_windows.enumerate([&](miral::Window& window)
 
235
        refocus(application, parent, workspaces_containing_window);
 
236
    }
 
237
}
 
238
 
 
239
void miral::BasicWindowManager::refocus(
 
240
    miral::Application const& application, miral::Window const& parent,
 
241
    std::vector<std::shared_ptr<Workspace>> const& workspaces_containing_window)
 
242
{
 
243
    // Try to make the parent active
 
244
    if (parent && select_active_window(parent))
 
245
        return;
 
246
 
 
247
    if (can_activate_window_for_session_in_workspace(application, workspaces_containing_window))
 
248
        return;
 
249
 
 
250
    // Try to activate to recently active window of any application in a shared workspace
 
251
    {
 
252
        miral::Window new_focus;
 
253
 
 
254
        mru_active_windows.enumerate([&](miral::Window& window)
 
255
            {
 
256
                // select_active_window() calls set_focus_to() which updates mru_active_windows and changes window
 
257
                auto const w = window;
 
258
 
 
259
                for (auto const& workspace : workspaces_containing(w))
190
260
                {
191
 
                    // select_active_window() calls set_focus_to() which updates mru_active_windows and changes window
192
 
                    auto const w = window;
193
 
                    return !(new_focus = select_active_window(w));
194
 
                });
195
 
 
196
 
            if (new_focus) return;
197
 
        }
198
 
 
199
 
        // Fallback to cycling through applications
200
 
        focus_next_application();
201
 
    }
 
261
                    for (auto const& ww : workspaces_containing_window)
 
262
                    {
 
263
                        if (ww == workspace)
 
264
                        {
 
265
                            return !(new_focus = select_active_window(w));
 
266
                        }
 
267
                    }
 
268
                }
 
269
 
 
270
                return true;
 
271
            });
 
272
 
 
273
        if (new_focus) return;
 
274
    }
 
275
 
 
276
    if (can_activate_window_for_session(application))
 
277
        return;
 
278
 
 
279
    // Try to activate to recently active window of any application
 
280
    {
 
281
        miral::Window new_focus;
 
282
 
 
283
        mru_active_windows.enumerate([&](miral::Window& window)
 
284
            {
 
285
                // select_active_window() calls set_focus_to() which updates mru_active_windows and changes window
 
286
                auto const w = window;
 
287
                return !(new_focus = select_active_window(w));
 
288
            });
 
289
 
 
290
        if (new_focus) return;
 
291
    }
 
292
 
 
293
    // Fallback to cycling through applications
 
294
    focus_next_application();
202
295
}
203
296
 
204
297
void miral::BasicWindowManager::erase(miral::WindowInfo const& info)
214
307
 
215
308
void miral::BasicWindowManager::add_display(geometry::Rectangle const& area)
216
309
{
217
 
    Locker lock{mutex, policy};
 
310
    Locker lock{this};
218
311
    displays.add(area);
219
312
 
220
313
    for (auto window : fullscreen_surfaces)
230
323
 
231
324
void miral::BasicWindowManager::remove_display(geometry::Rectangle const& area)
232
325
{
233
 
    Locker lock{mutex, policy};
 
326
    Locker lock{this};
234
327
    displays.remove(area);
235
328
    for (auto window : fullscreen_surfaces)
236
329
    {
245
338
 
246
339
bool miral::BasicWindowManager::handle_keyboard_event(MirKeyboardEvent const* event)
247
340
{
248
 
    Locker lock{mutex, policy};
 
341
    Locker lock{this};
249
342
    update_event_timestamp(event);
250
343
    return policy->handle_keyboard_event(event);
251
344
}
252
345
 
253
346
bool miral::BasicWindowManager::handle_touch_event(MirTouchEvent const* event)
254
347
{
255
 
    Locker lock{mutex, policy};
 
348
    Locker lock{this};
256
349
    update_event_timestamp(event);
257
350
    return policy->handle_touch_event(event);
258
351
}
259
352
 
260
353
bool miral::BasicWindowManager::handle_pointer_event(MirPointerEvent const* event)
261
354
{
262
 
    Locker lock{mutex, policy};
 
355
    Locker lock{this};
263
356
    update_event_timestamp(event);
264
357
 
265
358
    cursor = {
274
367
    std::shared_ptr<scene::Surface> const& surface,
275
368
    uint64_t timestamp)
276
369
{
277
 
    Locker lock{mutex, policy};
 
370
    Locker lock{this};
278
371
    if (timestamp >= last_input_event_timestamp)
279
372
        policy->handle_raise_window(info_for(surface));
280
373
}
315
408
        return surface->configure(attrib, value);
316
409
    }
317
410
 
318
 
    Locker lock{mutex, policy};
 
411
    Locker lock{this};
319
412
    auto& info = info_for(surface);
320
413
 
321
414
    validate_modification_request(modification, info);
407
500
 
408
501
void miral::BasicWindowManager::focus_next_application()
409
502
{
 
503
    if (auto const prev = active_window())
 
504
    {
 
505
        auto const workspaces_containing_window = workspaces_containing(prev);
 
506
 
 
507
        if (!workspaces_containing_window.empty())
 
508
        {
 
509
            do
 
510
            {
 
511
                focus_controller->focus_next_session();
 
512
 
 
513
                if (can_activate_window_for_session_in_workspace(
 
514
                    focus_controller->focused_session(),
 
515
                    workspaces_containing_window))
 
516
                {
 
517
                    return;
 
518
                }
 
519
            }
 
520
            while (focus_controller->focused_session() != prev.application());
 
521
        }
 
522
 
 
523
    }
 
524
 
410
525
    focus_controller->focus_next_session();
411
526
 
412
527
    if (can_activate_window_for_session(focus_controller->focused_session()))
417
532
    select_active_window(focussed_surface ? info_for(focussed_surface).window() : Window{});
418
533
}
419
534
 
 
535
auto miral::BasicWindowManager::workspaces_containing(Window const& window) const
 
536
-> std::vector<std::shared_ptr<Workspace>>
 
537
{
 
538
    auto const iter_pair = workspaces_to_windows.right.equal_range(window);
 
539
 
 
540
    std::vector<std::shared_ptr<Workspace>> workspaces_containing_window;
 
541
    for (auto kv = iter_pair.first; kv != iter_pair.second; ++kv)
 
542
    {
 
543
        if (auto const workspace = kv->second.lock())
 
544
        {
 
545
            workspaces_containing_window.push_back(workspace);
 
546
        }
 
547
    }
 
548
 
 
549
    return workspaces_containing_window;
 
550
}
 
551
 
420
552
void miral::BasicWindowManager::focus_next_within_application()
421
553
{
422
554
    if (auto const prev = active_window())
423
555
    {
 
556
        auto const workspaces_containing_window = workspaces_containing(prev);
424
557
        auto const& siblings = info_for(prev.application()).windows();
425
558
        auto current = find(begin(siblings), end(siblings), prev);
426
559
 
427
560
        if (current != end(siblings))
428
561
        {
 
562
            while (++current != end(siblings))
 
563
            {
 
564
                for (auto const& workspace : workspaces_containing(*current))
 
565
                {
 
566
                    for (auto const& ww : workspaces_containing_window)
 
567
                    {
 
568
                        if (ww == workspace)
 
569
                        {
 
570
                            if (prev != select_active_window(*current))
 
571
                                return;
 
572
                        }
 
573
                    }
 
574
                }
 
575
            }
 
576
        }
 
577
 
 
578
        for (current = begin(siblings); *current != prev; ++current)
 
579
        {
 
580
            for (auto const& workspace : workspaces_containing(*current))
 
581
            {
 
582
                for (auto const& ww : workspaces_containing_window)
 
583
                {
 
584
                    if (ww == workspace)
 
585
                    {
 
586
                        if (prev != select_active_window(*current))
 
587
                            return;
 
588
                    }
 
589
                }
 
590
            }
 
591
        }
 
592
 
 
593
        current = find(begin(siblings), end(siblings), prev);
 
594
        if (current != end(siblings))
 
595
        {
429
596
            while (++current != end(siblings) && prev == select_active_window(*current))
430
597
                ;
431
598
        }
886
1053
 
887
1054
        if (window == active_window())
888
1055
        {
 
1056
            auto const workspaces_containing_window = workspaces_containing(window);
 
1057
 
889
1058
            // Try to activate to recently active window of any application
890
1059
            mru_active_windows.enumerate([&](Window& candidate)
891
1060
                {
892
1061
                    if (candidate == window)
893
1062
                        return true;
894
1063
                    auto const w = candidate;
 
1064
                    for (auto const& workspace : workspaces_containing(w))
 
1065
                    {
 
1066
                        for (auto const& ww : workspaces_containing_window)
 
1067
                        {
 
1068
                            if (ww == workspace)
 
1069
                            {
 
1070
                                return !(select_active_window(w));
 
1071
                            }
 
1072
                        }
 
1073
                    }
 
1074
 
 
1075
                    return true;
 
1076
                });
 
1077
 
 
1078
            // Try to activate to recently active window of any application
 
1079
            if (window == active_window() || !active_window())
 
1080
                mru_active_windows.enumerate([&](Window& candidate)
 
1081
                {
 
1082
                    if (candidate == window)
 
1083
                        return true;
 
1084
                    auto const w = candidate;
895
1085
                    return !(select_active_window(w));
896
1086
                });
897
1087
 
945
1135
 
946
1136
void miral::BasicWindowManager::invoke_under_lock(std::function<void()> const& callback)
947
1137
{
948
 
    Locker lock{mutex, policy};
 
1138
    Locker lock{this};
949
1139
    callback();
950
1140
}
951
1141
 
1076
1266
    return new_focus;
1077
1267
}
1078
1268
 
 
1269
auto miral::BasicWindowManager::can_activate_window_for_session_in_workspace(
 
1270
    Application const& session,
 
1271
    std::vector<std::shared_ptr<Workspace>> const& workspaces) -> bool
 
1272
{
 
1273
    miral::Window new_focus;
 
1274
 
 
1275
    mru_active_windows.enumerate([&](miral::Window& window)
 
1276
        {
 
1277
            // select_active_window() calls set_focus_to() which updates mru_active_windows and changes window
 
1278
            auto const w = window;
 
1279
 
 
1280
            if (w.application() != session)
 
1281
                return true;
 
1282
 
 
1283
            for (auto const& workspace : workspaces_containing(w))
 
1284
            {
 
1285
                for (auto const& ww : workspaces)
 
1286
                {
 
1287
                    if (ww == workspace)
 
1288
                        return !(new_focus = select_active_window(w));
 
1289
                }
 
1290
            }
 
1291
 
 
1292
            return true;
 
1293
        });
 
1294
 
 
1295
    return new_focus;
 
1296
}
 
1297
 
1079
1298
auto miral::BasicWindowManager::place_new_surface(ApplicationInfo const& app_info, WindowSpecification parameters)
1080
1299
-> WindowSpecification
1081
1300
{
1610
1829
            BOOST_THROW_EXCEPTION(std::runtime_error("height must be positive"));
1611
1830
    }
1612
1831
}
 
1832
 
 
1833
class miral::Workspace
 
1834
{
 
1835
public:
 
1836
    explicit Workspace(miral::BasicWindowManager::DeadWorkspaces& dead_workspaces) :
 
1837
        dead_workspaces{dead_workspaces} {}
 
1838
 
 
1839
    std::weak_ptr<Workspace> self;
 
1840
 
 
1841
    ~Workspace()
 
1842
    {
 
1843
        std::lock_guard<std::mutex> lock {dead_workspaces.dead_workspaces_mutex};
 
1844
        dead_workspaces.workspaces.push_back(self);
 
1845
    }
 
1846
 
 
1847
private:
 
1848
    miral::BasicWindowManager::DeadWorkspaces& dead_workspaces;
 
1849
};
 
1850
 
 
1851
auto miral::BasicWindowManager::create_workspace() -> std::shared_ptr<Workspace>
 
1852
{
 
1853
    auto const result = std::make_shared<Workspace>(dead_workspaces);
 
1854
    result->self = result;
 
1855
    return result;
 
1856
}
 
1857
 
 
1858
void miral::BasicWindowManager::add_tree_to_workspace(
 
1859
    miral::Window const& window, std::shared_ptr<miral::Workspace> const& workspace)
 
1860
{
 
1861
    if (!window) return;
 
1862
 
 
1863
    auto root = window;
 
1864
    auto const* info = &info_for(root);
 
1865
 
 
1866
    while (auto const& parent = info->parent())
 
1867
    {
 
1868
        root = parent;
 
1869
        info = &info_for(root);
 
1870
    }
 
1871
 
 
1872
    std::vector<Window> windows;
 
1873
 
 
1874
    std::function<void(WindowInfo const& info)> const add_children =
 
1875
        [&,this](WindowInfo const& info)
 
1876
        {
 
1877
            for (auto const& child : info.children())
 
1878
            {
 
1879
                windows.push_back(child);
 
1880
                add_children(info_for(child));
 
1881
            }
 
1882
        };
 
1883
 
 
1884
    windows.push_back(root);
 
1885
    add_children(*info);
 
1886
 
 
1887
    auto const iter_pair = workspaces_to_windows.left.equal_range(workspace);
 
1888
 
 
1889
    std::vector<Window> windows_added;
 
1890
 
 
1891
    for (auto& w : windows)
 
1892
    {
 
1893
        if (!std::count_if(iter_pair.first, iter_pair.second,
 
1894
                           [&w](wwbimap_t::left_value_type const& kv) { return kv.second == w; }))
 
1895
        {
 
1896
            workspaces_to_windows.left.insert(wwbimap_t::left_value_type{workspace, w});
 
1897
            windows_added.push_back(w);
 
1898
        }
 
1899
    }
 
1900
 
 
1901
    if (!windows_added.empty())
 
1902
        workspace_policy->advise_adding_to_workspace(workspace, windows_added);
 
1903
}
 
1904
 
 
1905
void miral::BasicWindowManager::remove_tree_from_workspace(
 
1906
    miral::Window const& window, std::shared_ptr<miral::Workspace> const& workspace)
 
1907
{
 
1908
    if (!window) return;
 
1909
 
 
1910
    auto root = window;
 
1911
    auto const* info = &info_for(root);
 
1912
 
 
1913
    while (auto const& parent = info->parent())
 
1914
    {
 
1915
        root = parent;
 
1916
        info = &info_for(root);
 
1917
    }
 
1918
 
 
1919
    std::vector<Window> windows;
 
1920
 
 
1921
    std::function<void(WindowInfo const& info)> const add_children =
 
1922
        [&,this](WindowInfo const& info)
 
1923
            {
 
1924
                for (auto const& child : info.children())
 
1925
                {
 
1926
                    windows.push_back(child);
 
1927
                    add_children(info_for(child));
 
1928
                }
 
1929
            };
 
1930
 
 
1931
    windows.push_back(root);
 
1932
    add_children(*info);
 
1933
 
 
1934
    std::vector<Window> windows_removed;
 
1935
 
 
1936
    auto const iter_pair = workspaces_to_windows.left.equal_range(workspace);
 
1937
    for (auto kv = iter_pair.first; kv != iter_pair.second; ++kv)
 
1938
    {
 
1939
        if (std::count(begin(windows), end(windows), kv->second))
 
1940
        {
 
1941
            workspaces_to_windows.left.erase(kv);
 
1942
            windows_removed.push_back(kv->second);
 
1943
        }
 
1944
    }
 
1945
 
 
1946
    if (!windows_removed.empty())
 
1947
        workspace_policy->advise_removing_from_workspace(workspace, windows_removed);
 
1948
}
 
1949
 
 
1950
void miral::BasicWindowManager::for_each_workspace_containing(
 
1951
    miral::Window const& window, std::function<void(std::shared_ptr<miral::Workspace> const&)> const& callback)
 
1952
{
 
1953
    auto const iter_pair = workspaces_to_windows.right.equal_range(window);
 
1954
    for (auto kv = iter_pair.first; kv != iter_pair.second; ++kv)
 
1955
    {
 
1956
        if (auto const workspace = kv->second.lock())
 
1957
            callback(workspace);
 
1958
    }
 
1959
}
 
1960
 
 
1961
void miral::BasicWindowManager::for_each_window_in_workspace(
 
1962
    std::shared_ptr<miral::Workspace> const& workspace, std::function<void(miral::Window const&)> const& callback)
 
1963
{
 
1964
    auto const iter_pair = workspaces_to_windows.left.equal_range(workspace);
 
1965
    for (auto kv = iter_pair.first; kv != iter_pair.second; ++kv)
 
1966
        callback(kv->second);
 
1967
}