~ubuntu-branches/ubuntu/wily/mir/wily-proposed

« back to all changes in this revision

Viewing changes to examples/server_example_tiling_window_manager.cpp

  • Committer: Package Import Robot
  • Author(s): CI Train Bot
  • Date: 2015-05-12 13:12:55 UTC
  • mto: This revision was merged to the branch mainline in revision 96.
  • Revision ID: package-import@ubuntu.com-20150512131255-y7z12i8n4pbvo70x
Tags: upstream-0.13.0+15.10.20150512
ImportĀ upstreamĀ versionĀ 0.13.0+15.10.20150512

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright Ā© 2015 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License version 3,
 
6
 * as published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored By: Alan Griffiths <alan@octopull.co.uk>
 
17
 */
 
18
 
 
19
#include "server_example_tiling_window_manager.h"
 
20
 
 
21
#include "mir/scene/surface.h"
 
22
#include "mir/scene/surface_coordinator.h"
 
23
#include "mir/shell/surface_specification.h"
 
24
#include "mir/geometry/displacement.h"
 
25
 
 
26
#include <linux/input.h>
 
27
#include <csignal>
 
28
 
 
29
namespace me = mir::examples;
 
30
namespace ms = mir::scene;
 
31
using namespace mir::geometry;
 
32
 
 
33
///\example server_example_tiling_window_manager.cpp
 
34
/// Demonstrate implementing a simple tiling algorithm
 
35
 
 
36
me::TilingSurfaceInfo::TilingSurfaceInfo(
 
37
    std::shared_ptr<scene::Session> const& session,
 
38
    std::shared_ptr<scene::Surface> const& surface,
 
39
    scene::SurfaceCreationParameters const& /*params*/) :
 
40
    session{session},
 
41
    state{mir_surface_state_restored},
 
42
    restore_rect{surface->top_left(), surface->size()}
 
43
{
 
44
}
 
45
 
 
46
me::TilingWindowManagerPolicy::TilingWindowManagerPolicy(Tools* const tools) :
 
47
    tools{tools}
 
48
{
 
49
}
 
50
 
 
51
void me::TilingWindowManagerPolicy::click(Point cursor)
 
52
{
 
53
    auto const session = session_under(cursor);
 
54
    auto const surface = tools->surface_at(cursor);
 
55
    tools->set_focus_to(session, surface);
 
56
    if (auto const surface = tools->focused_surface())
 
57
        tools->raise({surface});
 
58
    old_cursor = cursor;
 
59
}
 
60
 
 
61
void me::TilingWindowManagerPolicy::handle_session_info_updated(TilingSessionInfoMap& session_info, Rectangles const& displays)
 
62
{
 
63
    update_tiles(session_info, displays);
 
64
}
 
65
 
 
66
void me::TilingWindowManagerPolicy::handle_displays_updated(TilingSessionInfoMap& session_info, Rectangles const& displays)
 
67
{
 
68
    update_tiles(session_info, displays);
 
69
}
 
70
 
 
71
void me::TilingWindowManagerPolicy::resize(Point cursor)
 
72
{
 
73
    if (auto const session = session_under(cursor))
 
74
    {
 
75
        if (session == session_under(old_cursor))
 
76
        {
 
77
            auto const& info = tools->info_for(session);
 
78
 
 
79
            if (resize(tools->focused_surface(), cursor, old_cursor, info.tile))
 
80
            {
 
81
                // Still dragging the same surface
 
82
            }
 
83
            else
 
84
            {
 
85
                auto const new_surface = tools->surface_at(old_cursor);
 
86
 
 
87
                if (new_surface && tools->info_for(new_surface).session.lock() == session)
 
88
                {
 
89
                    if (resize(new_surface, cursor, old_cursor, info.tile))
 
90
                    {
 
91
                        tools->set_focus_to(session, new_surface);
 
92
                        if (auto const surface = tools->focused_surface())
 
93
                            tools->raise({surface});
 
94
                    }
 
95
                }
 
96
            }
 
97
        }
 
98
    }
 
99
 
 
100
    old_cursor = cursor;
 
101
}
 
