Add dwm module

The module is currently working, but not fully stable. All tags specified in
config.cmake for internal/dwm are supported. This was implemented following the
i3 and bspwm modules. There is still some cleaning/refactoring to do to polish
the module up.
This commit is contained in:
Mihir Lad 2020-07-17 00:35:15 -04:00
parent 64aa2013c2
commit 79f262fb18
3 changed files with 358 additions and 0 deletions

78
include/modules/dwm.hpp Normal file
View File

@ -0,0 +1,78 @@
#pragma once
#include <dwmipcpp/connection.hpp>
#include "modules/meta/event_module.hpp"
#include "modules/meta/input_handler.hpp"
POLYBAR_NS
namespace modules {
class dwm_module : public event_module<dwm_module>, public input_handler {
public:
explicit dwm_module(const bar_settings&, string);
using tag_mask_t = unsigned int;
enum class state_t : uint8_t {
FOCUSED, ///< Monitor is selected and tag is selected, overrides all below
URGENT, ///< Tag is urgent, overrides all below
UNFOCUSED, ///< Monitor is not selected, but tag is selected
VISIBLE, ///< Tag is not selected, but occupied
NONE ///< Tag is unoccupied and unselected
};
struct tag_t {
tag_t(string& name, unsigned int bit_mask, state_t state, label_t&& label)
: name(name), bit_mask(bit_mask), state(state), label(forward<label_t>(label)) {}
string name;
unsigned int bit_mask;
state_t state;
label_t label;
};
auto stop() -> void override;
auto has_event() -> bool;
auto update() -> bool;
auto build(builder* builder, const string& tag) const -> bool;
protected:
auto input(string&& cmd) -> bool override;
private:
static constexpr const char* DEFAULT_FORMAT_TAGS{"<label-state> <label-layout> <label-title>"};
static constexpr const char* DEFAULT_TAG_LABEL{"%name%"};
static constexpr const char* TAG_LABEL_STATE{"<label-state>"};
static constexpr const char* TAG_LABEL_LAYOUT{"<label-layout>"};
static constexpr const char* TAG_LABEL_TITLE{"<label-title>"};
static constexpr const char* EVENT_PREFIX{"dwm"};
static constexpr const char* EVENT_LCLICK{"dwm-view-"};
static constexpr const char* EVENT_RCLICK{"dwm-toggleview-"};
static constexpr const char* EVENT_SCROLL_UP{"dwm-tagnext"};
static constexpr const char* EVENT_SCROLL_DOWN{"dwm-tagprev"};
auto get_state(tag_mask_t bit_mask) const -> state_t;
void update_monitor_ref();
bool m_click{true};
bool m_pin_tags{false};
label_t m_layout_label;
label_t m_seperator_label;
label_t m_title_label;
shared_ptr<std::vector<dwmipc::Monitor>> m_monitors;
unsigned int m_active_mon_num = 0;
unsigned int m_bar_mon = 0;
std::unordered_map<state_t, label_t> m_state_labels;
vector<tag_t> m_tags;
unique_ptr<dwmipc::Connection> m_ipc;
};
} // namespace modules
POLYBAR_NS_END

View File

