polybar-dwm/src/modules/bspwm.cpp
2016-05-19 20:23:45 +02:00

224 lines
7.9 KiB
C++

#include <thread>
#include <vector>
#include <sstream>
#include "config.hpp"
#include "lemonbuddy.hpp"
#include "bar.hpp"
#include "modules/bspwm.hpp"
#include "utils/config.hpp"
#include "utils/io.hpp"
#include "utils/memory.hpp"
#include "utils/string.hpp"
using namespace modules;
using namespace Bspwm;
#define DEFAULT_WS_ICON "workspace_icon:default"
#define DEFAULT_WS_LABEL "%icon% %name%"
BspwmModule::BspwmModule(const std::string& name_, const std::string& monitor) : EventModule(name_)
{
this->monitor = monitor;
this->formatter->add(DEFAULT_FORMAT, TAG_LABEL_STATE, { TAG_LABEL_STATE }, { TAG_LABEL_MODE });
if (this->formatter->has(TAG_LABEL_STATE)) {
this->state_labels.insert(std::make_pair(WORKSPACE_ACTIVE, drawtypes::get_optional_config_label(name(), "label:active", DEFAULT_WS_LABEL)));
this->state_labels.insert(std::make_pair(WORKSPACE_OCCUPIED, drawtypes::get_optional_config_label(name(), "label:occupied", DEFAULT_WS_LABEL)));
this->state_labels.insert(std::make_pair(WORKSPACE_URGENT, drawtypes::get_optional_config_label(name(), "label:urgent", DEFAULT_WS_LABEL)));
this->state_labels.insert(std::make_pair(WORKSPACE_EMPTY, drawtypes::get_optional_config_label(name(), "label:empty", DEFAULT_WS_LABEL)));
this->state_labels.insert(std::make_pair(WORKSPACE_DIMMED, drawtypes::get_optional_config_label(name(), "label:dimmed")));
}
if (this->formatter->has(TAG_LABEL_MODE)) {
this->mode_labels.insert(std::make_pair(MODE_LAYOUT_MONOCLE, drawtypes::get_optional_config_label(name(), "label:monocle")));
this->mode_labels.insert(std::make_pair(MODE_LAYOUT_TILED, drawtypes::get_optional_config_label(name(), "label:tiled")));
this->mode_labels.insert(std::make_pair(MODE_STATE_FULLSCREEN, drawtypes::get_optional_config_label(name(), "label:fullscreen")));
this->mode_labels.insert(std::make_pair(MODE_STATE_FLOATING, drawtypes::get_optional_config_label(name(), "label:floating")));
this->mode_labels.insert(std::make_pair(MODE_NODE_LOCKED, drawtypes::get_optional_config_label(name(), "label:locked")));
this->mode_labels.insert(std::make_pair(MODE_NODE_STICKY, drawtypes::get_optional_config_label(name(), "label:sticky")));
this->mode_labels.insert(std::make_pair(MODE_NODE_PRIVATE, drawtypes::get_optional_config_label(name(), "label:private")));
}
this->icons = std::make_unique<drawtypes::IconMap>();
this->icons->add(DEFAULT_WS_ICON, std::make_unique<drawtypes::Icon>(config::get<std::string>(name(), DEFAULT_WS_ICON, "")));
for (auto workspace : config::get_list<std::string>(name(), "workspace_icon", {})) {
auto vec = string::split(workspace, ';');
if (vec.size() == 2) this->icons->add(vec[0], std::make_unique<drawtypes::Icon>(vec[1]));
}
register_command_handler(name());
}
void BspwmModule::start()
{
this->socket_fd = io::socket::open(BSPWM_SOCKET_PATH);
if (io::socket::send(this->socket_fd, "subscribe\x00report") == 0)
throw ModuleError("Failed to subscribe to bspwm changes");
this->EventModule<BspwmModule>::start();
}
bool BspwmModule::has_event() {
return io::poll_read(this->socket_fd, 100);
}
bool BspwmModule::update()
{
std::string data;
if ((data = io::readline(this->socket_fd)).empty())
return false;
if (data == this->prev_data)
return false;
this->prev_data = data;
unsigned long n, m;
while ((n = data.find("\n")) != std::string::npos)
data.erase(n);
if (data.empty())
return false;
auto prefix = std::string(BSPWM_STATUS_PREFIX);
if (data.find(prefix) != 0) {
log_error("Received unknown status -> "+ data);
return false;
}
// Cut out the relevant section for the current monitor
if ((n = data.find(prefix +"M"+ this->monitor +":")) != std::string::npos) {
if ((m = data.find(":m")) != std::string::npos) data = data.substr(n, m);
} else if ((n = data.find(prefix +"m"+ this->monitor +":")) != std::string::npos) {
if ((m = data.find(":M")) != std::string::npos) data = data.substr(n, m);
} else if ((n = data.find("M"+ this->monitor +":")) != std::string::npos) {
data.erase(0, n);
} else if ((n = data.find("m"+ this->monitor +":")) != std::string::npos) {
data.erase(0, n);
}
if (data.find(prefix) == 0) data.erase(0, 1);
log_trace(data);
this->modes.clear();
this->workspaces.clear();
bool monitor_focused = true;
int workspace_n = 0;
for (auto &&tag : string::split(data, ':')) {
if (tag.empty()) continue;
auto value = tag.size() > 0 ? tag.substr(1) : "";
auto workspace_flag = WORKSPACE_NONE;
auto mode_flag = MODE_NONE;
switch (tag[0]) {
case 'm': monitor_focused = false; break;
case 'M': monitor_focused = true; break;
case 'F': workspace_flag = WORKSPACE_ACTIVE; break;
case 'O': workspace_flag = WORKSPACE_ACTIVE; break;
case 'o': workspace_flag = WORKSPACE_OCCUPIED; break;
case 'U': workspace_flag = WORKSPACE_URGENT; break;
case 'u': workspace_flag = WORKSPACE_URGENT; break;
case 'f': workspace_flag = WORKSPACE_EMPTY; break;
case 'L':
switch (value[0]) {
case 0: break;
case 'M': mode_flag = MODE_LAYOUT_MONOCLE; break;
case 'T': mode_flag = MODE_LAYOUT_TILED; break;
default: log_warning("[modules::Bspwm] Undefined L => "+ value);
}
break;
case 'T':
switch (value[0]) {
case 0: break;
case 'T': break;
case '=': mode_flag = MODE_STATE_FULLSCREEN; break;
case 'F': mode_flag = MODE_STATE_FLOATING; break;
default: log_warning("[modules::Bspwm] Undefined T => "+ value);
}
break;
case 'G':
repeat(value.length())
{
switch (value[repeat_i]) {
case 0: break;
case 'L': mode_flag = MODE_NODE_LOCKED; break;
case 'S': mode_flag = MODE_NODE_STICKY; break;
case 'P': mode_flag = MODE_NODE_PRIVATE; break;
default: log_warning("[modules::Bspwm] Undefined G => "+ value.substr(repeat_i, 1));
}
if (mode_flag != MODE_NONE && !this->mode_labels.empty())
this->modes.emplace_back(&this->mode_labels.find(mode_flag)->second);
}
continue;
default: log_warning("[modules::Bspwm] Undefined tag => "+ tag.substr(0, 1));
}
if (workspace_flag != WORKSPACE_NONE && this->formatter->has(TAG_LABEL_STATE)) {
std::unique_ptr<drawtypes::Icon> &icon = this->icons->get(value, DEFAULT_WS_ICON);
std::unique_ptr<drawtypes::Label> label = this->state_labels.find(workspace_flag)->second->clone();
if (!monitor_focused)
label->replace_defined_values(this->state_labels.find(WORKSPACE_DIMMED)->second);
label->replace_token("%name%", value);
label->replace_token("%icon%", icon->text);
label->replace_token("%index%", std::to_string(++workspace_n));
this->workspaces.emplace_back(std::make_unique<Workspace>(workspace_flag, std::move(label)));
}
if (mode_flag != MODE_NONE && !this->mode_labels.empty())
this->modes.emplace_back(&this->mode_labels.find(mode_flag)->second);
}
if (!monitor_focused) this->modes.clear();
return true;
}
bool BspwmModule::build(Builder *builder, const std::string& tag)
{
if (tag != TAG_LABEL_STATE)
return false;
int workspace_n = 0;
for (auto &&ws : this->workspaces) {
builder->cmd(Cmd::LEFT_CLICK, std::string(EVENT_CLICK) + std::to_string(++workspace_n));
builder->node(ws.get()->label);
if (ws->flag == WORKSPACE_ACTIVE && this->formatter->has(TAG_LABEL_MODE)) {
for (auto &&mode : this->modes)
builder->node(mode->get());
}
builder->cmd_close(true);
}
return true;
}
bool BspwmModule::handle_command(const std::string& cmd)
{
if (cmd.find(EVENT_CLICK) == std::string::npos || cmd.length() <= std::strlen(EVENT_CLICK))
return false;
std::system(("bspc desktop -f "+ this->monitor +":^"+ cmd.substr(std::strlen(EVENT_CLICK))).c_str());
return true;
}