102
 
 
103
auto me::TilingWindowManagerPolicy::handle_place_new_surface(
 
104
    std::shared_ptr<ms::Session> const& session,
 
105
    ms::SurfaceCreationParameters const& request_parameters)
 
106
-> ms::SurfaceCreationParameters
 
107
{
 
108
    auto parameters = request_parameters;
 
109
 
 
110
    Rectangle const& tile = tools->info_for(session).tile;
 
111
    parameters.top_left = parameters.top_left + (tile.top_left - Point{0, 0});
 
112
 
 
113
    clip_to_tile(parameters, tile);
 
114
    return parameters;
 
115
}
 
116
 
 
117
void me::TilingWindowManagerPolicy::generate_decorations_for(
 
118
    std::shared_ptr<ms::Session> const&,
 
119
    std::shared_ptr<ms::Surface> const&,
 
120
    TilingSurfaceInfoMap&)
 
121
{
 
122
}
 
123
 
 
124
void me::TilingWindowManagerPolicy::handle_new_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface)
 
125
{
 
126
    tools->info_for(session).surfaces.push_back(surface);
 
127
}
 
128
 
 
129
void me::TilingWindowManagerPolicy::handle_modify_surface(
 
130
    std::shared_ptr<scene::Session> const& /*session*/,
 
131
    std::shared_ptr<scene::Surface> const& surface,
 
132
    shell::SurfaceSpecification const& modifications)
 
133
{
 
134
    if (modifications.name.is_set())
 
135
        surface->rename(modifications.name.value());
 
136
}
 
137
 
 
138
void me::TilingWindowManagerPolicy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
 
139
{
 
140
    auto& surfaces = tools->info_for(session).surfaces;
 
141
 
 
142
    for (auto i = begin(surfaces); i != end(surfaces); ++i)
 
143
    {
 
144
        if (surface.lock() == i->lock())
 
145
        {
 
146
            surfaces.erase(i);
 
147
            break;
 
148
        }
 
149
    }
 
150
 
 
151
    if (surfaces.empty() && session == tools->focused_session())
 
152
    {
 
153
        tools->focus_next_session();
 
154
        if (auto const surface = tools->focused_surface())
 
155
            tools->raise({surface});
 
156
    }
 
157
}
 
158
 
 
159
int me::TilingWindowManagerPolicy::handle_set_state(std::shared_ptr<ms::Surface> const& surface, MirSurfaceState value)
 
160
{
 
161
    auto& info = tools->info_for(surface);
 
162
 
 
163
    switch (value)
 
164
    {
 
165
    case mir_surface_state_restored:
 
166
    case mir_surface_state_maximized:
 
167
    case mir_surface_state_vertmaximized:
 
168
    case mir_surface_state_horizmaximized:
 
169
        break;
 
170
 
 
171
    default:
 
172
        return info.state;
 
173
    }
 
174
 
 
175
    if (info.state == mir_surface_state_restored)
 
176
    {
 
177
        info.restore_rect = {surface->top_left(), surface->size()};
 
178
    }
 
179
 
 
180
    if (info.state == value)
 
181
    {
 
182
        return info.state;
 
183
    }
 
184
 
 
185
    auto const& tile = tools->info_for(info.session).tile;
 
186
 
 
187
    switch (value)
 
188
    {
 
189
    case mir_surface_state_restored:
 
190
        surface->move_to(info.restore_rect.top_left);
 
191
        surface->resize(info.restore_rect.size);
 
192
        break;
 
193
 
 
194
    case mir_surface_state_maximized:
 
195
        surface->move_to(tile.top_left);
 
196
        surface->resize(tile.size);
 
197
        break;
 
198
 
 
199
    case mir_surface_state_horizmaximized:
 
200
        surface->move_to({tile.top_left.x, info.restore_rect.top_left.y});
 
201
        surface->resize({tile.size.width, info.restore_rect.size.height});
 
202
        break;
 
203
 
 
204
    case mir_surface_state_vertmaximized:
 
205
        surface->move_to({info.restore_rect.top_left.x, tile.top_left.y});
 
206
        surface->resize({info.restore_rect.size.width, tile.size.height});
 
207
        break;
 
208
 
 
209
    default:
 
210
        break;
 
211
    }
 
212
 
 
213
    return info.state = value;
 
214
}
 
