kranewm

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

layout.cc (39663B)


      1 #include "../winsys/util.hh"
      2 #include "client.hh"
      3 #include "cycle.t.hh"
      4 #include "defaults.hh"
      5 #include "layout.hh"
      6 
      7 #include <algorithm>
      8 #include <cmath>
      9 #include <sstream>
     10 #include <fstream>
     11 
     12 using namespace winsys;
     13 
     14 LayoutHandler::Layout::Layout(LayoutKind kind)
     15     : kind(kind),
     16       config(kind_to_config(kind)),
     17       default_data(kind_to_default_data(kind)),
     18       data({}, true)
     19 {
     20     data.insert_at_back(new LayoutData(default_data));
     21     data.insert_at_back(new LayoutData(default_data));
     22     data.insert_at_back(new LayoutData(default_data));
     23 }
     24 
     25 LayoutHandler::Layout::~Layout()
     26 {
     27     for (LayoutData_ptr data : data)
     28         delete data;
     29 }
     30 
     31 
     32 LayoutHandler::LayoutHandler()
     33     : m_kind(LayoutKind::Float),
     34       m_prev_kind(LayoutKind::Float),
     35       m_layouts({
     36 #define NEW_LAYOUT(layout) { layout, new Layout(layout) }
     37           NEW_LAYOUT(LayoutKind::Float),
     38           NEW_LAYOUT(LayoutKind::FramelessFloat),
     39           NEW_LAYOUT(LayoutKind::SingleFloat),
     40           NEW_LAYOUT(LayoutKind::FramelessSingleFloat),
     41           NEW_LAYOUT(LayoutKind::Center),
     42           NEW_LAYOUT(LayoutKind::Monocle),
     43           NEW_LAYOUT(LayoutKind::MainDeck),
     44           NEW_LAYOUT(LayoutKind::StackDeck),
     45           NEW_LAYOUT(LayoutKind::DoubleDeck),
     46           NEW_LAYOUT(LayoutKind::Paper),
     47           NEW_LAYOUT(LayoutKind::CompactPaper),
     48           NEW_LAYOUT(LayoutKind::DoubleStack),
     49           NEW_LAYOUT(LayoutKind::CompactDoubleStack),
     50           NEW_LAYOUT(LayoutKind::HorizontalStack),
     51           NEW_LAYOUT(LayoutKind::CompactHorizontalStack),
     52           NEW_LAYOUT(LayoutKind::VerticalStack),
     53           NEW_LAYOUT(LayoutKind::CompactVerticalStack),
     54 #undef NEW_LAYOUT
     55       }),
     56       mp_layout(m_layouts.at(m_kind)),
     57       mp_prev_layout(m_layouts.at(m_kind))
     58 {}
     59 
     60 LayoutHandler::~LayoutHandler()
     61 {
     62     for (auto [_,layout] : m_layouts)
     63         delete layout;
     64 }
     65 
     66 
     67 void
     68 LayoutHandler::arrange(
     69     Region screen_region,
     70     placement_vector placements,
     71     client_iter begin,
     72     client_iter end
     73 ) const
     74 {
     75     if (mp_layout->config.margin) {
     76         Layout::LayoutData_ptr data = *mp_layout->data.active_element();
     77 
     78         screen_region.pos.x += data->margin.left;
     79         screen_region.pos.y += data->margin.top;
     80 
     81         screen_region.dim.w -= data->margin.left + data->margin.right;
     82         screen_region.dim.h -= data->margin.top + data->margin.bottom;
     83     }
     84 
     85     switch (m_kind) {
     86         case LayoutKind::Float:
     87         {
     88             arrange_float(screen_region, placements, begin, end);
     89             break;
     90         }
     91         case LayoutKind::FramelessFloat:
     92         {
     93             arrange_frameless_float(screen_region, placements, begin, end);
     94             break;
     95         }
     96         case LayoutKind::SingleFloat:
     97         {
     98             arrange_single_float(screen_region, placements, begin, end);
     99             break;
    100         }
    101         case LayoutKind::FramelessSingleFloat:
    102         {
    103             arrange_frameless_single_float(screen_region, placements, begin, end);
    104             break;
    105         }
    106         case LayoutKind::Center:
    107         {
    108             arrange_center(screen_region, placements, begin, end);
    109             break;
    110         }
    111         case LayoutKind::Monocle:
    112         {
    113             arrange_monocle(screen_region, placements, begin, end);
    114             break;
    115         }
    116         case LayoutKind::MainDeck:
    117         {
    118             arrange_main_deck(screen_region, placements, begin, end);
    119             break;
    120         }
    121         case LayoutKind::StackDeck:
    122         {
    123             arrange_stack_deck(screen_region, placements, begin, end);
    124             break;
    125         }
    126         case LayoutKind::DoubleDeck:
    127         {
    128             arrange_double_deck(screen_region, placements, begin, end);
    129             break;
    130         }
    131         case LayoutKind::Paper:
    132         {
    133             arrange_paper(screen_region, placements, begin, end);
    134             break;
    135         }
    136         case LayoutKind::CompactPaper:
    137         {
    138             arrange_compact_paper(screen_region, placements, begin, end);
    139             break;
    140         }
    141         case LayoutKind::DoubleStack:
    142         {
    143             arrange_double_stack(screen_region, placements, begin, end);
    144             break;
    145         }
    146         case LayoutKind::CompactDoubleStack:
    147         {
    148             arrange_compact_double_stack(screen_region, placements, begin, end);
    149             break;
    150         }
    151         case LayoutKind::HorizontalStack:
    152         {
    153             arrange_horizontal_stack(screen_region, placements, begin, end);
    154             break;
    155         }
    156         case LayoutKind::CompactHorizontalStack:
    157         {
    158             arrange_compact_horizontal_stack(screen_region, placements, begin, end);
    159             break;
    160         }
    161         case LayoutKind::VerticalStack:
    162         {
    163             arrange_vertical_stack(screen_region, placements, begin, end);
    164             break;
    165         }
    166         case LayoutKind::CompactVerticalStack:
    167         {
    168             arrange_compact_vertical_stack(screen_region, placements, begin, end);
    169             break;
    170         }
    171     }
    172 
    173     if (mp_layout->config.gap) {
    174         Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    175 
    176         std::for_each(
    177             placements.begin(),
    178             placements.end(),
    179             [data](Placement& placement) {
    180                 if (placement.region && !Client::is_free(placement.client)) {
    181                     placement.region->pos.x += data->gap_size;
    182                     placement.region->pos.y += data->gap_size;
    183                     placement.region->dim.w -= 2 * data->gap_size;
    184                     placement.region->dim.h -= 2 * data->gap_size;
    185 
    186                     placement.region->apply_minimum_dim(Client::MIN_CLIENT_DIM);
    187                 }
    188             }
    189         );
    190     }
    191 }
    192 
    193 
    194 LayoutHandler::LayoutKind
    195 LayoutHandler::kind() const
    196 {
    197     return m_kind;
    198 }
    199 
    200 void
    201 LayoutHandler::set_kind(LayoutKind kind)
    202 {
    203     if (kind == m_kind)
    204         return;
    205 
    206     m_prev_kind = m_kind;
    207     m_kind = kind;
    208 
    209     mp_prev_layout = mp_layout;
    210     mp_layout = *Util::const_retrieve(m_layouts, m_kind);
    211 }
    212 
    213 void
    214 LayoutHandler::set_prev_kind()
    215 {
    216     if (m_prev_kind == m_kind)
    217         return;
    218 
    219     std::swap(m_kind, m_prev_kind);
    220     std::swap(mp_layout, mp_prev_layout);
    221 }
    222 
    223 
    224 bool
    225 LayoutHandler::layout_is_free() const
    226 {
    227     return mp_layout->config.method == Placement::PlacementMethod::Free;
    228 }
    229 
    230 bool
    231 LayoutHandler::layout_has_margin() const
    232 {
    233     return mp_layout->config.margin;
    234 }
    235 
    236 bool
    237 LayoutHandler::layout_has_gap() const
    238 {
    239     return mp_layout->config.gap;
    240 }
    241 
    242 bool
    243 LayoutHandler::layout_is_persistent() const
    244 {
    245     return mp_layout->config.persistent;
    246 }
    247 
    248 bool
    249 LayoutHandler::layout_is_single() const
    250 {
    251     return mp_layout->config.single;
    252 }
    253 
    254 bool
    255 LayoutHandler::layout_wraps() const
    256 {
    257     return mp_layout->config.wraps;
    258 }
    259 
    260 
    261 std::size_t
    262 LayoutHandler::gap_size() const
    263 {
    264     return (*mp_layout->data.active_element())->gap_size;
    265 }
    266 
    267 std::size_t
    268 LayoutHandler::main_count() const
    269 {
    270     return (*mp_layout->data.active_element())->main_count;
    271 }
    272 
    273 float
    274 LayoutHandler::main_factor() const
    275 {
    276     return (*mp_layout->data.active_element())->main_factor;
    277 }
    278 
    279 Extents
    280 LayoutHandler::margin() const
    281 {
    282     return (*mp_layout->data.active_element())->margin;
    283 }
    284 
    285 
    286 void
    287 LayoutHandler::copy_data_from_prev_layout()
    288 {
    289     *(*mp_layout->data.active_element())
    290         = *(*mp_prev_layout->data.active_element());
    291 }
    292 
    293 void
    294 LayoutHandler::set_prev_layout_data()
    295 {
    296     std::optional<Layout::LayoutData_ptr> prev_data
    297         = mp_layout->data.prev_active_element();
    298 
    299     if (prev_data)
    300         mp_layout->data.activate_element(*prev_data);
    301 }
    302 
    303 
    304 void
    305 LayoutHandler::change_gap_size(Util::Change<int> change)
    306 {
    307     Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    308     int value = static_cast<int>(data->gap_size) + change;
    309 
    310     if (value <= 0)
    311         data->gap_size = 0;
    312     else if (static_cast<std::size_t>(value) >= Layout::LayoutData::MAX_GAP_SIZE)
    313         data->gap_size = Layout::LayoutData::MAX_GAP_SIZE;
    314     else
    315         data->gap_size = value;
    316 }
    317 
    318 void
    319 LayoutHandler::change_main_count(Util::Change<int> change)
    320 {
    321     Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    322     int value = static_cast<int>(data->main_count) + change;
    323 
    324     if (value <= 0)
    325         data->main_count = 0;
    326     else if (static_cast<std::size_t>(value) >= Layout::LayoutData::MAX_MAIN_COUNT)
    327         data->main_count = Layout::LayoutData::MAX_MAIN_COUNT;
    328     else
    329         data->main_count = value;
    330 }
    331 
    332 void
    333 LayoutHandler::change_main_factor(Util::Change<float> change)
    334 {
    335     Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    336     float value = data->main_factor + change;
    337 
    338     if (value <= 0.05f)
    339         data->main_factor = 0.05f;
    340     else if (value >= 0.95f)
    341         data->main_factor = 0.95f;
    342     else
    343         data->main_factor = value;
    344 }
    345 
    346 void
    347 LayoutHandler::change_margin(Util::Change<int> change)
    348 {
    349     change_margin(Edge::Left, change);
    350     change_margin(Edge::Top, change);
    351     change_margin(Edge::Right, change);
    352     change_margin(Edge::Bottom, change);
    353 }
    354 
    355 void
    356 LayoutHandler::change_margin(Edge edge, Util::Change<int> change)
    357 {
    358     Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    359     int* margin;
    360     const int* max_value;
    361 
    362     switch (edge) {
    363     case Edge::Left:
    364     {
    365         margin = &data->margin.left;
    366         max_value = &Layout::LayoutData::MAX_MARGIN.left;
    367         break;
    368     }
    369     case Edge::Top:
    370     {
    371         margin = &data->margin.top;
    372         max_value = &Layout::LayoutData::MAX_MARGIN.top;
    373         break;
    374     }
    375     case Edge::Right:
    376     {
    377         margin = &data->margin.right;
    378         max_value = &Layout::LayoutData::MAX_MARGIN.right;
    379         break;
    380     }
    381     case Edge::Bottom:
    382     {
    383         margin = &data->margin.bottom;
    384         max_value = &Layout::LayoutData::MAX_MARGIN.bottom;
    385         break;
    386     }
    387     }
    388 
    389     int value = *margin + change;
    390 
    391     if (value <= 0)
    392         *margin = 0;
    393     else if (value >= *max_value)
    394         *margin = *max_value;
    395     else
    396         *margin = value;
    397 }
    398 
    399 void
    400 LayoutHandler::reset_gap_size()
    401 {
    402     (*mp_layout->data.active_element())->gap_size
    403         = mp_layout->default_data.gap_size;
    404 }
    405 
    406 void
    407 LayoutHandler::reset_margin()
    408 {
    409     (*mp_layout->data.active_element())->margin
    410         = mp_layout->default_data.margin;
    411 }
    412 
    413 void
    414 LayoutHandler::reset_layout_data()
    415 {
    416     *(*mp_layout->data.active_element())
    417         = mp_layout->default_data;
    418 }
    419 
    420 void
    421 LayoutHandler::cycle_layout_data(Direction direction)
    422 {
    423     mp_layout->data.cycle_active(direction);
    424 }
    425 
    426 
    427 void
    428 LayoutHandler::save_layout(std::size_t number) const
    429 {
    430     std::stringstream datadir_ss;
    431     std::string home_path = std::getenv("HOME");
    432 
    433     if (const char* env_xdgdata = std::getenv("XDG_DATA_HOME"))
    434         datadir_ss << env_xdgdata << "/" << WM_NAME << "/";
    435     else
    436         datadir_ss << home_path << "/.local/share/" << WM_NAME << "/"
    437                    << "layout_" << number;
    438 
    439     std::string file_path = datadir_ss.str();
    440 
    441     std::vector<Layout::LayoutData> data;
    442     data.reserve(mp_layout->data.size());
    443     for (Layout::LayoutData_ptr data_ptr : mp_layout->data.as_deque())
    444         data.push_back(*data_ptr);
    445 
    446     typename std::vector<Layout::LayoutData>::size_type size
    447         = data.size();
    448 
    449     std::ofstream out(file_path, std::ios::out | std::ios::binary);
    450     out.write(reinterpret_cast<const char*>(&m_kind), sizeof(LayoutKind));
    451     out.write(reinterpret_cast<const char*>(&size), sizeof(size));
    452     out.write(reinterpret_cast<const char*>(&data[0]),
    453         data.size() * sizeof(Layout::LayoutData));
    454     out.close();
    455 }
    456 
    457 void
    458 LayoutHandler::load_layout(std::size_t number)
    459 {
    460     std::stringstream datadir_ss;
    461     std::string home_path = std::getenv("HOME");
    462 
    463     if (const char* env_xdgdata = std::getenv("XDG_DATA_HOME"))
    464         datadir_ss << env_xdgdata << "/" << WM_NAME << "/";
    465     else
    466         datadir_ss << home_path << "/.local/share/" << WM_NAME << "/"
    467                    << "layout_" << number;
    468 
    469     std::string file_path = datadir_ss.str();
    470 
    471     LayoutKind kind;
    472     std::vector<Layout::LayoutData> data;
    473     typename std::vector<Layout::LayoutData>::size_type size = 0;
    474 
    475     std::ifstream in(file_path, std::ios::in | std::ios::binary);
    476     if (in.good()) {
    477         in.read(reinterpret_cast<char*>(&kind), sizeof(LayoutKind));
    478         in.read(reinterpret_cast<char*>(&size), sizeof(size));
    479 
    480         data.resize(size, mp_layout->default_data);
    481         in.read(reinterpret_cast<char*>(&data[0]),
    482             data.size() * sizeof(Layout::LayoutData));
    483 
    484         set_kind(kind);
    485         for (Layout::LayoutData_ptr data : mp_layout->data)
    486             delete data;
    487 
    488         mp_layout->data.clear();
    489         for (auto data_ : data)
    490             mp_layout->data.insert_at_back(new Layout::LayoutData(data_));
    491     }
    492     in.close();
    493 }
    494 
    495 
    496 void
    497 LayoutHandler::arrange_float(
    498     Region,
    499     placement_vector placements,
    500     client_iter begin,
    501     client_iter end
    502 ) const
    503 {
    504     std::transform(
    505         begin,
    506         end,
    507         std::back_inserter(placements),
    508         [this](Client_ptr client) -> Placement {
    509             return Placement {
    510                 mp_layout->config.method,
    511                 client,
    512                 mp_layout->config.decoration,
    513                 client->free_region
    514             };
    515         }
    516     );
    517 }
    518 
    519 void
    520 LayoutHandler::arrange_frameless_float(
    521     Region screen_region,
    522     placement_vector placements,
    523     client_iter begin,
    524     client_iter end
    525 ) const
    526 {
    527     arrange_float(screen_region, placements, begin, end);
    528 }
    529 
    530 void
    531 LayoutHandler::arrange_single_float(
    532     Region,
    533     placement_vector placements,
    534     client_iter begin,
    535     client_iter end
    536 ) const
    537 {
    538     std::transform(
    539         begin,
    540         end,
    541         std::back_inserter(placements),
    542         [this](Client_ptr client) -> Placement {
    543             return Placement {
    544                 mp_layout->config.method,
    545                 client,
    546                 mp_layout->config.decoration,
    547                 client->focused ? std::optional(client->free_region) : std::nullopt
    548             };
    549         }
    550     );
    551 }
    552 
    553 void
    554 LayoutHandler::arrange_frameless_single_float(
    555     Region screen_region,
    556     placement_vector placements,
    557     client_iter begin,
    558     client_iter end
    559 ) const
    560 {
    561     arrange_single_float(screen_region, placements, begin, end);
    562 }
    563 
    564 void
    565 LayoutHandler::arrange_center(
    566     Region screen_region,
    567     placement_vector placements,
    568     client_iter begin,
    569     client_iter end
    570 ) const
    571 {
    572     const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    573 
    574     std::size_t h_comp = Layout::LayoutData::MAX_MAIN_COUNT;
    575     float w_ratio = data->main_factor / 0.95;
    576     float h_ratio = static_cast<float>((h_comp - data->main_count))
    577         / static_cast<float>(h_comp);
    578 
    579     std::transform(
    580         begin,
    581         end,
    582         std::back_inserter(placements),
    583         [=,this](Client_ptr client) -> Placement {
    584             Region region = screen_region;
    585 
    586             int w = region.dim.w * w_ratio;
    587             int h = region.dim.h * h_ratio;
    588 
    589             if (w <= region.dim.w)
    590                 region.pos.x += (region.dim.w - w) / 2.f;
    591 
    592             if (h <= region.dim.h)
    593                 region.pos.y += (region.dim.h - h) / 2.f;
    594 
    595             region.dim = { w, h };
    596 
    597             return Placement {
    598                 mp_layout->config.method,
    599                 client,
    600                 mp_layout->config.decoration,
    601                 region
    602             };
    603         }
    604     );
    605 }
    606 
    607 void
    608 LayoutHandler::arrange_monocle(
    609     Region screen_region,
    610     placement_vector placements,
    611     client_iter begin,
    612     client_iter end
    613 ) const
    614 {
    615     std::transform(
    616         begin,
    617         end,
    618         std::back_inserter(placements),
    619         [=,this](Client_ptr client) {
    620             return Placement {
    621                 mp_layout->config.method,
    622                 client,
    623                 mp_layout->config.decoration,
    624                 screen_region
    625             };
    626         }
    627     );
    628 }
    629 
    630 void
    631 LayoutHandler::arrange_main_deck(
    632     Region screen_region,
    633     placement_vector placements,
    634     client_iter begin,
    635     client_iter end
    636 ) const
    637 {
    638     const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    639     std::size_t n = end - begin;
    640 
    641     if (n == 1) {
    642         placements.emplace_back(Placement {
    643             mp_layout->config.method,
    644             *begin,
    645             Decoration::NO_DECORATION,
    646             screen_region
    647         });
    648 
    649         return;
    650     }
    651 
    652     std::size_t n_main;
    653     std::size_t n_stack;
    654 
    655     if (n <= data->main_count) {
    656         n_main = n;
    657         n_stack = 0;
    658     } else {
    659         n_main = data->main_count;
    660         n_stack = n - n_main;
    661     }
    662 
    663     int w_main
    664         = data->main_count > 0
    665         ? static_cast<float>(screen_region.dim.w) * data->main_factor
    666         : 0;
    667 
    668     int x_stack = screen_region.pos.x + w_main;
    669     int w_stack = screen_region.dim.w - w_main;
    670     int h_main = n_main > 0 ? screen_region.dim.h : 0;
    671     int h_stack = n_stack > 0 ? screen_region.dim.h / n_stack : 0;
    672 
    673     std::size_t i = 0;
    674 
    675     std::transform(
    676         begin,
    677         end,
    678         std::back_inserter(placements),
    679         [=,this,&i](Client_ptr client) -> Placement {
    680             if (i < data->main_count) {
    681                 ++i;
    682                 return Placement {
    683                     mp_layout->config.method,
    684                     client,
    685                     mp_layout->config.decoration,
    686                     Region {
    687                         Pos {
    688                             screen_region.pos.x,
    689                             screen_region.pos.y
    690                         },
    691                         Dim {
    692                             n_stack == 0 ? screen_region.dim.w : w_main,
    693                             h_main
    694                         }
    695                     }
    696                 };
    697             } else {
    698                 return Placement {
    699                     mp_layout->config.method,
    700                     client,
    701                     mp_layout->config.decoration,
    702                     Region {
    703                         Pos {
    704                             x_stack,
    705                             screen_region.pos.y
    706                                 + static_cast<int>((i++ - data->main_count) * h_stack)
    707                         },
    708                         Dim {
    709                             w_stack,
    710                             h_stack
    711                         }
    712                     }
    713                 };
    714             }
    715         }
    716     );
    717 }
    718 
    719 void
    720 LayoutHandler::arrange_stack_deck(
    721     Region screen_region,
    722     placement_vector placements,
    723     client_iter begin,
    724     client_iter end
    725 ) const
    726 {
    727     const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    728     std::size_t n = end - begin;
    729 
    730     if (n == 1) {
    731         placements.emplace_back(Placement {
    732             mp_layout->config.method,
    733             *begin,
    734             Decoration::NO_DECORATION,
    735             screen_region
    736         });
    737 
    738         return;
    739     }
    740 
    741     std::size_t n_main;
    742     std::size_t n_stack;
    743 
    744     if (n <= data->main_count) {
    745         n_main = n;
    746         n_stack = 0;
    747     } else {
    748         n_main = data->main_count;
    749         n_stack = n - n_main;
    750     }
    751 
    752     int w_main
    753         = data->main_count > 0
    754         ? static_cast<float>(screen_region.dim.w) * data->main_factor
    755         : 0;
    756 
    757     int x_stack = screen_region.pos.x + w_main;
    758     int w_stack = screen_region.dim.w - w_main;
    759     int h_main = n_main > 0 ? screen_region.dim.h / n_main : 0;
    760     int h_stack = n_stack > 0 ? screen_region.dim.h : 0;
    761 
    762     std::size_t i = 0;
    763 
    764     std::transform(
    765         begin,
    766         end,
    767         std::back_inserter(placements),
    768         [=,this,&i](Client_ptr client) -> Placement {
    769             if (i < data->main_count) {
    770                 return Placement {
    771                     mp_layout->config.method,
    772                     client,
    773                     mp_layout->config.decoration,
    774                     Region {
    775                         Pos {
    776                             screen_region.pos.x,
    777                             screen_region.pos.y
    778                                 + static_cast<int>(i++) * h_main
    779                         },
    780                         Dim {
    781                             n_stack == 0 ? screen_region.dim.w : w_main,
    782                             h_main
    783                         }
    784                     }
    785                 };
    786             } else {
    787                 ++i;
    788                 return Placement {
    789                     mp_layout->config.method,
    790                     client,
    791                     mp_layout->config.decoration,
    792                     Region {
    793                         Pos {
    794                             x_stack,
    795                             screen_region.pos.y
    796                         },
    797                         Dim {
    798                             w_stack,
    799                             h_stack
    800                         }
    801                     }
    802                 };
    803             }
    804         }
    805     );
    806 }
    807 
    808 void
    809 LayoutHandler::arrange_double_deck(
    810     Region screen_region,
    811     placement_vector placements,
    812     client_iter begin,
    813     client_iter end
    814 ) const
    815 {
    816     const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    817     std::size_t n = end - begin;
    818 
    819     if (n == 1) {
    820         placements.emplace_back(Placement {
    821             mp_layout->config.method,
    822             *begin,
    823             Decoration::NO_DECORATION,
    824             screen_region
    825         });
    826 
    827         return;
    828     }
    829 
    830     std::size_t n_main;
    831     std::size_t n_stack;
    832 
    833     if (n <= data->main_count) {
    834         n_main = n;
    835         n_stack = 0;
    836     } else {
    837         n_main = data->main_count;
    838         n_stack = n - n_main;
    839     }
    840 
    841     int w_main
    842         = data->main_count > 0
    843         ? static_cast<float>(screen_region.dim.w) * data->main_factor
    844         : 0;
    845 
    846     int x_stack = screen_region.pos.x + w_main;
    847     int w_stack = screen_region.dim.w - w_main;
    848     int h_main = n_main > 0 ? screen_region.dim.h : 0;
    849     int h_stack = n_stack > 0 ? screen_region.dim.h : 0;
    850 
    851     std::size_t i = 0;
    852 
    853     std::transform(
    854         begin,
    855         end,
    856         std::back_inserter(placements),
    857         [=,this,&i](Client_ptr client) -> Placement {
    858             if (i++ < data->main_count) {
    859                 return Placement {
    860                     mp_layout->config.method,
    861                     client,
    862                     mp_layout->config.decoration,
    863                     Region {
    864                         Pos {
    865                             screen_region.pos.x,
    866                             screen_region.pos.y
    867                         },
    868                         Dim {
    869                             n_stack == 0 ? screen_region.dim.w : w_main,
    870                             h_main
    871                         }
    872                     }
    873                 };
    874             } else {
    875                 return Placement {
    876                     mp_layout->config.method,
    877                     client,
    878                     mp_layout->config.decoration,
    879                     Region {
    880                         Pos {
    881                             x_stack,
    882                             screen_region.pos.y
    883                         },
    884                         Dim {
    885                             w_stack,
    886                             h_stack
    887                         }
    888                     }
    889                 };
    890             }
    891         }
    892     );
    893 }
    894 
    895 void
    896 LayoutHandler::arrange_paper(
    897     Region screen_region,
    898     placement_vector placements,
    899     client_iter begin,
    900     client_iter end
    901 ) const
    902 {
    903     static const float MIN_W_RATIO = 0.5;
    904 
    905     const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
    906     std::size_t n = end - begin;
    907 
    908     if (n == 1) {
    909         placements.emplace_back(Placement {
    910             mp_layout->config.method,
    911             *begin,
    912             Decoration::NO_DECORATION,
    913             screen_region
    914         });
    915 
    916         return;
    917     }
    918 
    919     int cw;
    920     if (data->main_factor > MIN_W_RATIO) {
    921         cw = screen_region.dim.w * data->main_factor;
    922     } else {
    923         cw = screen_region.dim.w * MIN_W_RATIO;
    924     }
    925 
    926     int w = static_cast<float>(screen_region.dim.w - cw)
    927         / static_cast<float>(n - 1);
    928 
    929     bool contains_active = false;
    930     const auto last_active = std::max_element(
    931         begin,
    932         end,
    933         [&contains_active](const Client_ptr lhs, const Client_ptr rhs) {
    934             if (lhs->focused) {
    935                 contains_active = true;
    936                 return false;
    937             } else if (rhs->focused) {
    938                 contains_active = true;
    939                 return true;
    940             }
    941 
    942             return lhs->last_focused < rhs->last_focused;
    943         }
    944     );
    945 
    946     bool after_active = false;
    947     std::size_t i = 0;
    948 
    949     std::transform(
    950         begin,
    951         end,
    952         std::back_inserter(placements),
    953         [=,this,&after_active,&i](Client_ptr client) -> Placement {
    954             int x = screen_region.pos.x + static_cast<int>(i++ * w);
    955 
    956             if ((!contains_active && *last_active == client) || client->focused) {
    957                 after_active = true;
    958 
    959                 return Placement {
    960                     mp_layout->config.method,
    961                     client,
    962                     mp_layout->config.decoration,
    963                     Region {
    964                         Pos {
    965                             x,
    966                             screen_region.pos.y
    967                         },
    968                         Dim {
    969                             cw,
    970                             screen_region.dim.h
    971                         }
    972                     }
    973                 };
    974             } else {
    975                 if (after_active)
    976                     x += cw - w;
    977 
    978                 return Placement {
    979                     mp_layout->config.method,
    980                     client,
    981                     mp_layout->config.decoration,
    982                     Region {
    983                         Pos {
    984                             x,
    985                             screen_region.pos.y
    986                         },
    987                         Dim {
    988                             w,
    989                             screen_region.dim.h
    990                         }
    991                     }
    992                 };
    993             }
    994         }
    995     );
    996 }
    997 
    998 void
    999 LayoutHandler::arrange_compact_paper(
   1000     Region screen_region,
   1001     placement_vector placements,
   1002     client_iter begin,
   1003     client_iter end
   1004 ) const
   1005 {
   1006     arrange_paper(screen_region, placements, begin, end);
   1007 }
   1008 
   1009 void
   1010 LayoutHandler::arrange_double_stack(
   1011     Region screen_region,
   1012     placement_vector placements,
   1013     client_iter begin,
   1014     client_iter end
   1015 ) const
   1016 {
   1017     const Layout::LayoutData_ptr data = *mp_layout->data.active_element();
   1018     std::size_t n = end - begin;
   1019 
   1020     if (n == 1) {
   1021         placements.emplace_back(Placement {
   1022             mp_layout->config.method,
   1023             *begin,
   1024             Decoration::NO_DECORATION,
   1025             screen_region
   1026         });
   1027 
   1028         return;
   1029     }
   1030 
   1031     std::size_t n_main;
   1032     std::size_t n_stack;
   1033 
   1034     if (n <= data->main_count) {
   1035         n_main = n;
   1036         n_stack = 0;
   1037     } else {
   1038         n_main = data->main_count;
   1039         n_stack = n - n_main;
   1040     }
   1041 
   1042     int w_main
   1043         = data->main_count > 0
   1044         ? static_cast<float>(screen_region.dim.w) * data->main_factor
   1045         : 0;
   1046 
   1047     int x_stack = screen_region.pos.x + w_main;
   1048     int w_stack = screen_region.dim.w - w_main;
   1049     int h_main = n_main > 0 ? screen_region.dim.h / n_main : 0;
   1050     int h_stack = n_stack > 0 ? screen_region.dim.h / n_stack : 0;
   1051 
   1052     std::size_t i = 0;
   1053 
   1054     std::transform(
   1055         begin,
   1056         end,
   1057         std::back_inserter(placements),
   1058         [=,this,&i](Client_ptr client) -> Placement {
   1059             if (i < data->main_count) {
   1060                 return Placement {
   1061                     mp_layout->config.method,
   1062                     client,
   1063                     mp_layout->config.decoration,
   1064                     Region {
   1065                         Pos {
   1066                             screen_region.pos.x,
   1067                             screen_region.pos.y
   1068                                 + static_cast<int>(i++) * h_main
   1069                         },
   1070                         Dim {
   1071                             n_stack == 0 ? screen_region.dim.w : w_main,
   1072                             h_main
   1073                         }
   1074                     }
   1075                 };
   1076             } else {
   1077                 return Placement {
   1078                     mp_layout->config.method,
   1079                     client,
   1080                     mp_layout->config.decoration,
   1081                     Region {
   1082                         Pos {
   1083                             x_stack,
   1084                             screen_region.pos.y
   1085                                 + static_cast<int>((i++ - data->main_count) * h_stack)
   1086                         },
   1087                         Dim {
   1088                             w_stack,
   1089                             h_stack
   1090                         }
   1091                     }
   1092                 };
   1093             }
   1094         }
   1095     );
   1096 }
   1097 
   1098 void
   1099 LayoutHandler::arrange_compact_double_stack(
   1100     Region screen_region,
   1101     placement_vector placements,
   1102     client_iter begin,
   1103     client_iter end
   1104 ) const
   1105 {
   1106     arrange_double_stack(screen_region, placements, begin, end);
   1107 }
   1108 
   1109 void
   1110 LayoutHandler::arrange_horizontal_stack(
   1111     Region screen_region,
   1112     placement_vector placements,
   1113     client_iter begin,
   1114     client_iter end
   1115 ) const
   1116 {
   1117     std::size_t n = end - begin;
   1118 
   1119     if (n == 1) {
   1120         placements.emplace_back(Placement {
   1121             mp_layout->config.method,
   1122             *begin,
   1123             Decoration::NO_DECORATION,
   1124             screen_region
   1125         });
   1126 
   1127         return;
   1128     }
   1129 
   1130     int w = std::lround(static_cast<float>(screen_region.dim.w) / n);
   1131     std::size_t i = 0;
   1132 
   1133     std::transform(
   1134         begin,
   1135         end,
   1136         std::back_inserter(placements),
   1137         [=,this,&i](Client_ptr client) -> Placement {
   1138             return Placement {
   1139                 mp_layout->config.method,
   1140                 client,
   1141                 mp_layout->config.decoration,
   1142                 Region {
   1143                     Pos {
   1144                         screen_region.pos.x + static_cast<int>(i++ * w),
   1145                         screen_region.pos.y
   1146                     },
   1147                     Dim {
   1148                         w,
   1149                         screen_region.dim.h
   1150                     }
   1151                 }
   1152             };
   1153         }
   1154     );
   1155 }
   1156 
   1157 void
   1158 LayoutHandler::arrange_compact_horizontal_stack(
   1159     Region screen_region,
   1160     placement_vector placements,
   1161     client_iter begin,
   1162     client_iter end
   1163 ) const
   1164 {
   1165     arrange_horizontal_stack(screen_region, placements, begin, end);
   1166 }
   1167 
   1168 void
   1169 LayoutHandler::arrange_vertical_stack(
   1170     Region screen_region,
   1171     placement_vector placements,
   1172     client_iter begin,
   1173     client_iter end
   1174 ) const
   1175 {
   1176     std::size_t n = end - begin;
   1177 
   1178     if (n == 1) {
   1179         placements.emplace_back(Placement {
   1180             mp_layout->config.method,
   1181             *begin,
   1182             Decoration::NO_DECORATION,
   1183             screen_region
   1184         });
   1185 
   1186         return;
   1187     }
   1188 
   1189     int h = std::lround(static_cast<float>(screen_region.dim.h) / n);
   1190     std::size_t i = 0;
   1191 
   1192     std::transform(
   1193         begin,
   1194         end,
   1195         std::back_inserter(placements),
   1196         [=,this,&i](Client_ptr client) -> Placement {
   1197             return Placement {
   1198                 mp_layout->config.method,
   1199                 client,
   1200                 mp_layout->config.decoration,
   1201                 Region {
   1202                     Pos {
   1203                         screen_region.pos.x,
   1204                         screen_region.pos.y + static_cast<int>(i++ * h)
   1205                     },
   1206                     Dim {
   1207                         screen_region.dim.w,
   1208                         h
   1209                     }
   1210                 }
   1211             };
   1212         }
   1213     );
   1214 }
   1215 
   1216 void
   1217 LayoutHandler::arrange_compact_vertical_stack(
   1218     Region screen_region,
   1219     placement_vector placements,
   1220     client_iter begin,
   1221     client_iter end
   1222 ) const
   1223 {
   1224     arrange_vertical_stack(screen_region, placements, begin, end);
   1225 }
   1226 
   1227 
   1228 LayoutHandler::Layout::LayoutConfig
   1229 LayoutHandler::Layout::kind_to_config(LayoutKind kind)
   1230 {
   1231     switch (kind) {
   1232     case LayoutKind::Float:
   1233     {
   1234         return LayoutConfig {
   1235             Placement::PlacementMethod::Free,
   1236             Decoration::FREE_DECORATION,
   1237             false,
   1238             false,
   1239             false,
   1240             false,
   1241             true
   1242         };
   1243     }
   1244     case LayoutKind::FramelessFloat:
   1245     {
   1246         return LayoutConfig {
   1247             Placement::PlacementMethod::Free,
   1248             Decoration::NO_DECORATION,
   1249             false,
   1250             false,
   1251             false,
   1252             false,
   1253             true
   1254         };
   1255     }
   1256     case LayoutKind::SingleFloat:
   1257     {
   1258         return LayoutConfig {
   1259             Placement::PlacementMethod::Free,
   1260             Decoration::FREE_DECORATION,
   1261             false,
   1262             false,
   1263             true,
   1264             true,
   1265             true
   1266         };
   1267     }
   1268     case LayoutKind::FramelessSingleFloat:
   1269     {
   1270         return LayoutConfig {
   1271             Placement::PlacementMethod::Free,
   1272             Decoration::NO_DECORATION,
   1273             false,
   1274             false,
   1275             true,
   1276             true,
   1277             true
   1278         };
   1279     }
   1280     case LayoutKind::Center:
   1281     {
   1282         return LayoutConfig {
   1283             Placement::PlacementMethod::Tile,
   1284             Decoration::NO_DECORATION,
   1285             true,
   1286             true,
   1287             false,
   1288             false,
   1289             true
   1290         };
   1291     }
   1292     case LayoutKind::Monocle:
   1293     {
   1294         return LayoutConfig {
   1295             Placement::PlacementMethod::Tile,
   1296             Decoration::NO_DECORATION,
   1297             true,
   1298             true,
   1299             false,
   1300             false,
   1301             true
   1302         };
   1303     }
   1304     case LayoutKind::MainDeck:
   1305     {
   1306         return LayoutConfig {
   1307             Placement::PlacementMethod::Tile,
   1308             Decoration {
   1309                 std::nullopt,
   1310                 Frame {
   1311                     Extents { 0, 0, 3, 0 },
   1312                     ColorScheme::DEFAULT_COLOR_SCHEME
   1313                 }
   1314             },
   1315             true,
   1316             true,
   1317             false,
   1318             false,
   1319             true
   1320         };
   1321     }
   1322     case LayoutKind::StackDeck:
   1323     {
   1324         return LayoutConfig {
   1325             Placement::PlacementMethod::Tile,
   1326             Decoration {
   1327                 std::nullopt,
   1328                 Frame {
   1329                     Extents { 0, 0, 3, 0 },
   1330                     ColorScheme::DEFAULT_COLOR_SCHEME
   1331                 }
   1332             },
   1333             true,
   1334             true,
   1335             false,
   1336             false,
   1337             true
   1338         };
   1339     }
   1340     case LayoutKind::DoubleDeck:
   1341     {
   1342         return LayoutConfig {
   1343             Placement::PlacementMethod::Tile,
   1344             Decoration {
   1345                 std::nullopt,
   1346                 Frame {
   1347                     Extents { 0, 0, 3, 0 },
   1348                     ColorScheme::DEFAULT_COLOR_SCHEME
   1349                 }
   1350             },
   1351             true,
   1352             true,
   1353             false,
   1354             false,
   1355             true
   1356         };
   1357     }
   1358     case LayoutKind::Paper:
   1359     {
   1360         return LayoutConfig {
   1361             Placement::PlacementMethod::Tile,
   1362             Decoration {
   1363                 std::nullopt,
   1364                 Frame {
   1365                     Extents { 1, 1, 0, 0 },
   1366                     ColorScheme::DEFAULT_COLOR_SCHEME
   1367                 }
   1368             },
   1369             true,
   1370             true,
   1371             true,
   1372             false,
   1373             false
   1374         };
   1375     }
   1376     case LayoutKind::CompactPaper:
   1377     {
   1378         return LayoutConfig {
   1379             Placement::PlacementMethod::Tile,
   1380             Decoration {
   1381                 std::nullopt,
   1382                 Frame {
   1383                     Extents { 1, 1, 0, 0 },
   1384                     ColorScheme::DEFAULT_COLOR_SCHEME
   1385                 }
   1386             },
   1387             true,
   1388             false,
   1389             true,
   1390             false,
   1391             false
   1392         };
   1393     }
   1394     case LayoutKind::DoubleStack:
   1395     {
   1396         return LayoutConfig {
   1397             Placement::PlacementMethod::Tile,
   1398             Decoration {
   1399                 std::nullopt,
   1400                 Frame {
   1401                     Extents { 0, 0, 3, 0 },
   1402                     ColorScheme::DEFAULT_COLOR_SCHEME
   1403                 }
   1404             },
   1405             true,
   1406             true,
   1407             false,
   1408             false,
   1409             true
   1410         };
   1411     }
   1412     case LayoutKind::CompactDoubleStack:
   1413     {
   1414         return LayoutConfig {
   1415             Placement::PlacementMethod::Tile,
   1416             Decoration {
   1417                 std::nullopt,
   1418                 Frame {
   1419                     Extents { 0, 0, 3, 0 },
   1420                     ColorScheme::DEFAULT_COLOR_SCHEME
   1421                 }
   1422             },
   1423             true,
   1424             false,
   1425             false,
   1426             false,
   1427             true
   1428         };
   1429     }
   1430     case LayoutKind::HorizontalStack:
   1431     {
   1432         return LayoutConfig {
   1433             Placement::PlacementMethod::Tile,
   1434             Decoration {
   1435                 std::nullopt,
   1436                 Frame {
   1437                     Extents { 0, 0, 3, 0 },
   1438                     ColorScheme::DEFAULT_COLOR_SCHEME
   1439                 }
   1440             },
   1441             true,
   1442             true,
   1443             false,
   1444             false,
   1445             true
   1446         };
   1447     }
   1448     case LayoutKind::CompactHorizontalStack:
   1449     {
   1450         return LayoutConfig {
   1451             Placement::PlacementMethod::Tile,
   1452             Decoration {
   1453                 std::nullopt,
   1454                 Frame {
   1455                     Extents { 0, 0, 3, 0 },
   1456                     ColorScheme::DEFAULT_COLOR_SCHEME
   1457                 }
   1458             },
   1459             true,
   1460             false,
   1461             false,
   1462             false,
   1463             true
   1464         };
   1465     }
   1466     case LayoutKind::VerticalStack:
   1467     {
   1468         return LayoutConfig {
   1469             Placement::PlacementMethod::Tile,
   1470             Decoration {
   1471                 std::nullopt,
   1472                 Frame {
   1473                     Extents { 3, 0, 0, 0 },
   1474                     ColorScheme::DEFAULT_COLOR_SCHEME
   1475                 }
   1476             },
   1477             true,
   1478             true,
   1479             false,
   1480             false,
   1481             true
   1482         };
   1483     }
   1484     case LayoutKind::CompactVerticalStack:
   1485     {
   1486         return LayoutConfig {
   1487             Placement::PlacementMethod::Tile,
   1488             Decoration {
   1489                 std::nullopt,
   1490                 Frame {
   1491                     Extents { 3, 0, 0, 0 },
   1492                     ColorScheme::DEFAULT_COLOR_SCHEME
   1493                 }
   1494             },
   1495             true,
   1496             false,
   1497             false,
   1498             false,
   1499             true
   1500         };
   1501     }
   1502     default: Util::die("no associated configuration defined");
   1503     }
   1504 
   1505     return kind_to_config(LayoutKind::Float);
   1506 }
   1507 
   1508 LayoutHandler::Layout::LayoutData
   1509 LayoutHandler::Layout::kind_to_default_data(LayoutKind kind)
   1510 {
   1511     switch (kind) {
   1512     case LayoutKind::Center:
   1513     {
   1514         return Layout::LayoutData {
   1515             Extents { 0, 0, 0, 0 },
   1516             0,
   1517             5,
   1518             .40f
   1519         };
   1520     }
   1521     case LayoutKind::Float:                  // fallthrough
   1522     case LayoutKind::FramelessFloat:         // fallthrough
   1523     case LayoutKind::SingleFloat:            // fallthrough
   1524     case LayoutKind::FramelessSingleFloat:   // fallthrough
   1525     case LayoutKind::Monocle:                // fallthrough
   1526     case LayoutKind::MainDeck:               // fallthrough
   1527     case LayoutKind::StackDeck:              // fallthrough
   1528     case LayoutKind::DoubleDeck:             // fallthrough
   1529     case LayoutKind::Paper:                  // fallthrough
   1530     case LayoutKind::CompactPaper:           // fallthrough
   1531     case LayoutKind::DoubleStack:            // fallthrough
   1532     case LayoutKind::CompactDoubleStack:     // fallthrough
   1533     case LayoutKind::HorizontalStack:        // fallthrough
   1534     case LayoutKind::CompactHorizontalStack: // fallthrough
   1535     case LayoutKind::VerticalStack:          // fallthrough
   1536     case LayoutKind::CompactVerticalStack:
   1537     {
   1538         return Layout::LayoutData {
   1539             Extents { 0, 0, 0, 0 },
   1540             0,
   1541             1,
   1542             .50f
   1543         };
   1544     }
   1545     default: Util::die("no associated default data defined");
   1546     }
   1547 
   1548     return kind_to_default_data(LayoutKind::Float);
   1549 }