refactor(bspwm): Better socket handling

- Reconnect on POLLHUP
- Switch desktop using socket instead of std::system()

Fixes jaagr/lemonbuddy#27
This commit is contained in:
Michael Carlberg 2016-06-28 04:59:28 +02:00
parent 99cb53a565
commit b8a1dd628e
2 changed files with 85 additions and 27 deletions

View File

@ -10,8 +10,13 @@
namespace modules
{
namespace Bspwm
namespace bspwm
{
typedef struct payload_t {
char data[BUFSIZ];
size_t len = 0;
} payload_t;
enum Flag
{
WORKSPACE_NONE,
@ -53,10 +58,10 @@ namespace modules
static constexpr auto EVENT_CLICK = "bwm";
std::map<Bspwm::Flag, std::unique_ptr<drawtypes::Label>> mode_labels;
std::map<Bspwm::Flag, std::unique_ptr<drawtypes::Label>> state_labels;
std::map<bspwm::Flag, std::unique_ptr<drawtypes::Label>> mode_labels;
std::map<bspwm::Flag, std::unique_ptr<drawtypes::Label>> state_labels;
std::vector<std::unique_ptr<Bspwm::Workspace>> workspaces;
std::vector<std::unique_ptr<bspwm::Workspace>> workspaces;
std::vector<std::unique_ptr<drawtypes::Label>*> modes;
std::unique_ptr<drawtypes::IconMap> icons;
@ -67,7 +72,7 @@ namespace modules
public:
BspwmModule(std::string name, std::string monitor);
~BspwmModule() { close(this->socket_fd); }
~BspwmModule();
void start();
bool has_event();

View File

@ -1,6 +1,7 @@
#include <thread>
#include <vector>
#include <sstream>
#include <sys/socket.h>
#include "config.hpp"
#include "lemonbuddy.hpp"
@ -12,13 +13,45 @@
#include "utils/string.hpp"
using namespace modules;
using namespace Bspwm;
using namespace bspwm;
#define DEFAULT_WS_ICON "workspace_icon-default"
#define DEFAULT_WS_LABEL "%icon% %name%"
bspwm::payload_t generate_payload(std::string command)
{
bspwm::payload_t payload;
size_t size = sizeof(payload.data);
int offset = 0, chars = 0;
for (auto &&sub: string::split(command, ' ')) {
chars = snprintf(payload.data + offset, size - offset, "%s%c", sub.c_str(), 0);
payload.len += chars;
offset += chars;
}
return payload;
}
bool send_payload(int fd, bspwm::payload_t payload)
{
int bytes = 0;
if ((bytes = send(fd, payload.data, payload.len, 0)) == -1)
log_debug("bspwm: Failed sending message to socket");
return bytes > 0;
}
int create_subscriber()
{
int socket_fd;
if ((socket_fd = io::socket::open(BSPWM_SOCKET_PATH)) == -1)
throw ModuleError("Could not connect to socket");
if (!send_payload(socket_fd, generate_payload("subscribe report")))
throw ModuleError("Failed to subscribe to bspwm changes");
return socket_fd;
}
BspwmModule::BspwmModule(std::string name_, std::string monitor)
: EventModule(name_), monitor(monitor)
: EventModule(name_)
, monitor(monitor)
{
this->formatter->add(DEFAULT_FORMAT, TAG_LABEL_STATE, { TAG_LABEL_STATE }, { TAG_LABEL_MODE });
@ -51,28 +84,34 @@ BspwmModule::BspwmModule(std::string name_, std::string monitor)
register_command_handler(name());
}
BspwmModule::~BspwmModule()
{
if (this->socket_fd > -1)
close(this->socket_fd);
}
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->socket_fd = create_subscriber();
this->EventModule<BspwmModule>::start();
}
bool BspwmModule::has_event() {
return io::poll_read(this->socket_fd, 100);
bool BspwmModule::has_event()
{
if (io::poll(this->socket_fd, POLLHUP, 0)) {
close(this->socket_fd);
this->logger->warning("bspwm: Reconnecting to socket...");
this->socket_fd = create_subscriber();
}
return io::poll(this->socket_fd, POLLIN, 100);
}
bool BspwmModule::update()
{
std::string data;
auto bytes_read = 0;
auto data = io::readline(this->socket_fd, bytes_read);
if ((data = io::readline(this->socket_fd)).empty())
return false;
if (data == this->prev_data)
if (bytes_read <= 0 || data.empty() || data == this->prev_data)
return false;
this->prev_data = data;
@ -88,7 +127,7 @@ bool BspwmModule::update()
const auto prefix = std::string(BSPWM_STATUS_PREFIX);
if (data.compare(0, prefix.length(), prefix) != 0) {
log_error("Received unknown status -> "+ data);
this->logger->error("bspwm: Received unknown status -> "+ data);
return false;
}
@ -109,7 +148,7 @@ bool BspwmModule::update()
if (data.compare(0, prefix.length(), prefix) == 0)
data.erase(0, 1);
log_trace(data);
log_trace2(this->logger, data);
this->modes.clear();
this->workspaces.clear();
@ -138,7 +177,7 @@ bool BspwmModule::update()
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);
default: this->logger->warning("bspwm: Undefined L => "+ value);
}
break;
@ -148,7 +187,7 @@ bool BspwmModule::update()
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);
default: this->logger->warning("bspwm: Undefined T => "+ value);
}
break;
@ -160,14 +199,14 @@ bool BspwmModule::update()
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));
default: this->logger->warning("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));
default: this->logger->warning("bspwm: Undefined tag => "+ tag.substr(0, 1));
}
if (workspace_flag != WORKSPACE_NONE && this->formatter->has(TAG_LABEL_STATE)) {
@ -223,8 +262,22 @@ bool BspwmModule::handle_command(std::string cmd)
if (cmd.find(EVENT_CLICK) == std::string::npos || cmd.length() <= std::strlen(EVENT_CLICK))
return false;
int status = std::system(("bspc desktop -f "+ this->monitor +":^"+ cmd.substr(std::strlen(EVENT_CLICK))).c_str());
log_trace(std::to_string(status));
std::stringstream payload_s;
payload_s
<< "desktop -f "
<< this->monitor
<< ":^"
<< std::atoi(cmd.substr(std::strlen(EVENT_CLICK)).c_str());
int payload_fd;
if ((payload_fd = io::socket::open(BSPWM_SOCKET_PATH)) == -1)
this->logger->error("bspwm: Failed to open socket");
else if (!send_payload(payload_fd, generate_payload(payload_s.str())))
this->logger->error("bspwm: Failed to change desktop");
close(payload_fd);
return true;
}