215
 
 
216
void me::TilingWindowManagerPolicy::drag(Point cursor)
 
217
{
 
218
    if (auto const session = session_under(cursor))
 
219
    {
 
220
        if (session == session_under(old_cursor))
 
221
        {
 
222
            auto const& info = tools->info_for(session);
 
223
            if (drag(tools->focused_surface(), cursor, old_cursor, info.tile))
 
224
            {
 
225
                // Still dragging the same surface
 
226
            }
 
227
            else
 
228
            {
 
229
                auto const new_surface = tools->surface_at(old_cursor);
 
230
 
 
231
                if (new_surface && tools->info_for(new_surface).session.lock() == session)
 
232
                {
 
233
                    if (resize(new_surface, cursor, old_cursor, info.tile))
 
234
                    {
 
235
                        tools->set_focus_to(session, new_surface);
 
236
                        if (auto const surface = tools->focused_surface())
 
237
                            tools->raise({surface});
 
238
                    }
 
239
                }
 
240
            }
 
241
        }
 
242
    }
 
243
 
 
244
    old_cursor = cursor;
 
245
}
 
246
 
 
247
bool me::TilingWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
 
248
{
 
249
    auto const action = mir_keyboard_event_action(event);
 
250
    auto const scan_code = mir_keyboard_event_scan_code(event);
 
251
    auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask;
 
252
 
 
253
    if (action == mir_keyboard_action_down && scan_code == KEY_F11)
 
254
    {
 
255
        switch (modifiers & modifier_mask)
 
256
        {
 
257
        case mir_input_event_modifier_alt:
 
258
            toggle(mir_surface_state_maximized);
 
259
            return true;
 
260
 
 
261
        case mir_input_event_modifier_shift:
 
262
            toggle(mir_surface_state_vertmaximized);
 
263
            return true;
 
264
 
 
265
        case mir_input_event_modifier_ctrl:
 
266
            toggle(mir_surface_state_horizmaximized);
 
267
            return true;
 
268
 
 
269
        default:
 
270
            break;
 
271
        }
 
272
    }
 
273
    else if (action == mir_keyboard_action_down && scan_code == KEY_F4)
 
274
    {
 
275
        if (auto const session = tools->focused_session())
 
276
        {
 
277
            switch (modifiers & modifier_mask)
 
278
            {
 
279
            case mir_input_event_modifier_alt:
 
280
                kill(session->process_id(), SIGTERM);
 
281
                return true;
 
282
 
 
283
            case mir_input_event_modifier_ctrl:
 
284
                if (auto const surf = session->default_surface())
 
285
                {
 
286
                    surf->request_client_surface_close();
 
287
                    return true;
 
288
                }
 
289
 
 
290
            default:
 
291
                break;
 
292
            }
 
293
        }
 
294
    }
 
295
 
 
296
    return false;
 
297
}
 
298
 
 
299
bool me::TilingWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event)
 
300
{
 
301
    auto const count = mir_touch_event_point_count(event);
 
302
 
 
303
    long total_x = 0;
 
304
    long total_y = 0;
 
305
 
 
306
    for (auto i = 0U; i != count; ++i)
 
307
    {
 
308
        total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x);
 
309
        total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y);
 
310
    }
 
311
 
 
312
    Point const cursor{total_x/count, total_y/count};
 
313
 
 
314
    bool is_drag = true;
 
315
    for (auto i = 0U; i != count; ++i)
 
316
    {
 
317
        switch (mir_touch_event_action(event, i))
 
318
        {
 
319
        case mir_touch_action_up:
 
320
            return false;
 
321
 
 
322
        case mir_touch_action_down:
 
323
            is_drag = false;
 
324
 
 
325
        case mir_touch_action_change:
 
326
            continue;
 
327
        }
 
328
    }
 
329
 
 
330
    if (is_drag && count == 3)
 
331
    {
 
332
        drag(cursor);
 
333
        return true;
 
334
    }
 
335
    else
 
