dwm: Add click handlers to label-layout

secondary-layout-symbol is the symbol of the layout to switch to when the layout
symbol is left-clicked. This symbol is used to look up the memory address of the
layout to set. The default is the monocle layout.

setlayoutsafe is the dwm command name for setting layouts. The argument is the
layout memory address. Left-clicking switches to the layout represented by
secondary-layout-symbol, and right-clicking switches to the previous layout
(specified by an argument of 0).

To keep track of the different layouts, the layouts are retrieved in the
constructor if the layout label is included and stored in m_layouts.
m_current_layout is updated with the address of the current layout.
m_default_layout is updated in the constructor to the first layout in the array
which is the default layout in dwm. m_secondary_layout is updated to the address
of the layout identified by secondary-layout-symbol.

The builder adds click handlers (if layout label is included) to the layout
symbol as described above. Left-clicking toggles between the secondary layout
and the default layout. Right-clicking toggles between the previous layout and
the current layout.

find_layout are a pair of functions for finding a layout by address or symbol
from the m_layouts array.
This commit is contained in:
Mihir Lad 2020-07-20 23:46:54 -04:00
parent ecc881b9b3
commit bc16268d30
3 changed files with 100 additions and 7 deletions

View File

@ -172,6 +172,9 @@ format = <label-state> <label-layout> <label-title>
enable-click = false enable-click = false
; If enable-click = true, clicking the layout symbol will switch to this layout
secondary-layout-symbol = [M]
; State ; State
; Available tokens: ; Available tokens:
; %name% ; %name%

View File

@ -93,6 +93,18 @@ namespace modules {
*/ */
static constexpr const char* EVENT_TAG_RCLICK{"toggleview"}; 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 * 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; 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 * Check if the command matches the specified event name and if so, send a
* command to dwm after parsing the command. * command to dwm after parsing the command.
@ -193,6 +215,28 @@ namespace modules {
*/ */
bool m_click{true}; 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 * Holds the address to the currently active monitor in the m_monitors array
*/ */
@ -233,6 +277,11 @@ namespace modules {
*/ */
shared_ptr<vector<dwmipc::Monitor>> m_monitors; shared_ptr<vector<dwmipc::Monitor>> m_monitors;
/**
* Vector of layouts returned by m_ipc->get_layouts
*/
shared_ptr<vector<dwmipc::Layout>> m_layouts;
/** /**
* Maps state_t enum values to their corresponding labels * Maps state_t enum values to their corresponding labels
*/ */

View File

@ -56,6 +56,8 @@ namespace modules {
m_click = m_conf.get(name(), "enable-click", m_click); 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 { try {
update_monitor_ref(); update_monitor_ref();
@ -68,11 +70,20 @@ namespace modules {
m_tags.emplace_back(t.tag_name, t.bit_mask, state, move(label)); 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) { if (m_layout_label) {
auto layouts = m_ipc->get_layouts(); 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 // Initialize layout symbol
m_layout_label->replace_token("%layout%", m_bar_mon->layout.symbol.cur); 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->on_layout_change = [this](const dwmipc::LayoutChangeEvent& ev) { on_layout_change(ev); };
m_ipc->subscribe(dwmipc::Event::LAYOUT_CHANGE); m_ipc->subscribe(dwmipc::Event::LAYOUT_CHANGE);
} }
@ -137,10 +148,21 @@ namespace modules {
} }
auto dwm_module::build(builder* builder, const string& tag) const -> bool { auto dwm_module::build(builder* builder, const string& tag) const -> bool {
if (tag == TAG_LABEL_LAYOUT) { if (tag == TAG_LABEL_TITLE) {
builder->node(m_layout_label);
} else if (tag == TAG_LABEL_TITLE) {
builder->node(m_title_label); 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) { } else if (tag == TAG_LABEL_STATE) {
bool first = true; bool first = true;
for (const auto& tag : m_tags) { 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 { auto dwm_module::check_send_cmd(string cmd, const string& ev_name) -> bool {
std::cerr << cmd << std::endl;
// cmd = <EVENT_PREFIX><ev_name>-<arg> // cmd = <EVENT_PREFIX><ev_name>-<arg>
cmd.erase(0, strlen(EVENT_PREFIX)); cmd.erase(0, strlen(EVENT_PREFIX));
@ -176,7 +197,7 @@ namespace modules {
if (cmd.compare(0, ev_name.size(), ev_name) == 0) { if (cmd.compare(0, ev_name.size(), ev_name) == 0) {
// Erase '<ev_name>-' // Erase '<ev_name>-'
cmd.erase(0, ev_name.size() + 1); 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 { try {
m_ipc->run_command(ev_name, stoul(cmd)); m_ipc->run_command(ev_name, stoul(cmd));
@ -196,7 +217,8 @@ namespace modules {
return false; 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 { 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 &lt;
}
}
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 &lt;
}
}
return nullptr;
}
void dwm_module::update_tag_labels() { void dwm_module::update_tag_labels() {
for (auto& t : m_tags) { for (auto& t : m_tags) {
t.state = get_state(t.bit_mask); t.state = get_state(t.bit_mask);
@ -293,6 +333,7 @@ namespace modules {
void dwm_module::on_layout_change(const dwmipc::LayoutChangeEvent& ev) { void dwm_module::on_layout_change(const dwmipc::LayoutChangeEvent& ev) {
if (ev.monitor_num == m_bar_mon->num) { if (ev.monitor_num == m_bar_mon->num) {
m_current_layout = find_layout(ev.new_address);
m_layout_label->reset_tokens(); m_layout_label->reset_tokens();
m_layout_label->replace_token("%layout%", ev.new_symbol); m_layout_label->replace_token("%layout%", ev.new_symbol);
} }