@ -24,6 +24,9 @@
#if ENABLE_I3 #if ENABLE_I3
#include "modules/i3.hpp" #include "modules/i3.hpp"
#endif #endif
#if ENABLE_DWM
#include "modules/dwm.hpp"
#endif
#if ENABLE_MPD #if ENABLE_MPD
#include "modules/mpd.hpp" #include "modules/mpd.hpp"
#endif #endif
@ -62,6 +65,8 @@ namespace {
return new cpu_module(bar, move(module_name)); return new cpu_module(bar, move(module_name));
} else if (name == date_module::TYPE) { } else if (name == date_module::TYPE) {
return new date_module(bar, move(module_name)); return new date_module(bar, move(module_name));
} else if (name == "internal/dwm") {
return new dwm_module(bar, move(module_name));
} else if (name == github_module::TYPE) { } else if (name == github_module::TYPE) {
return new github_module(bar, move(module_name)); return new github_module(bar, move(module_name));
} else if (name == fs_module::TYPE) { } else if (name == fs_module::TYPE) {

275
src/modules/dwm.cpp Normal file
View File

@ -0,0 +1,275 @@
#include "modules/dwm.hpp"
#include <dwmipcpp/errors.hpp>
#include "components/builder.hpp"
#include "components/config.hpp"
#include "drawtypes/label.hpp"
#include "modules/meta/base.inl"
#include "utils/env.hpp"
#include "utils/file.hpp"
POLYBAR_NS
namespace modules {
template class module<dwm_module>;
dwm_module::dwm_module(const bar_settings& bar, string name_) : event_module<dwm_module>(bar, move(name_)) {
string socket_path = env_util::get("DWM_SOCKET");
if (socket_path.empty()) {
socket_path = DWM_SOCKET_PATH;
}
if (!file_util::exists(socket_path)) {
throw module_error("Could not find socket: " + (socket_path.empty() ? "<empty>" : socket_path));
}
m_ipc = factory_util::unique<dwmipc::Connection>(socket_path);
// Load configuration
m_click = m_conf.get(name(), "enable-click", m_click);
m_pin_tags = m_conf.get(name(), "pin-tags", m_pin_tags);
m_formatter->add(DEFAULT_FORMAT, DEFAULT_FORMAT_TAGS, {TAG_LABEL_STATE, TAG_LABEL_LAYOUT, TAG_LABEL_TITLE});
if (m_formatter->has(TAG_LABEL_STATE)) {
m_state_labels.insert(
std::make_pair(state_t::FOCUSED, load_optional_label(m_conf, name(), "label-focused", DEFAULT_TAG_LABEL)));
m_state_labels.insert(std::make_pair(
state_t::UNFOCUSED, load_optional_label(m_conf, name(), "label-unfocused", DEFAULT_TAG_LABEL)));
m_state_labels.insert(
std::make_pair(state_t::VISIBLE, load_optional_label(m_conf, name(), "label-visible", DEFAULT_TAG_LABEL)));
m_state_labels.insert(
std::make_pair(state_t::URGENT, load_optional_label(m_conf, name(), "label-urgent", DEFAULT_TAG_LABEL)));
m_state_labels.insert(
std::make_pair(state_t::NONE, load_optional_label(m_conf, name(), "label-none", DEFAULT_TAG_LABEL)));
}
if (m_formatter->has(TAG_LABEL_LAYOUT)) {
m_layout_label = load_optional_label(m_conf, name(), "label-layout", "%layout%");
}
if (m_formatter->has(TAG_LABEL_TITLE)) {
m_title_label = load_optional_label(m_conf, name(), "label-title", "%title%");
m_title_label->replace_token("%title%", "");
}
m_seperator_label = load_optional_label(m_conf, name(), "label-separator", "");
try {
m_monitors = m_ipc->get_monitors();
std::sort(m_monitors->begin(), m_monitors->end(),
[](dwmipc::Monitor& m1, dwmipc::Monitor& m2) { return m1.num < m2.num; });
update_monitor_ref();
auto tags = m_ipc->get_tags();
// m_tags.resize(tags->size());
for (dwmipc::Tag& t : *tags) {
auto state = get_state(t.bit_mask);
auto label = m_state_labels.at(state)->clone();
label->replace_token("%name%", t.tag_name);
m_tags.emplace_back(t.tag_name, t.bit_mask, state, move(label));
}
if (m_layout_label) {
auto layouts = m_ipc->get_layouts();
m_layout_label->replace_token("%layout%", m_monitors->at(m_bar_mon).layout.symbol.cur);
m_ipc->on_layout_change = [this](const dwmipc::LayoutChangeEvent& ev) {
m_layout_label->reset_tokens();
m_layout_label->replace_token("%layout%", ev.new_symbol);
};
}
m_ipc->on_selected_monitor_change = [this](const dwmipc::SelectedMonitorChangeEvent& ev) {
m_active_mon_num = ev.new_mon_num;
for (auto& t : m_tags) {
t.state = get_state(t.bit_mask);
t.label = m_state_labels.at(t.state)->clone();
t.label->reset_tokens();
t.label->replace_token("%name%", t.name);
}
};
m_ipc->on_tag_change = [this](const dwmipc::TagChangeEvent& ev) {
auto& mon = m_monitors->at(ev.monitor_num);
mon.tag_state = ev.new_state;
if (ev.monitor_num == m_bar_mon) {
for (auto& t : m_tags) {
t.state = get_state(t.bit_mask);
t.label = m_state_labels.at(t.state)->clone();
t.label->reset_tokens();
t.label->replace_token("%name%", t.name);
}
}
};
m_ipc->on_selected_client_change = [this](const dwmipc::SelectedClientChangeEvent& ev) {
if (ev.monitor_num != m_bar_mon) {
return;
}
m_title_label->reset_tokens();
if (ev.new_win_id != 0) {
auto focused_client = m_ipc->get_client(ev.new_win_id);
m_title_label->replace_token("%title%", focused_client->name);
} else {
m_title_label->replace_token("%title%", "");
}
};
m_ipc->subscribe(dwmipc::Event::LAYOUT_CHANGE);
m_ipc->subscribe(dwmipc::Event::SELECTED_MONITOR_CHANGE);
m_ipc->subscribe(dwmipc::Event::TAG_CHANGE);
m_ipc->subscribe(dwmipc::Event::SELECTED_CLIENT_CHANGE);
} catch (const dwmipc::IPCError& err) {
throw module_error(err.what());
}
}
void dwm_module::stop() {
try {
m_log.info("%s: Disconnecting from socket", name());
m_ipc.reset();
} catch (...) {
}
event_module::stop();
}
bool dwm_module::has_event() {
try {
return m_ipc->handle_event();
} catch (const dwmipc::SocketClosedError& err) {
m_log.err("%s: Disconnected from socket: %s", name(),err.what());
try {
if (!m_ipc->is_main_socket_connected()) {
m_log.info("%s: Attempting to reconnect to main socket", name());
m_ipc->connect_main_socket();
m_log.info("%s: Successfully reconnected to main socket", name());
} else if (!m_ipc->is_event_socket_connected()) {
m_log.info("%s: Attempting to reconnect event socket", name());
m_ipc->connect_event_socket();
m_log.info("%s: Successfully reconnected event to socket", name());
}
} catch (const exception& err) {
m_log.err("%s: Failed to reconnect to socket: %s", name(), err.what());
}
} catch (const exception& err) {
m_log.err("%s: Failed to handle event (reason: %s)", name(), err.what());
}
return false;
}
bool dwm_module::update() {
return true;
}
bool dwm_module::build(builder* builder, const string& tag) const {
if (tag == TAG_LABEL_LAYOUT) {
builder->node(m_layout_label);
} else if (tag == TAG_LABEL_TITLE) {
builder->node(m_title_label);
} else if (tag == TAG_LABEL_STATE) {
bool first = true;
for (const auto& tag : m_tags) {
if ((tag.state == state_t::NONE && m_pin_tags) || (!m_pin_tags && tag.state != state_t::NONE)) {
if (first) {
first = false;
} else if (*m_seperator_label) {
builder->node(m_seperator_label);
}
if (m_click) {
builder->cmd(mousebtn::LEFT, string{EVENT_LCLICK} + to_string(tag.bit_mask));
builder->cmd(mousebtn::RIGHT, string{EVENT_RCLICK} + to_string(tag.bit_mask));
builder->node(tag.label);
builder->cmd_close();
builder->cmd_close();
} else {
builder->node(tag.label);
}
}
}
} else {
return false;
}
return true;
}
bool dwm_module::input(string&& cmd) {
if (cmd.find(EVENT_PREFIX) != 0) {
return false;
}
if (cmd.compare(0, strlen(EVENT_LCLICK), EVENT_LCLICK) == 0) {
cmd.erase(0, strlen(EVENT_LCLICK));
m_log.info("%s: Sending workspace view command to ipc handler", name());
try {
m_ipc->run_command("view", stoul(cmd));
return true;
} catch (const dwmipc::IPCError& err) {
throw module_error(err.what());
}
} else if (cmd.compare(0, strlen(EVENT_RCLICK), EVENT_RCLICK) == 0) {
cmd.erase(0, strlen(EVENT_RCLICK));
m_log.info("%s: Sending workspace toggleview command to ipc handler", name());
try {
m_ipc->run_command("toggleview", stoul(cmd));
return true;
} catch (const dwmipc::IPCError& err) {
throw module_error(err.what());
}
}
return true;
}
dwm_module::state_t dwm_module::get_state(tag_mask_t bit_mask) const {
// Tag selected > occupied > urgent
// Monitor selected - Tag selected FOCUSED
// Monitor unselected - Tag selected UNFOCUSED
// Tag unselected - Tag occupied - Tag non-urgent VISIBLE
// Tag unselected - Tag occupied - Tag urgent URGENT
// Tag unselected - Tag unoccupied PIN?
auto tag_state = m_monitors->at(m_bar_mon).tag_state;
bool is_mon_active = m_bar_mon == m_active_mon_num;
if (is_mon_active && tag_state.selected & bit_mask) {
// Tag selected on selected monitor
return state_t::FOCUSED;
} else if (tag_state.urgent & bit_mask) {
// Tag is urgent
return state_t::URGENT;
} else if (!is_mon_active && tag_state.selected & bit_mask) {
// Tag is selected, but not on selected monitor
return state_t::UNFOCUSED;
} else if (tag_state.occupied & bit_mask) {
// Tag is occupied, but not selected
return state_t::VISIBLE;
}
return state_t::NONE;
}
void dwm_module::update_monitor_ref() {
for (const dwmipc::Monitor& m : *m_monitors) {
const auto& geom = m.monitor_geom;
const auto& bmon = *m_bar.monitor;
if (geom.x == bmon.x && geom.y == bmon.y && geom.width == bmon.w && geom.height == bmon.h) {
m_bar_mon = m.num;
}
if (m.is_selected) {
m_active_mon_num = m.num;
}
}
}
} // namespace modules
POLYBAR_NS_END