336
    {
 
337
        click(cursor);
 
338
        return false;
 
339
    }
 
340
}
 
341
 
 
342
bool me::TilingWindowManagerPolicy::handle_pointer_event(MirPointerEvent const* event)
 
343
{
 
344
    auto const action = mir_pointer_event_action(event);
 
345
    auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask;
 
346
    Point const cursor{
 
347
        mir_pointer_event_axis_value(event, mir_pointer_axis_x),
 
348
        mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
 
349
 
 
350
    if (action == mir_pointer_action_button_down)
 
351
    {
 
352
        click(cursor);
 
353
        return false;
 
354
    }
 
355
    else if (action == mir_pointer_action_motion &&
 
356
             modifiers == mir_input_event_modifier_alt)
 
357
    {
 
358
        if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
 
359
        {
 
360
            drag(cursor);
 
361
            return true;
 
362
        }
 
363
 
 
364
        if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary))
 
365
        {
 
366
            resize(cursor);
 
367
            return true;
 
368
        }
 
369
    }
 
370
 
 
371
    return false;
 
372
}
 
373
 
 
374
void me::TilingWindowManagerPolicy::toggle(MirSurfaceState state)
 
375
{
 
376
    if (auto const session = tools->focused_session())
 
377
    {
 
378
        if (auto const surface = session->default_surface())
 
379
        {
 
380
            if (surface->state() == state)
 
381
                state = mir_surface_state_restored;
 
382
 
 
383
            auto const value = handle_set_state(surface, MirSurfaceState(state));
 
384
            surface->configure(mir_surface_attrib_state, value);
 
385
        }
 
386
    }
 
387
}
 
388
 
 
389
std::shared_ptr<ms::Session> me::TilingWindowManagerPolicy::session_under(Point position)
 
390
{
 
391
    return tools->find_session([&](TilingSessionInfo const& info) { return info.tile.contains(position);});
 
392
}
 
393
 
 
394
void me::TilingWindowManagerPolicy::update_tiles(
 
395
    TilingSessionInfoMap& session_info,
 
396
    Rectangles const& displays)
 
397
{
 
398
    if (session_info.size() < 1 || displays.size() < 1) return;
 
399
 
 
400
    auto const sessions = session_info.size();
 
401
 
 
402
    auto const bounding_rect = displays.bounding_rectangle();
 
403
 
 
404
    auto const total_width  = bounding_rect.size.width.as_int();
 
405
    auto const total_height = bounding_rect.size.height.as_int();
 
406
 
 
407
    auto index = 0;
 
408
 
 
409
    for (auto& info : session_info)
 
410
    {
 
411
        auto const x = (total_width*index)/sessions;
 
412
        ++index;
 
413
        auto const dx = (total_width*index)/sessions - x;
 
414
 
 
415
        auto const old_tile = info.second.tile;
 
416
        Rectangle const new_tile{{x, 0}, {dx, total_height}};
 
417
 
 
418
        update_surfaces(info.first, old_tile, new_tile);
 
419
 
 
420
        info.second.tile = new_tile;
 
421
    }
 
422
}
 
423
 
 
424
void me::TilingWindowManagerPolicy::update_surfaces(std::weak_ptr<ms::Session> const& session, Rectangle const& old_tile, Rectangle const& new_tile)
 
425
{
 
426
    auto displacement = new_tile.top_left - old_tile.top_left;
 
427
    auto& info = tools->info_for(session);
 
428
 
 
429
    for (auto const& ps : info.surfaces)
 
430
    {
 
431
        if (auto const surface = ps.lock())
 
432
        {
 
433
            auto const old_pos = surface->top_left();
 
434
            surface->move_to(old_pos + displacement);
 
435
 
 
436
            fit_to_new_tile(*surface, old_tile, new_tile);
 
437
        }
 
438
    }
 
439
}
 
440
 
 
441
void me::TilingWindowManagerPolicy::clip_to_tile(ms::SurfaceCreationParameters& parameters, Rectangle const& tile)
 
