diff --git a/config.cmake b/config.cmake index d6ed90eb..ad66c49d 100644 --- a/config.cmake +++ b/config.cmake @@ -172,6 +172,9 @@ format = enable-click = false +; If enable-click = true, clicking the layout symbol will switch to this layout +secondary-layout-symbol = [M] + ; State ; Available tokens: ; %name% diff --git a/include/modules/dwm.hpp b/include/modules/dwm.hpp index 1e64bb2f..0c026e83 100644 --- a/include/modules/dwm.hpp +++ b/include/modules/dwm.hpp @@ -93,6 +93,18 @@ namespace modules { */ static constexpr const char* EVENT_TAG_RCLICK{"toggleview"}; + /** + * Event name is same as the IPC command name to set the layout to the + * secondary layout when the layout symbol is clicked + */ + static constexpr const char* EVENT_LAYOUT_LCLICK{"setlayoutsafe"}; + + /** + * Event name is same as the IPC command name to set the layout to the + * last layout. + */ + static constexpr const char* EVENT_LAYOUT_RCLICK{"setlayoutsafe"}; + /** * Called by has_event on layout changes. This updates the layout label * @@ -162,6 +174,16 @@ namespace modules { */ auto get_state(tag_mask_t bit_mask) const -> state_t; + /** + * Get the address to the layout represented by the symbol. + */ + auto find_layout(const string& sym) const -> const dwmipc::Layout*; + + /** + * Get the address to the layout represented by the address. + */ + auto find_layout(uintptr_t addr) const -> const dwmipc::Layout*; + /** * Check if the command matches the specified event name and if so, send a * command to dwm after parsing the command. @@ -193,6 +215,28 @@ namespace modules { */ bool m_click{true}; + /** + * If the layout symbol is clicked on, it will set the layout represented by + * this symbol. The default is monocle mode [M]. + */ + string m_secondary_layout_symbol{"[M]"}; + + /** + * Holds the address to the secondary layout specified by the secondary + * layout symbol + */ + const dwmipc::Layout* m_secondary_layout = nullptr; + + /** + * Holds the address to the current layout + */ + const dwmipc::Layout* m_current_layout = nullptr; + + /** + * Holds the address to the default layout + */ + const dwmipc::Layout* m_default_layout = nullptr; + /** * Holds the address to the currently active monitor in the m_monitors array */ @@ -233,6 +277,11 @@ namespace modules { */ shared_ptr> m_monitors; + /** + * Vector of layouts returned by m_ipc->get_layouts + */ + shared_ptr> m_layouts; + /** * Maps state_t enum values to their corresponding labels */ diff --git a/src/modules/dwm.cpp b/src/modules/dwm.cpp index 21bbf461..170fa336 100644 --- a/src/modules/dwm.cpp +++ b/src/modules/dwm.cpp @@ -56,6 +56,8 @@ namespace modules { m_click = m_conf.get(name(), "enable-click", m_click); + m_secondary_layout_symbol = m_conf.get(name(), "secondary-layout-symbol", m_secondary_layout_symbol); + try { update_monitor_ref(); @@ -68,11 +70,20 @@ namespace modules { m_tags.emplace_back(t.tag_name, t.bit_mask, state, move(label)); } - // This event is only needed to update the layout label if (m_layout_label) { auto layouts = m_ipc->get_layouts(); + m_layouts = m_ipc->get_layouts(); + // First layout is treated as default by dwm + m_default_layout = &m_layouts->at(0); + m_current_layout = find_layout(m_bar_mon->layout.address.cur); + m_secondary_layout = find_layout(m_secondary_layout_symbol); + if (m_secondary_layout == nullptr) { + throw module_error("Secondary layout symbol does not exist"); + } + // Initialize layout symbol m_layout_label->replace_token("%layout%", m_bar_mon->layout.symbol.cur); + // This event is only needed to update the layout label m_ipc->on_layout_change = [this](const dwmipc::LayoutChangeEvent& ev) { on_layout_change(ev); }; m_ipc->subscribe(dwmipc::Event::LAYOUT_CHANGE); } @@ -137,10 +148,21 @@ namespace modules { } auto dwm_module::build(builder* builder, const string& tag) const -> bool { - if (tag == TAG_LABEL_LAYOUT) { - builder->node(m_layout_label); - } else if (tag == TAG_LABEL_TITLE) { + if (tag == TAG_LABEL_TITLE) { builder->node(m_title_label); + } else if (tag == TAG_LABEL_LAYOUT) { + if (m_click) { + // Toggle between secondary and default layout + auto addr = (m_current_layout == m_default_layout ? m_secondary_layout : m_default_layout)->address; + builder->cmd(mousebtn::LEFT, build_cmd(EVENT_LAYOUT_LCLICK, to_string(addr))); + // Set previous layout + builder->cmd(mousebtn::RIGHT, build_cmd(EVENT_LAYOUT_RCLICK, "0")); + builder->node(m_layout_label); + builder->cmd_close(); + builder->cmd_close(); + } else { + builder->node(m_layout_label); + } } else if (tag == TAG_LABEL_STATE) { bool first = true; for (const auto& tag : m_tags) { @@ -168,7 +190,6 @@ namespace modules { } auto dwm_module::check_send_cmd(string cmd, const string& ev_name) -> bool { - std::cerr << cmd << std::endl; // cmd = - cmd.erase(0, strlen(EVENT_PREFIX)); @@ -176,7 +197,7 @@ namespace modules { if (cmd.compare(0, ev_name.size(), ev_name) == 0) { // Erase '-' cmd.erase(0, ev_name.size() + 1); - m_log.info("%s: Sending workspace %s command to ipc handler", ev_name, name()); + m_log.info("%s: Sending workspace %s command to ipc handler", name(), ev_name); try { m_ipc->run_command(ev_name, stoul(cmd)); @@ -196,7 +217,8 @@ namespace modules { return false; } - return check_send_cmd(cmd, EVENT_TAG_LCLICK) || check_send_cmd(cmd, EVENT_TAG_RCLICK); + return check_send_cmd(cmd, EVENT_TAG_LCLICK) || check_send_cmd(cmd, EVENT_TAG_RCLICK) || + check_send_cmd(cmd, EVENT_LAYOUT_LCLICK) || check_send_cmd(cmd, EVENT_LAYOUT_RCLICK); } auto dwm_module::get_state(tag_mask_t bit_mask) const -> state_t { @@ -247,6 +269,24 @@ namespace modules { } } + auto dwm_module::find_layout(const string& sym) const -> const dwmipc::Layout* { + for (const auto& lt : *m_layouts) { + if (lt.symbol == sym) { + return < + } + } + return nullptr; + } + + auto dwm_module::find_layout(const uintptr_t addr) const -> const dwmipc::Layout* { + for (const auto& lt : *m_layouts) { + if (lt.address == addr) { + return < + } + } + return nullptr; + } + void dwm_module::update_tag_labels() { for (auto& t : m_tags) { t.state = get_state(t.bit_mask); @@ -293,6 +333,7 @@ namespace modules { void dwm_module::on_layout_change(const dwmipc::LayoutChangeEvent& ev) { if (ev.monitor_num == m_bar_mon->num) { + m_current_layout = find_layout(ev.new_address); m_layout_label->reset_tokens(); m_layout_label->replace_token("%layout%", ev.new_symbol); }