polybar/src/modules/bspwm.cpp

511 lines
17 KiB
C++
Raw Normal View History

#include "modules/bspwm.hpp"
2016-11-02 19:22:45 +00:00
#include <sys/socket.h>
#include "drawtypes/iconset.hpp"
#include "drawtypes/label.hpp"
#include "modules/meta/base.inl"
2016-12-14 06:45:29 +00:00
#include "utils/file.hpp"
#include "utils/string.hpp"
2016-11-19 05:22:44 +00:00
POLYBAR_NS
2016-11-02 19:22:45 +00:00
2016-11-30 20:03:28 +00:00
namespace {
using bspwm_state = modules::bspwm_module::state;
2017-01-19 10:11:28 +00:00
unsigned int make_mask(bspwm_state s1, bspwm_state s2 = bspwm_state::NONE) {
unsigned int mask{0U};
if (static_cast<unsigned int>(s1)) {
mask |= 1U << (static_cast<unsigned int>(s1) - 1U);
2016-12-04 03:11:47 +00:00
}
2017-01-19 10:11:28 +00:00
if (static_cast<unsigned int>(s2)) {
mask |= 1U << (static_cast<unsigned int>(s2) - 1U);
2016-12-04 03:11:47 +00:00
}
2016-11-30 20:03:28 +00:00
return mask;
}
2017-01-19 10:11:28 +00:00
unsigned int check_mask(unsigned int base, bspwm_state s1, bspwm_state s2 = bspwm_state::NONE) {
unsigned int mask{0U};
if (static_cast<unsigned int>(s1)) {
mask |= 1U << (static_cast<unsigned int>(s1) - 1U);
}
2017-01-19 10:11:28 +00:00
if (static_cast<unsigned int>(s2)) {
mask |= 1U << (static_cast<unsigned int>(s2) - 1U);
}
return (base & mask) == mask;
}
} // namespace
2016-11-30 20:03:28 +00:00
2016-11-02 19:22:45 +00:00
namespace modules {
2016-11-20 22:04:31 +00:00
template class module<bspwm_module>;
bspwm_module::bspwm_module(const bar_settings& bar, string name_, const config& config)
: event_module<bspwm_module>(bar, move(name_), config) {
m_router->register_action_with_data(EVENT_FOCUS, [this](const std::string& data) { action_focus(data); });
m_router->register_action(EVENT_NEXT, [this]() { action_next(); });
m_router->register_action(EVENT_PREV, [this]() { action_prev(); });
2016-11-30 17:23:18 +00:00
auto socket_path = bspwm_util::get_socket_path();
if (!file_util::exists(socket_path)) {
throw module_error("Could not find socket: " + (socket_path.empty() ? "<empty>" : socket_path));
}
// Create ipc subscriber
m_subscriber = bspwm_util::make_subscriber();
// Load configuration values
m_pinworkspaces = m_conf.get(name(), "pin-workspaces", m_pinworkspaces);
m_click = m_conf.get(name(), "enable-click", m_click);
m_scroll = m_conf.get(name(), "enable-scroll", m_scroll);
m_occscroll = m_conf.get(name(), "occupied-scroll", m_occscroll);
m_revscroll = m_conf.get(name(), "reverse-scroll", m_revscroll);
m_inlinemode = m_conf.get(name(), "inline-mode", m_inlinemode);
m_fuzzy_match = m_conf.get(name(), "fuzzy-match", m_fuzzy_match);
// Add formats and create components
m_formatter->add(DEFAULT_FORMAT, TAG_LABEL_STATE, {TAG_LABEL_STATE}, {TAG_LABEL_MONITOR, TAG_LABEL_MODE});
if (m_formatter->has(TAG_LABEL_MONITOR)) {
m_monitorlabel = load_optional_label(m_conf, name(), "label-monitor", DEFAULT_MONITOR_LABEL);
}
2016-11-02 19:22:45 +00:00
if (m_formatter->has(TAG_LABEL_STATE)) {
2016-11-30 20:03:28 +00:00
// XXX: Warn about deprecated parameters
m_conf.warn_deprecated(name(), "label-dimmed-active", "label-dimmed-focused");
// clang-format off
try {
m_statelabels.emplace(make_mask(state::FOCUSED), load_label(m_conf, name(), "label-active", DEFAULT_LABEL));
m_conf.warn_deprecated(name(), "label-active", "label-focused and label-dimmed-focused");
} catch (const key_error& err) {
m_statelabels.emplace(make_mask(state::FOCUSED), load_optional_label(m_conf, name(), "label-focused", DEFAULT_LABEL));
}
m_statelabels.emplace(make_mask(state::OCCUPIED),
load_optional_label(m_conf, name(), "label-occupied", DEFAULT_LABEL));
m_statelabels.emplace(make_mask(state::URGENT),
load_optional_label(m_conf, name(), "label-urgent", DEFAULT_LABEL));
m_statelabels.emplace(make_mask(state::EMPTY),
load_optional_label(m_conf, name(), "label-empty", DEFAULT_LABEL));
m_statelabels.emplace(make_mask(state::DIMMED),
load_optional_label(m_conf, name(), "label-dimmed"));
vector<pair<state, string>> focused_overrides{
{state::OCCUPIED, "label-focused-occupied"},
{state::URGENT, "label-focused-urgent"},
{state::EMPTY, "label-focused-empty"}};
for (auto&& os : focused_overrides) {
2017-01-19 10:11:28 +00:00
unsigned int mask{make_mask(state::FOCUSED, os.first)};
2016-11-30 20:03:28 +00:00
try {
m_statelabels.emplace(mask, load_label(m_conf, name(), os.second));
} catch (const key_error& err) {
m_statelabels.emplace(mask, m_statelabels.at(make_mask(state::FOCUSED))->clone());
}
}
vector<pair<state, string>> dimmed_overrides{
{state::FOCUSED, "label-dimmed-focused"},
{state::OCCUPIED, "label-dimmed-occupied"},
{state::URGENT, "label-dimmed-urgent"},
{state::EMPTY, "label-dimmed-empty"}};
for (auto&& os : dimmed_overrides) {
m_statelabels.emplace(make_mask(state::DIMMED, os.first),
load_optional_label(m_conf, name(), os.second, m_statelabels.at(make_mask(os.first))->get()));
}
// clang-format on
2016-11-02 19:22:45 +00:00
}
if (m_formatter->has(TAG_LABEL_MODE)) {
2016-11-30 20:03:28 +00:00
m_modelabels.emplace(mode::LAYOUT_MONOCLE, load_optional_label(m_conf, name(), "label-monocle"));
m_modelabels.emplace(mode::LAYOUT_TILED, load_optional_label(m_conf, name(), "label-tiled"));
m_modelabels.emplace(mode::STATE_FULLSCREEN, load_optional_label(m_conf, name(), "label-fullscreen"));
m_modelabels.emplace(mode::STATE_FLOATING, load_optional_label(m_conf, name(), "label-floating"));
m_modelabels.emplace(mode::STATE_PSEUDOTILED, load_optional_label(m_conf, name(), "label-pseudotiled"));
2016-11-30 20:03:28 +00:00
m_modelabels.emplace(mode::NODE_LOCKED, load_optional_label(m_conf, name(), "label-locked"));
m_modelabels.emplace(mode::NODE_STICKY, load_optional_label(m_conf, name(), "label-sticky"));
m_modelabels.emplace(mode::NODE_PRIVATE, load_optional_label(m_conf, name(), "label-private"));
m_modelabels.emplace(mode::NODE_MARKED, load_optional_label(m_conf, name(), "label-marked"));
2016-11-02 19:22:45 +00:00
}
m_labelseparator = load_optional_label(m_conf, name(), "label-separator", "");
m_icons = std::make_shared<iconset>();
m_icons->add(DEFAULT_ICON, std::make_shared<label>(m_conf.get(name(), DEFAULT_ICON, ""s)));
2016-11-02 19:22:45 +00:00
int i = 0;
2016-11-25 12:55:15 +00:00
for (const auto& workspace : m_conf.get_list<string>(name(), "ws-icon", {})) {
auto vec = string_util::tokenize(workspace, ';');
2016-11-02 19:22:45 +00:00
if (vec.size() == 2) {
m_icons->add(vec[0], std::make_shared<label>(vec[1]));
} else {
m_log.err("%s: Ignoring ws-icon-%d because it has %s semicolons", name(), i, vec.size() > 2? "too many" : "too few");
2016-11-02 19:22:45 +00:00
}
i++;
2016-11-02 19:22:45 +00:00
}
2016-11-30 17:23:18 +00:00
}
2016-11-02 19:22:45 +00:00
2016-11-30 17:23:18 +00:00
void bspwm_module::stop() {
2016-11-02 19:22:45 +00:00
if (m_subscriber) {
m_log.info("%s: Disconnecting from socket", name());
m_subscriber->disconnect();
}
event_module::stop();
2016-11-30 17:23:18 +00:00
}
2016-11-02 19:22:45 +00:00
2016-11-30 17:23:18 +00:00
bool bspwm_module::has_event() {
2016-11-02 19:22:45 +00:00
if (m_subscriber->poll(POLLHUP, 0)) {
m_log.notice("%s: Reconnecting to socket...", name());
2016-11-02 19:22:45 +00:00
m_subscriber = bspwm_util::make_subscriber();
}
2016-12-14 06:44:41 +00:00
return m_subscriber->peek(1);
2016-11-30 17:23:18 +00:00
}
2016-11-02 19:22:45 +00:00
2016-11-30 17:23:18 +00:00
bool bspwm_module::update() {
2016-12-14 06:44:41 +00:00
if (!m_subscriber) {
return false;
}
string data{m_subscriber->receive(BUFSIZ)};
bool result = false;
2016-11-02 19:22:45 +00:00
for (auto&& status_line : string_util::split(data, '\n')) {
// Need to return true if ANY of the handle_status calls
// return true
result = this->handle_status(status_line) || result;
}
2016-11-02 19:22:45 +00:00
return result;
}
bool bspwm_module::handle_status(string& data) {
if (data.empty()) {
return false;
2016-11-25 12:55:15 +00:00
}
2016-11-02 19:22:45 +00:00
2016-12-14 06:45:29 +00:00
size_t prefix_len{strlen(BSPWM_STATUS_PREFIX)};
if (data.compare(0, prefix_len, BSPWM_STATUS_PREFIX) != 0) {
2016-11-02 19:22:45 +00:00
m_log.err("%s: Unknown status '%s'", name(), data);
return false;
}
2016-12-14 06:45:29 +00:00
string_util::hash_type hash;
2016-11-25 12:55:15 +00:00
if ((hash = string_util::hash(data)) == m_hash) {
2016-11-02 19:22:45 +00:00
return false;
2016-11-25 12:55:15 +00:00
}
2016-12-14 06:45:29 +00:00
2016-11-02 19:22:45 +00:00
m_hash = hash;
size_t pos;
2016-11-02 19:22:45 +00:00
// Extract the string for the defined monitor
if (m_pinworkspaces) {
const auto needle_active = ":M" + m_bar.monitor->name + ":";
const auto needle_inactive = ":m" + m_bar.monitor->name + ":";
2016-12-14 06:45:29 +00:00
if ((pos = data.find(BSPWM_STATUS_PREFIX)) != string::npos) {
data = data.replace(pos, prefix_len, ":");
2016-11-25 12:55:15 +00:00
}
if ((pos = data.find(needle_active)) != string::npos) {
data.erase(0, pos + 1);
2016-11-25 12:55:15 +00:00
}
if ((pos = data.find(needle_inactive)) != string::npos) {
data.erase(0, pos + 1);
2016-11-25 12:55:15 +00:00
}
if ((pos = data.find(":m", 1)) != string::npos) {
data.erase(pos);
2016-11-25 12:55:15 +00:00
}
if ((pos = data.find(":M", 1)) != string::npos) {
data.erase(pos);
2016-11-25 12:55:15 +00:00
}
2016-12-14 06:45:29 +00:00
} else if ((pos = data.find(BSPWM_STATUS_PREFIX)) != string::npos) {
data = data.replace(pos, prefix_len, ":");
} else {
return false;
}
m_log.info("%s: Parsing socket data: %s", name(), data);
m_monitors.clear();
2016-12-14 06:45:29 +00:00
size_t workspace_n{0U};
2016-11-02 19:22:45 +00:00
for (auto&& tag : string_util::split(data, ':')) {
auto value = tag.substr(1);
2016-11-30 20:03:28 +00:00
auto mode_flag = mode::NONE;
2017-01-19 10:11:28 +00:00
unsigned int workspace_mask{0U};
if (tag[0] == 'm' || tag[0] == 'M') {
m_monitors.emplace_back(std::make_unique<bspwm_monitor>());
m_monitors.back()->name = value;
if (m_monitorlabel) {
m_monitors.back()->label = m_monitorlabel->clone();
m_monitors.back()->label->replace_token("%name%", value);
}
}
2016-11-02 19:22:45 +00:00
switch (tag[0]) {
case 'm':
m_monitors.back()->focused = false;
2016-11-02 19:22:45 +00:00
break;
case 'M':
m_monitors.back()->focused = true;
2016-11-02 19:22:45 +00:00
break;
case 'F':
2016-11-30 20:03:28 +00:00
workspace_mask = make_mask(state::FOCUSED, state::EMPTY);
2016-11-02 19:22:45 +00:00
break;
case 'O':
2016-11-30 20:03:28 +00:00
workspace_mask = make_mask(state::FOCUSED, state::OCCUPIED);
break;
case 'U':
2016-11-30 20:03:28 +00:00
workspace_mask = make_mask(state::FOCUSED, state::URGENT);
break;
case 'f':
2016-11-30 20:03:28 +00:00
workspace_mask = make_mask(state::EMPTY);
2016-11-02 19:22:45 +00:00
break;
case 'o':
2016-11-30 20:03:28 +00:00
workspace_mask = make_mask(state::OCCUPIED);
2016-11-02 19:22:45 +00:00
break;
case 'u':
2016-11-30 20:03:28 +00:00
workspace_mask = make_mask(state::URGENT);
2016-11-02 19:22:45 +00:00
break;
case 'L':
switch (value[0]) {
case 0:
break;
case 'M':
2016-11-30 20:03:28 +00:00
mode_flag = mode::LAYOUT_MONOCLE;
2016-11-02 19:22:45 +00:00
break;
case 'T':
2016-11-30 20:03:28 +00:00
mode_flag = mode::LAYOUT_TILED;
2016-11-02 19:22:45 +00:00
break;
default:
m_log.warn("%s: Undefined L => '%s'", name(), value);
}
break;
case 'T':
switch (value[0]) {
case 0:
break;
case '@':
break;
2016-11-02 19:22:45 +00:00
case 'T':
break;
case '=':
2016-11-30 20:03:28 +00:00
mode_flag = mode::STATE_FULLSCREEN;
2016-11-02 19:22:45 +00:00
break;
case 'F':
2016-11-30 20:03:28 +00:00
mode_flag = mode::STATE_FLOATING;
2016-11-02 19:22:45 +00:00
break;
case 'P':
mode_flag = mode::STATE_PSEUDOTILED;
break;
2016-11-02 19:22:45 +00:00
default:
m_log.warn("%s: Undefined T => '%s'", name(), value);
}
break;
case 'G':
if (!m_monitors.back()->focused) {
break;
}
2016-12-14 06:44:41 +00:00
for (size_t i = 0U; i < value.length(); i++) {
2016-11-02 19:22:45 +00:00
switch (value[i]) {
case 0:
break;
case 'L':
2016-11-30 20:03:28 +00:00
mode_flag = mode::NODE_LOCKED;
2016-11-02 19:22:45 +00:00
break;
case 'S':
2016-11-30 20:03:28 +00:00
mode_flag = mode::NODE_STICKY;
2016-11-02 19:22:45 +00:00
break;
case 'P':
2016-11-30 20:03:28 +00:00
mode_flag = mode::NODE_PRIVATE;
2016-11-02 19:22:45 +00:00
break;
case 'M':
mode_flag = mode::NODE_MARKED;
break;
2016-11-02 19:22:45 +00:00
default:
m_log.warn("%s: Undefined G => '%s'", name(), value.substr(i, 1));
}
2016-11-30 20:03:28 +00:00
if (mode_flag != mode::NONE && !m_modelabels.empty()) {
m_monitors.back()->modes.emplace_back(m_modelabels.find(mode_flag)->second->clone());
}
2016-11-02 19:22:45 +00:00
}
continue;
default:
m_log.warn("%s: Undefined tag => '%s'", name(), tag.substr(0, 1));
continue;
}
if (!m_monitors.back()) {
m_log.warn("%s: No monitor created", name());
continue;
2016-11-02 19:22:45 +00:00
}
2016-11-30 20:03:28 +00:00
if (workspace_mask && m_formatter->has(TAG_LABEL_STATE)) {
auto icon = m_icons->get(value, DEFAULT_ICON, m_fuzzy_match);
2016-11-30 20:03:28 +00:00
auto label = m_statelabels.at(workspace_mask)->clone();
2016-11-02 19:22:45 +00:00
if (!m_monitors.back()->focused) {
2016-12-04 03:11:47 +00:00
if (m_statelabels[make_mask(state::DIMMED)]) {
2016-11-30 20:03:28 +00:00
label->replace_defined_values(m_statelabels[make_mask(state::DIMMED)]);
2016-12-04 03:11:47 +00:00
}
if (workspace_mask & make_mask(state::EMPTY)) {
2016-11-30 20:03:28 +00:00
label->replace_defined_values(m_statelabels[make_mask(state::DIMMED, state::EMPTY)]);
2016-12-04 03:11:47 +00:00
}
if (workspace_mask & make_mask(state::OCCUPIED)) {
2016-11-30 20:03:28 +00:00
label->replace_defined_values(m_statelabels[make_mask(state::DIMMED, state::OCCUPIED)]);
2016-12-04 03:11:47 +00:00
}
if (workspace_mask & make_mask(state::FOCUSED)) {
2016-11-30 20:03:28 +00:00
label->replace_defined_values(m_statelabels[make_mask(state::DIMMED, state::FOCUSED)]);
2016-12-04 03:11:47 +00:00
}
if (workspace_mask & make_mask(state::URGENT)) {
2016-11-30 20:03:28 +00:00
label->replace_defined_values(m_statelabels[make_mask(state::DIMMED, state::URGENT)]);
2016-12-04 03:11:47 +00:00
}
}
2016-11-02 19:22:45 +00:00
label->reset_tokens();
label->replace_token("%name%", value);
label->replace_token("%icon%", icon->get());
label->replace_token("%index%", to_string(++workspace_n));
2016-11-30 20:03:28 +00:00
m_monitors.back()->workspaces.emplace_back(workspace_mask, move(label));
2016-11-02 19:22:45 +00:00
}
2016-11-30 20:03:28 +00:00
if (mode_flag != mode::NONE && !m_modelabels.empty()) {
m_monitors.back()->modes.emplace_back(m_modelabels.find(mode_flag)->second->clone());
}
2016-11-02 19:22:45 +00:00
}
return true;
2016-11-30 17:23:18 +00:00
}
2016-11-02 19:22:45 +00:00
2016-11-30 17:23:18 +00:00
string bspwm_module::get_output() {
string output;
2016-12-14 06:44:41 +00:00
for (m_index = 0U; m_index < m_monitors.size(); m_index++) {
if (m_index > 0) {
Add units support (POINT, PIXEL, SPACE) (#2578) * add units support (POINT, PIXEL, SPACE) for polybar - add a size_with_unit struct - add a geometry_format_values struct - move dpi initialisation from renderer.cpp to bar.cpp - add a string to size_with_unit converter - add point support (with pt) - add pixel support (with px) * Fix unit test compilation * clang-format * Better names The old names didn't really capture the purpose of the structs and function. space_type -> spacing_type space_size -> spacing_val size_type -> extent_type geometry -> extent_val geometry_format_values -> percentage_with_offset * Remove parse_size_with_unit No longer needed. The convert<spacing_val> function in config.cpp already does all the work for us and always setting the type to pixel was wrong. In addition, line-size should not be of type spacing_val but extent_val. * Cleanup I tried to address most of my comments on the old PR * Fix renderer width calculation We can't just blindly add the x difference to the width because for example the width should increase if x < width and the increase keeps x < width. Similarly, we can't just add the offset to the width. * Rename geom_format_to_pixels to percentage_with_offset_to_pixel * Cleanup * Apply suggested changes from Patrick on GitHub Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com> * Update src/components/bar.cpp Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com> * Update src/components/config.cpp Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com> * Update src/components/builder.cpp Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com> * Update src/components/builder.cpp Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com> * config: Use stod for parsing percentage * Use stof instead of strtof * units: Fix test edge cases * Remove unnecessary clang-format toggle * Use percentage_with_offset for margin-{top,bottom} * Support negative extent values * Rename unit to units and create a cpp file * Move percentage_with_offset_to_pixel unit test to units * Add unit tests for units_utils * Clarify when and how negative spacing/extent is allowed Negative spacing is never allowed and produces a config error. Extents allow negative values in theory, but only a few use-cases accept it. Only the extent value used for the `%{O}` tag and the offset value in percentage_with_offset can be negative. Everything else is capped below at 0. The final pixel value of percentage_with_offset also caps below at 0. * Fix parsing errors not being caught in config * Print a proper error message for uncaught exceptions * Cleanup module::get_output All changes preserve the existing semantics * Stop using remove_trailing_space in module::get_output Instead, we first check if the current tag is built, and only if it is, the spacing is prepended. * Remove unused imports * Restore old behavior If there are two tags and the second one isn't built (module::build returns false), the space in between them is removed. For example in the mpd module: format-online = <toggle> <label-song> foo If mpd is not running, the mpd module will return false when trying to build the `<label-song>` tag. If we don't remove the space between `<toggle>` and `<label-song>`, we end up with two spaces between `<toggle>` and `foo`. This change is to match the old behavior where at least one trailing space character was removed from the builder. * Add changelog entry * Remove unused setting * Use percentage with offset for tray-offset Co-authored-by: Jérôme BOULMIER <jerome.boulmier@outlook.fr> Co-authored-by: Joe Groocock <github@frebib.net>
2022-02-20 20:08:57 +00:00
m_builder->spacing(m_formatter->get(DEFAULT_FORMAT)->spacing);
}
output += this->event_module::get_output();
}
return output;
2016-11-30 17:23:18 +00:00
}
2016-11-02 19:22:45 +00:00
2016-11-30 17:23:18 +00:00
bool bspwm_module::build(builder* builder, const string& tag) const {
if (tag == TAG_LABEL_MONITOR) {
builder->node(m_monitors[m_index]->label);
return true;
2016-11-30 20:03:28 +00:00
} else if (tag == TAG_LABEL_STATE && !m_monitors[m_index]->workspaces.empty()) {
2016-12-14 06:44:41 +00:00
size_t workspace_n{0U};
if (m_scroll) {
builder->action(mousebtn::SCROLL_DOWN, *this, m_revscroll ? EVENT_NEXT : EVENT_PREV, "");
builder->action(mousebtn::SCROLL_UP, *this, m_revscroll ? EVENT_PREV : EVENT_NEXT, "");
}
for (auto&& ws : m_monitors[m_index]->workspaces) {
if (ws.second.get()) {
if (workspace_n != 0 && *m_labelseparator) {
builder->node(m_labelseparator);
}
workspace_n++;
if (m_click) {
builder->action(mousebtn::LEFT, *this, EVENT_FOCUS, sstream() << m_index << "+" << workspace_n, ws.second);
} else {
builder->node(ws.second);
}
if (m_inlinemode && m_monitors[m_index]->focused && check_mask(ws.first, bspwm_state::FOCUSED)) {
for (auto&& mode : m_monitors[m_index]->modes) {
builder->node(mode);
}
}
}
}
if (m_scroll) {
builder->action_close();
builder->action_close();
}
2016-11-02 19:22:45 +00:00
return workspace_n > 0;
} else if (tag == TAG_LABEL_MODE && !m_inlinemode && m_monitors[m_index]->focused &&
!m_monitors[m_index]->modes.empty()) {
int modes_n = 0;
2016-11-02 19:22:45 +00:00
for (auto&& mode : m_monitors[m_index]->modes) {
if (mode && *mode) {
builder->node(mode);
modes_n++;
}
2016-11-02 19:22:45 +00:00
}
return modes_n > 0;
2016-11-02 19:22:45 +00:00
}
return false;
2016-11-30 17:23:18 +00:00
}
2016-11-02 19:22:45 +00:00
void bspwm_module::action_focus(const string& data) {
size_t separator{string_util::find_nth(data, 0, "+", 1)};
size_t monitor_n{std::strtoul(data.substr(0, separator).c_str(), nullptr, 10)};
string workspace_n{data.substr(separator + 1)};
if (monitor_n < m_monitors.size()) {
send_command("desktop -f " + m_monitors[monitor_n]->name + ":^" + workspace_n,
"Sending desktop focus command to ipc handler");
} else {
m_log.err("%s: Invalid monitor index in command: %s", name(), data);
2016-11-02 19:22:45 +00:00
}
}
void bspwm_module::action_next() {
focus_direction(true);
}
2016-11-02 19:22:45 +00:00
void bspwm_module::action_prev() {
focus_direction(false);
}
void bspwm_module::focus_direction(bool next) {
string scrolldir = next ? "next" : "prev";
string modifier;
if (m_occscroll) {
modifier += ".occupied";
}
if (m_pinworkspaces) {
modifier += ".local";
for (const auto& mon : m_monitors) {
if (m_bar.monitor->match(mon->name, false) && !mon->focused) {
send_command("monitor -f " + mon->name, "Sending monitor focus command to ipc handler");
break;
}
}
}
send_command("desktop -f " + scrolldir + modifier, "Sending desktop " + scrolldir + " command to ipc handler");
}
void bspwm_module::send_command(const string& payload_cmd, const string& log_info) {
auto ipc = bspwm_util::make_connection();
auto payload = bspwm_util::make_payload(payload_cmd);
m_log.info("%s: %s", name(), log_info);
ipc->send(payload->data, payload->len, 0);
ipc->disconnect();
2016-11-30 17:23:18 +00:00
}
} // namespace modules
2016-11-02 19:22:45 +00:00
2016-11-19 05:22:44 +00:00
POLYBAR_NS_END