442
{
 
443
    auto const displacement = parameters.top_left - tile.top_left;
 
444
 
 
445
    auto width = std::min(tile.size.width.as_int()-displacement.dx.as_int(), parameters.size.width.as_int());
 
446
    auto height = std::min(tile.size.height.as_int()-displacement.dy.as_int(), parameters.size.height.as_int());
 
447
 
 
448
    parameters.size = Size{width, height};
 
449
}
 
450
 
 
451
void me::TilingWindowManagerPolicy::fit_to_new_tile(ms::Surface& surface, Rectangle const& old_tile, Rectangle const& new_tile)
 
452
{
 
453
    auto const displacement = surface.top_left() - new_tile.top_left;
 
454
 
 
455
    // For now just scale if was filling width/height of tile
 
456
    auto const old_size = surface.size();
 
457
    auto const scaled_width = old_size.width == old_tile.size.width ? new_tile.size.width : old_size.width;
 
458
    auto const scaled_height = old_size.height == old_tile.size.height ? new_tile.size.height : old_size.height;
 
459
 
 
460
    auto width = std::min(new_tile.size.width.as_int()-displacement.dx.as_int(), scaled_width.as_int());
 
461
    auto height = std::min(new_tile.size.height.as_int()-displacement.dy.as_int(), scaled_height.as_int());
 
462
 
 
463
    surface.resize({width, height});
 
464
}
 
465
 
 
466
bool me::TilingWindowManagerPolicy::drag(std::shared_ptr<ms::Surface> surface, Point to, Point from, Rectangle bounds)
 
467
{
 
468
    if (surface && surface->input_area_contains(from))
 
469
    {
 
470
        auto const top_left = surface->top_left();
 
471
        auto const surface_size = surface->size();
 
472
        auto const bottom_right = top_left + as_displacement(surface_size);
 
473
 
 
474
        auto movement = to - from;
 
475
 
 
476
        if (movement.dx < DeltaX{0})
 
477
            movement.dx = std::max(movement.dx, (bounds.top_left - top_left).dx);
 
478
 
 
479
        if (movement.dy < DeltaY{0})
 
480
            movement.dy = std::max(movement.dy, (bounds.top_left - top_left).dy);
 
481
 
 
482
        if (movement.dx > DeltaX{0})
 
483
            movement.dx = std::min(movement.dx, (bounds.bottom_right() - bottom_right).dx);
 
484
 
 
485
        if (movement.dy > DeltaY{0})
 
486
            movement.dy = std::min(movement.dy, (bounds.bottom_right() - bottom_right).dy);
 
487
 
 
488
        auto new_pos = surface->top_left() + movement;
 
489
 
 
490
        surface->move_to(new_pos);
 
491
        return true;
 
492
    }
 
493
 
 
494
    return false;
 
495
}
 
496
 
 
497
bool me::TilingWindowManagerPolicy::resize(std::shared_ptr<ms::Surface> surface, Point cursor, Point old_cursor, Rectangle bounds)
 
498
{
 
499
    if (surface && surface->input_area_contains(old_cursor))
 
500
    {
 
501
        auto const top_left = surface->top_left();
 
502
 
 
503
        auto const old_displacement = old_cursor - top_left;
 
504
        auto const new_displacement = cursor - top_left;
 
505
 
 
506
        auto const scale_x = new_displacement.dx.as_float()/std::max(1.0f, old_displacement.dx.as_float());
 
507
        auto const scale_y = new_displacement.dy.as_float()/std::max(1.0f, old_displacement.dy.as_float());
 
508
 
 
509
        if (scale_x <= 0.0f || scale_y <= 0.0f) return false;
 
510
 
 
511
        auto const old_size = surface->size();
 
512
        Size new_size{scale_x*old_size.width, scale_y*old_size.height};
 
513
 
 
514
        auto const size_limits = as_size(bounds.bottom_right() - top_left);
 
515
 
 
516
        if (new_size.width > size_limits.width)
 
517
            new_size.width = size_limits.width;
 
518
 
 
519
        if (new_size.height > size_limits.height)
 
520
            new_size.height = size_limits.height;
 
521
 
 
522
        surface->resize(new_size);
 
523
 
 
524
        return true;
 
525
    }
 
526
 
 
527
    return false;
 
528
}