diff --git a/include/modules/dwm.hpp b/include/modules/dwm.hpp index ff108d26..aeb46efd 100644 --- a/include/modules/dwm.hpp +++ b/include/modules/dwm.hpp @@ -48,31 +48,39 @@ namespace modules { static constexpr const char* TAG_LABEL_LAYOUT{""}; static constexpr const char* TAG_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"}; + static constexpr const char* EVENT_PREFIX{"dwm-"}; + static constexpr const char* EVENT_LCLICK{"view"}; + static constexpr const char* EVENT_RCLICK{"toggleview"}; + + void on_layout_change(const dwmipc::LayoutChangeEvent& ev); + void on_monitor_focus_change(const dwmipc::MonitorFocusChangeEvent& ev); + void on_tag_change(const dwmipc::TagChangeEvent& ev); + void on_client_focus_change(const dwmipc::ClientFocusChangeEvent& ev); + void on_focused_title_change(const dwmipc::FocusedTitleChangeEvent& ev); + + void update_monitor_ref(); + void update_tag_labels(); + void update_title_label(unsigned int client_id); auto get_state(tag_mask_t bit_mask) const -> state_t; - void update_monitor_ref(); + auto check_send_cmd(string&& cmd, const string& ev_name) -> bool; + auto reconnect_dwm() -> bool; bool m_click{true}; bool m_pin_tags{false}; + const dwmipc::Monitor* m_active_mon = nullptr; + const dwmipc::Monitor* m_bar_mon = nullptr; + unsigned int m_focused_client_id = 0; + label_t m_layout_label; label_t m_seperator_label; label_t m_title_label; + unique_ptr m_ipc; shared_ptr> m_monitors; - unsigned int m_active_mon_num = 0; - unsigned int m_bar_mon = 0; - unsigned int m_focused_client_id = 0; - std::unordered_map m_state_labels; vector m_tags; - - unique_ptr m_ipc; }; } // namespace modules diff --git a/src/modules/dwm.cpp b/src/modules/dwm.cpp index 46628eb8..66379863 100644 --- a/src/modules/dwm.cpp +++ b/src/modules/dwm.cpp @@ -17,6 +17,7 @@ namespace modules { dwm_module::dwm_module(const bar_settings& bar, string name_) : event_module(bar, move(name_)) { string socket_path = env_util::get("DWM_SOCKET"); if (socket_path.empty()) { + // Defined by cmake socket_path = DWM_SOCKET_PATH; } @@ -27,11 +28,9 @@ namespace modules { m_ipc = factory_util::unique(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}); + // Populate m_state_labels map with labels and their states 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))); @@ -41,39 +40,29 @@ namespace modules { 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))); + // Dummy label for unoccupied tags m_state_labels.insert( std::make_pair(state_t::NONE, load_optional_label(m_conf, name(), "label-none", DEFAULT_TAG_LABEL))); } + m_seperator_label = load_optional_label(m_conf, name(), "label-separator", ""); + 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", ""); + m_click = m_conf.get(name(), "enable-click", m_click); + m_pin_tags = m_conf.get(name(), "pin-tags", m_pin_tags); 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 selected_client = m_monitors->at(m_bar_mon).clients.selected; - std::shared_ptr c; - if (selected_client != 0) { - c = m_ipc->get_client(selected_client); - } - - m_title_label->reset_tokens(); - m_title_label->replace_token("%title%", c->name); - + // Initialize tags array 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(); @@ -81,69 +70,38 @@ 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_layout_label->replace_token("%layout%", m_monitors->at(m_bar_mon).layout.symbol.cur); - m_ipc->on_layout_change = [this](const dwmipc::LayoutChangeEvent& ev) { - if (ev.monitor_num == m_bar_mon) { - m_layout_label->reset_tokens(); - m_layout_label->replace_token("%layout%", ev.new_symbol); - } - }; + // Initialize layout symbol + m_layout_label->replace_token("%layout%", m_bar_mon->layout.symbol.cur); + m_ipc->on_layout_change = [this](const dwmipc::LayoutChangeEvent& ev) { on_layout_change(ev); }; + m_ipc->subscribe(dwmipc::Event::LAYOUT_CHANGE); } + // These events are only necessary to update the focused window title + if (m_title_label) { + update_title_label(m_active_mon->clients.selected); + m_ipc->on_client_focus_change = [this](const dwmipc::ClientFocusChangeEvent& ev) { + this->on_client_focus_change(ev); + }; + m_ipc->subscribe(dwmipc::Event::CLIENT_FOCUS_CHANGE); + + m_ipc->on_focused_title_change = [this](const dwmipc::FocusedTitleChangeEvent& ev) { + this->on_focused_title_change(ev); + }; + m_ipc->subscribe(dwmipc::Event::FOCUSED_TITLE_CHANGE); + } + + // This event is for keeping track of the currently focused monitor m_ipc->on_monitor_focus_change = [this](const dwmipc::MonitorFocusChangeEvent& 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); - } + this->on_monitor_focus_change(ev); }; - - 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_client_focus_change = [this](const dwmipc::ClientFocusChangeEvent& ev) { - if (ev.monitor_num != m_bar_mon) { - return; - } - - m_focused_client_id = ev.new_win_id; - m_title_label->reset_tokens(); - if (m_focused_client_id != 0) { - auto focused_client = m_ipc->get_client(m_focused_client_id); - m_title_label->replace_token("%title%", focused_client->name); - } else { - m_title_label->replace_token("%title%", ""); - } - }; - - m_ipc->on_focused_title_change = [this](const dwmipc::FocusedTitleChangeEvent& ev) { - if (ev.monitor_num == m_bar_mon && ev.client_window_id == m_focused_client_id) { - m_title_label->reset_tokens(); - m_title_label->replace_token("%title%", ev.new_name); - } - }; - - m_ipc->subscribe(dwmipc::Event::LAYOUT_CHANGE); - m_ipc->subscribe(dwmipc::Event::CLIENT_FOCUS_CHANGE); - m_ipc->subscribe(dwmipc::Event::TAG_CHANGE); m_ipc->subscribe(dwmipc::Event::MONITOR_FOCUS_CHANGE); - m_ipc->subscribe(dwmipc::Event::FOCUSED_TITLE_CHANGE); + + // This event is for keeping track of the tag states + m_ipc->on_tag_change = [this](const dwmipc::TagChangeEvent& ev) { this->on_tag_change(ev); }; + m_ipc->subscribe(dwmipc::Event::TAG_CHANGE); } catch (const dwmipc::IPCError& err) { throw module_error(err.what()); } @@ -159,35 +117,24 @@ namespace modules { event_module::stop(); } - bool dwm_module::has_event() { + auto dwm_module::has_event() -> bool { 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()); - } + return reconnect_dwm(); } catch (const exception& err) { m_log.err("%s: Failed to handle event (reason: %s)", name(), err.what()); } return false; } - bool dwm_module::update() { + auto dwm_module::update() -> bool { + // All updates are handled in has_event return true; } - bool dwm_module::build(builder* builder, const string& tag) const { + 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) { @@ -195,7 +142,9 @@ namespace modules { } else if (tag == TAG_LABEL_STATE) { bool first = true; for (const auto& tag : m_tags) { + // Print tags without state only if m_pin_tags if ((tag.state == state_t::NONE && m_pin_tags) || (!m_pin_tags && tag.state != state_t::NONE)) { + // Don't insert separator before first tag if (first) { first = false; } else if (*m_seperator_label) { @@ -219,36 +168,36 @@ namespace modules { return true; } - bool dwm_module::input(string&& cmd) { + auto dwm_module::check_send_cmd(string&& cmd, const string& ev_name) -> bool { + // cmd = -- + // +1 : Erase '-' + cmd.erase(0, strlen(EVENT_PREFIX) + 1); + + // cmd = - + if (cmd.compare(0, ev_name.size(), ev_name) == 0) { + // Erase '-' + cmd.erase(0, ev_name.size() + 1); + m_log.info("%s: Sending workspace view command to ipc handler", name()); + + try { + m_ipc->run_command(ev_name, stoul(cmd)); + return true; + } catch (const dwmipc::IPCError& err) { + throw module_error(err.what()); + } + } + return false; + } + + auto dwm_module::input(string&& cmd) -> bool { 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; + return check_send_cmd(move(cmd), EVENT_LCLICK) || check_send_cmd(move(cmd), EVENT_RCLICK); } - dwm_module::state_t dwm_module::get_state(tag_mask_t bit_mask) const { + auto dwm_module::get_state(tag_mask_t bit_mask) const -> state_t { // Tag selected > occupied > urgent // Monitor selected - Tag selected FOCUSED // Monitor unselected - Tag selected UNFOCUSED @@ -256,8 +205,8 @@ namespace modules { // 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; + auto tag_state = m_bar_mon->tag_state; + bool is_mon_active = m_bar_mon == m_active_mon; if (is_mon_active && tag_state.selected & bit_mask) { // Tag selected on selected monitor @@ -277,19 +226,101 @@ namespace modules { } void dwm_module::update_monitor_ref() { + m_monitors = m_ipc->get_monitors(); + // Sort by increasing monitor index + std::sort(m_monitors->begin(), m_monitors->end(), + [](dwmipc::Monitor& m1, dwmipc::Monitor& m2) { return m1.num < m2.num; }); + for (const dwmipc::Monitor& m : *m_monitors) { const auto& geom = m.monitor_geom; const auto& bmon = *m_bar.monitor; + // Compare geometry if (geom.x == bmon.x && geom.y == bmon.y && geom.width == bmon.w && geom.height == bmon.h) { - m_bar_mon = m.num; + m_bar_mon = &m; } if (m.is_selected) { - m_active_mon_num = m.num; + m_active_mon = &m; } } } + void dwm_module::update_tag_labels() { + 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); + } + } + + void dwm_module::update_title_label(unsigned int client_id) { + std::string new_title; + if (client_id != 0) { + try { + new_title = m_ipc->get_client(client_id)->name; + } catch (const dwmipc::IPCError &err) { + throw module_error(err.what()); + } + } + m_title_label->reset_tokens(); + m_title_label->replace_token("%title%", new_title); + } + + auto dwm_module::reconnect_dwm() -> bool { + 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()); + } + 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()); + } + return true; + } catch (const exception& err) { + m_log.err("%s: Failed to reconnect to socket: %s", name(), err.what()); + } + return false; + } + + void dwm_module::on_layout_change(const dwmipc::LayoutChangeEvent& ev) { + if (ev.monitor_num == m_bar_mon->num) { + m_layout_label->reset_tokens(); + m_layout_label->replace_token("%layout%", ev.new_symbol); + } + } + + void dwm_module::on_monitor_focus_change(const dwmipc::MonitorFocusChangeEvent& ev) { + m_active_mon = &m_monitors->at(ev.new_mon_num); + update_tag_labels(); + } + + void dwm_module::on_tag_change(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->num) { + update_tag_labels(); + } + } + + void dwm_module::on_focused_title_change(const dwmipc::FocusedTitleChangeEvent& ev) { + if (ev.monitor_num == m_bar_mon->num && ev.client_window_id == m_focused_client_id) { + m_title_label->reset_tokens(); + m_title_label->replace_token("%title%", ev.new_name); + } + } + + void dwm_module::on_client_focus_change(const dwmipc::ClientFocusChangeEvent& ev) { + if (ev.monitor_num == m_bar_mon->num) { + m_focused_client_id = ev.new_win_id; + update_title_label(ev.new_win_id); + } + } + } // namespace modules POLYBAR_NS_END