kranewm

An ICCCM & EWMH compliant X11 reparenting, dynamic window manager, written in C++
git clone git://git.deurzen.net/kranewm
Log | Files | Refs | LICENSE

workspace.cc (11629B)


      1 #include "../winsys/util.hh"
      2 #include "context.hh"
      3 #include "cycle.t.hh"
      4 #include "workspace.hh"
      5 
      6 #include <algorithm>
      7 #include <optional>
      8 
      9 bool
     10 Buffer::is_occupied() const
     11 {
     12     return m_client;
     13 }
     14 
     15 
     16 Client_ptr
     17 Buffer::client() const
     18 {
     19     return m_client;
     20 }
     21 
     22 std::optional<winsys::Grip>
     23 Buffer::grip() const
     24 {
     25     return m_grip;
     26 }
     27 
     28 std::optional<winsys::Pos>
     29 Buffer::grip_pos() const
     30 {
     31     return m_grip_pos;
     32 }
     33 
     34 std::optional<winsys::Region>
     35 Buffer::client_region() const
     36 {
     37     return m_client_region;
     38 }
     39 
     40 
     41 void
     42 Buffer::set_grip_pos(winsys::Pos pos)
     43 {
     44     m_grip_pos = pos;
     45 }
     46 
     47 void
     48 Buffer::set_client_region(winsys::Region region)
     49 {
     50     m_client_region = region;
     51 }
     52 
     53 void
     54 Buffer::set(Client_ptr client, winsys::Grip grip, winsys::Pos pos, winsys::Region region)
     55 {
     56     m_client = client;
     57     m_grip = grip;
     58     m_grip_pos = pos;
     59     m_client_region = region;
     60 }
     61 
     62 void
     63 Buffer::unset()
     64 {
     65     m_client = nullptr;
     66     m_grip.reset();
     67     m_grip_pos.reset();
     68     m_client_region.reset();
     69 }
     70 
     71 
     72 bool
     73 Workspace::empty() const
     74 {
     75     return m_clients.empty();
     76 }
     77 
     78 bool
     79 Workspace::contains(Client_ptr client) const
     80 {
     81     return m_clients.contains(client);
     82 }
     83 
     84 
     85 bool
     86 Workspace::focus_follows_mouse() const
     87 {
     88     return m_focus_follows_mouse;
     89 }
     90 
     91 void
     92 Workspace::set_focus_follows_mouse(bool focus_follows_mouse)
     93 {
     94     m_focus_follows_mouse = focus_follows_mouse;
     95 }
     96 
     97 
     98 bool
     99 Workspace::layout_is_free() const
    100 {
    101     return m_layout_handler.layout_is_free();
    102 }
    103 
    104 bool
    105 Workspace::layout_has_margin() const
    106 {
    107     return m_layout_handler.layout_has_margin();
    108 }
    109 
    110 bool
    111 Workspace::layout_has_gap() const
    112 {
    113     return m_layout_handler.layout_has_gap();
    114 }
    115 
    116 bool
    117 Workspace::layout_is_persistent() const
    118 {
    119     return m_layout_handler.layout_is_persistent();
    120 }
    121 
    122 bool
    123 Workspace::layout_is_single() const
    124 {
    125     return m_layout_handler.layout_is_single();
    126 }
    127 
    128 bool
    129 Workspace::layout_wraps() const
    130 {
    131     return m_layout_handler.layout_wraps();
    132 }
    133 
    134 
    135 std::size_t
    136 Workspace::size() const
    137 {
    138     return m_clients.size();
    139 }
    140 
    141 std::size_t
    142 Workspace::length() const
    143 {
    144     return m_clients.length();
    145 }
    146 
    147 std::size_t
    148 Workspace::main_count() const
    149 {
    150     return m_layout_handler.main_count();
    151 }
    152 
    153 
    154 Context_ptr
    155 Workspace::context() const
    156 {
    157     return mp_context;
    158 }
    159 
    160 
    161 Index
    162 Workspace::index() const
    163 {
    164     return m_index;
    165 }
    166 
    167 std::string const&
    168 Workspace::name() const
    169 {
    170     return m_name;
    171 }
    172 
    173 std::string
    174 Workspace::identifier() const
    175 {
    176     if (!m_name.empty())
    177         return mp_context->name()
    178             + ":"
    179             + std::to_string(m_index)
    180             + ":"
    181             + m_name;
    182 
    183     return mp_context->name()
    184         + ":"
    185         + std::to_string(m_index);
    186 }
    187 
    188 Client_ptr
    189 Workspace::active() const
    190 {
    191     return mp_active;
    192 }
    193 
    194 
    195 Cycle<Client_ptr> const&
    196 Workspace::clients() const
    197 {
    198     return m_clients;
    199 }
    200 
    201 std::vector<Client_ptr>
    202 Workspace::stack_after_focus() const
    203 {
    204     std::vector<Client_ptr> stack = m_clients.stack();
    205 
    206     if (mp_active) {
    207         Util::erase_remove(stack, mp_active);
    208         stack.push_back(mp_active);
    209     }
    210 
    211     return stack;
    212 }
    213 
    214 
    215 Client_ptr
    216 Workspace::next_client() const
    217 {
    218     std::optional<Client_ptr> client
    219         = m_clients.next_element(winsys::Direction::Forward);
    220 
    221     if (client != mp_active)
    222         return *client;
    223 
    224     return nullptr;
    225 }
    226 
    227 Client_ptr
    228 Workspace::prev_client() const
    229 {
    230     std::optional<Client_ptr> client
    231         = m_clients.next_element(winsys::Direction::Backward);
    232 
    233     if (client != mp_active)
    234         return *client;
    235 
    236     return nullptr;
    237 }
    238 
    239 
    240 std::optional<Client_ptr>
    241 Workspace::find_client(ClientSelector const& selector) const
    242 {
    243     if (m_clients.empty())
    244         return std::nullopt;
    245 
    246     switch (selector.criterium()) {
    247     case ClientSelector::SelectionCriterium::AtFirst:
    248     {
    249         return m_clients[0];
    250     }
    251     case ClientSelector::SelectionCriterium::AtLast:
    252     {
    253         return m_clients[Util::last_index(m_clients.as_deque())];
    254     }
    255     case ClientSelector::SelectionCriterium::AtMain:
    256     {
    257         std::size_t main_count = m_layout_handler.main_count();
    258 
    259         if (main_count <= m_clients.size())
    260             return m_clients[main_count];
    261 
    262         break;
    263     }
    264     case ClientSelector::SelectionCriterium::AtIndex:
    265     {
    266         std::size_t index = selector.index();
    267 
    268         if (index <= m_clients.size())
    269             return m_clients[index];
    270 
    271         break;
    272     }
    273     }
    274 
    275     return std::nullopt;
    276 }
    277 
    278 
    279 void
    280 Workspace::cycle(winsys::Direction direction)
    281 {
    282     switch (direction) {
    283     case winsys::Direction::Forward:
    284     {
    285         if (!layout_wraps() && m_clients.active_index() == m_clients.last_index())
    286             return;
    287 
    288         break;
    289     }
    290     case winsys::Direction::Backward:
    291     {
    292         if (!layout_wraps() && m_clients.active_index() == 0)
    293             return;
    294 
    295         break;
    296     }
    297     }
    298 
    299     m_clients.cycle_active(direction);
    300     mp_active = m_clients.active_element().value_or(nullptr);
    301 }
    302 
    303 void
    304 Workspace::drag(winsys::Direction direction)
    305 {
    306     switch (direction) {
    307     case winsys::Direction::Forward:
    308     {
    309         if (!layout_wraps() && m_clients.active_index() == m_clients.last_index())
    310             return;
    311 
    312         break;
    313     }
    314     case winsys::Direction::Backward:
    315     {
    316         if (!layout_wraps() && m_clients.active_index() == 0)
    317             return;
    318 
    319         break;
    320     }
    321     }
    322 
    323     m_clients.drag_active(direction);
    324     mp_active = m_clients.active_element().value_or(nullptr);
    325 }
    326 
    327 void
    328 Workspace::reverse()
    329 {
    330     m_clients.reverse();
    331     mp_active = m_clients.active_element().value_or(nullptr);
    332 }
    333 
    334 void
    335 Workspace::rotate(winsys::Direction direction)
    336 {
    337     m_clients.rotate(direction);
    338     mp_active = m_clients.active_element().value_or(nullptr);
    339 }
    340 
    341 void
    342 Workspace::shuffle_main(winsys::Direction direction)
    343 {
    344     m_clients.rotate_range(direction, 0, m_layout_handler.main_count());
    345     mp_active = m_clients.active_element().value_or(nullptr);
    346 }
    347 
    348 void
    349 Workspace::shuffle_stack(winsys::Direction direction)
    350 {
    351     m_clients.rotate_range(direction, m_layout_handler.main_count(), m_clients.size());
    352     mp_active = m_clients.active_element().value_or(nullptr);
    353 }
    354 
    355 
    356 void
    357 Workspace::activate_client(Client_ptr client)
    358 {
    359     if (m_clients.contains(client)) {
    360         m_clients.activate_element(client);
    361         mp_active = client;
    362     }
    363 }
    364 
    365 
    366 void
    367 Workspace::add_client(Client_ptr client)
    368 {
    369     if (m_clients.contains(client))
    370         return;
    371 
    372     m_clients.insert_at_back(client);
    373     mp_active = client;
    374 }
    375 
    376 void
    377 Workspace::remove_client(Client_ptr client)
    378 {
    379     m_clients.remove_element(client);
    380     mp_active = m_clients.active_element().value_or(nullptr);
    381 }
    382 
    383 void
    384 Workspace::replace_client(Client_ptr client, Client_ptr replacement)
    385 {
    386     bool was_active
    387         = m_clients.active_element().value_or(nullptr) == client;
    388 
    389     m_clients.replace_element(client, replacement);
    390 
    391     if (was_active) {
    392         m_clients.activate_element(replacement);
    393         mp_active = replacement;
    394     }
    395 }
    396 
    397 
    398 void
    399 Workspace::client_to_icon(Client_ptr client)
    400 {
    401     if (m_clients.remove_element(client))
    402         m_icons.insert_at_back(client);
    403 
    404     mp_active = m_clients.active_element().value_or(nullptr);
    405 }
    406 
    407 void
    408 Workspace::icon_to_client(Client_ptr client)
    409 {
    410     if (m_icons.remove_element(client))
    411         m_clients.insert_at_back(client);
    412 
    413     mp_active = m_clients.active_element().value_or(nullptr);
    414 }
    415 
    416 void
    417 Workspace::add_icon(Client_ptr client)
    418 {
    419     if (m_icons.contains(client))
    420         return;
    421 
    422     m_icons.insert_at_back(client);
    423 }
    424 
    425 void
    426 Workspace::remove_icon(Client_ptr client)
    427 {
    428     m_icons.remove_element(client);
    429 }
    430 
    431 std::optional<Client_ptr>
    432 Workspace::pop_icon()
    433 {
    434     return m_icons.empty()
    435         ? std::nullopt
    436         : std::optional(m_icons[m_icons.size() - 1]);
    437 }
    438 
    439 
    440 void
    441 Workspace::client_to_disowned(Client_ptr client)
    442 {
    443     if (m_clients.remove_element(client))
    444         m_disowned.insert_at_back(client);
    445 
    446     mp_active = m_clients.active_element().value_or(nullptr);
    447 }
    448 
    449 void
    450 Workspace::disowned_to_client(Client_ptr client)
    451 {
    452     if (m_disowned.remove_element(client))
    453         m_clients.insert_at_back(client);
    454 
    455     mp_active = m_clients.active_element().value_or(nullptr);
    456 }
    457 
    458 void
    459 Workspace::add_disowned(Client_ptr client)
    460 {
    461     if (m_disowned.contains(client))
    462         return;
    463 
    464     m_disowned.insert_at_back(client);
    465 }
    466 
    467 void
    468 Workspace::remove_disowned(Client_ptr client)
    469 {
    470     m_disowned.remove_element(client);
    471 }
    472 
    473 
    474 void
    475 Workspace::save_layout(std::size_t number) const
    476 {
    477     m_layout_handler.save_layout(number);
    478 }
    479 
    480 void
    481 Workspace::load_layout(std::size_t number)
    482 {
    483     m_layout_handler.load_layout(number);
    484 }
    485 
    486 
    487 void
    488 Workspace::toggle_layout_data()
    489 {
    490     m_layout_handler.set_prev_layout_data();
    491 }
    492 
    493 void
    494 Workspace::cycle_layout_data(winsys::Direction direction)
    495 {
    496     m_layout_handler.cycle_layout_data(direction);
    497 }
    498 
    499 void
    500 Workspace::copy_data_from_prev_layout()
    501 {
    502     m_layout_handler.copy_data_from_prev_layout();
    503 }
    504 
    505 
    506 void
    507 Workspace::change_gap_size(Util::Change<int> change)
    508 {
    509     m_layout_handler.change_gap_size(change);
    510 }
    511 
    512 void
    513 Workspace::change_main_count(Util::Change<int> change)
    514 {
    515     m_layout_handler.change_main_count(change);
    516 }
    517 
    518 void
    519 Workspace::change_main_factor(Util::Change<float> change)
    520 {
    521     m_layout_handler.change_main_factor(change);
    522 }
    523 
    524 void
    525 Workspace::change_margin(Util::Change<int> change)
    526 {
    527     m_layout_handler.change_margin(change);
    528 }
    529 
    530 void
    531 Workspace::change_margin(winsys::Edge edge, Util::Change<int> change)
    532 {
    533     m_layout_handler.change_margin(edge, change);
    534 }
    535 
    536 void
    537 Workspace::reset_gap_size()
    538 {
    539     m_layout_handler.reset_gap_size();
    540 }
    541 
    542 void
    543 Workspace::reset_margin()
    544 {
    545     m_layout_handler.reset_margin();
    546 }
    547 
    548 void
    549 Workspace::reset_layout_data()
    550 {
    551     m_layout_handler.reset_layout_data();
    552 }
    553 
    554 
    555 void
    556 Workspace::toggle_layout()
    557 {
    558     m_layout_handler.set_prev_kind();
    559 }
    560 
    561 void
    562 Workspace::set_layout(LayoutHandler::LayoutKind layout)
    563 {
    564     m_layout_handler.set_kind(layout);
    565 }
    566 
    567 std::vector<Placement>
    568 Workspace::arrange(winsys::Region region) const
    569 {
    570     std::deque<Client_ptr> clients = m_clients.as_deque();
    571     std::vector<Placement> placements;
    572     placements.reserve(clients.size());
    573 
    574     auto fullscreen_iter = std::stable_partition(
    575         clients.begin(),
    576         clients.end(),
    577         [](const Client_ptr client) -> bool {
    578             return client->fullscreen && !client->contained;
    579         }
    580     );
    581 
    582     auto free_iter = std::stable_partition(
    583         fullscreen_iter,
    584         clients.end(),
    585         [=,this](const Client_ptr client) -> bool {
    586             return !layout_is_free() && Client::is_free(client);
    587         }
    588     );
    589 
    590     std::transform(
    591         clients.begin(),
    592         fullscreen_iter,
    593         std::back_inserter(placements),
    594         [region](const Client_ptr client) -> Placement {
    595             return Placement {
    596                 Placement::PlacementMethod::Tile,
    597                 client,
    598                 winsys::Decoration::NO_DECORATION,
    599                 region
    600             };
    601         }
    602     );
    603 
    604     std::transform(
    605         fullscreen_iter,
    606         free_iter,
    607         std::back_inserter(placements),
    608         [](const Client_ptr client) -> Placement {
    609             return Placement {
    610                 Placement::PlacementMethod::Free,
    611                 client,
    612                 winsys::Decoration::FREE_DECORATION,
    613                 client->free_region
    614             };
    615         }
    616     );
    617 
    618     m_layout_handler.arrange(
    619         region,
    620         placements,
    621         free_iter,
    622         clients.end()
    623     );
    624 
    625     if (layout_is_single()) {
    626         std::for_each(
    627             placements.begin(),
    628             placements.end(),
    629             [](Placement& placement) {
    630                 if (!placement.client->focused)
    631                     placement.region = std::nullopt;
    632             }
    633         );
    634     }
    635 
    636     return placements;
    637 }