commit 73a48d19aa6ade265811bc8ebc19eae0eb400bb8
parent 8a0056464b8cf801a46a046a6e58c7f85ed624e1
Author: deurzen <m.deurzen@tum.de>
Date: Fri, 6 Aug 2021 03:22:06 +0200
adds initial IPC message handling functionality
Diffstat:
9 files changed, 434 insertions(+), 34 deletions(-)
diff --git a/src/core/main.cc b/src/core/main.cc
@@ -1,12 +1,13 @@
#include <iostream>
#include "../winsys/xdata/xconnection.hh"
+#include "defaults.hh"
#include "model.hh"
int
main(int, char **)
{
- XConnection conn = {};
+ XConnection conn = { WM_NAME };
Model model(conn);
model.run();
return 0;
diff --git a/src/core/model.cc b/src/core/model.cc
@@ -7,14 +7,18 @@
#include <cstdlib>
#include <cstring>
#include <fstream>
+#include <functional>
#include <iostream>
#include <set>
#include <sstream>
-#include <sys/wait.h>
-#include <unistd.h>
#include <unordered_set>
#include <vector>
+extern "C" {
+#include <sys/wait.h>
+#include <unistd.h>
+}
+
#ifdef DEBUG
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#endif
@@ -863,13 +867,29 @@ Model::~Model()
m_client_map.clear();
}
+
void
Model::run()
{
while (m_running)
- std::visit(event_visitor, m_conn.step());
+ if (m_conn.block()) {
+ // process IPC message
+ m_conn.process_messages(
+ [=,this](winsys::Message message) {
+ std::visit(m_message_visitor, message);
+ }
+ );
+
+ // process windowing system event
+ m_conn.process_events(
+ [=,this](winsys::Event event) {
+ std::visit(m_event_visitor, event);
+ }
+ );
+ }
}
+
void
Model::init_signals() const
{
@@ -3978,6 +3998,119 @@ Model::handle_screen_change()
}
+void
+Model::process_command(winsys::CommandMessage message)
+{
+ static const std::unordered_map<std::string_view, winsys::Direction> directions = {
+ { "Forward", winsys::Direction::Forward },
+ { "forward", winsys::Direction::Forward },
+ { "fwd", winsys::Direction::Forward },
+ { "Backward", winsys::Direction::Backward },
+ { "backward", winsys::Direction::Backward },
+ { "bwd", winsys::Direction::Backward },
+ };
+
+ static const std::unordered_map<std::string_view, std::function<void(void)>> commands = {
+ { "toggle_partition",
+ [&,this]() {
+ toggle_partition();
+ }
+ },
+ { "activate_next_partition",
+ [&,this]() {
+ if (!message.args.empty() && directions.count(message.args.front()))
+ activate_next_partition(directions.at(message.args.front()));
+ }
+ },
+ { "activate_partition",
+ [&,this]() {
+ if (!message.args.empty()) {
+ std::istringstream index_stream(message.args.front());
+ Index index = 0;
+
+ if (index_stream >> index)
+ activate_partition(index);
+ }
+ }
+ },
+
+ { "toggle_context",
+ [&,this]() {
+ toggle_context();
+ }
+ },
+ { "activate_next_context",
+ [&,this]() {
+ if (!message.args.empty() && directions.count(message.args.front()))
+ activate_next_context(directions.at(message.args.front()));
+ }
+ },
+ { "activate_context",
+ [&,this]() {
+ if (!message.args.empty()) {
+ std::istringstream index_stream(message.args.front());
+ Index index = 0;
+
+ if (index_stream >> index)
+ activate_context(index);
+ }
+ }
+ },
+
+ { "toggle_workspace",
+ [&,this]() {
+ toggle_workspace();
+ }
+ },
+ { "activate_next_workspace",
+ [&,this]() {
+ if (!message.args.empty() && directions.count(message.args.front()))
+ activate_next_workspace(directions.at(message.args.front()));
+ }
+ },
+ { "activate_workspace",
+ [&,this]() {
+ if (!message.args.empty()) {
+ std::istringstream index_stream(message.args.front());
+ Index index = 0;
+
+ if (index_stream >> index)
+ activate_workspace(index);
+ }
+ }
+ },
+ };
+
+ if (!message.args.empty()) {
+ std::string command = message.args.front();
+ message.args.pop_front();
+
+ if (commands.count(command) > 0)
+ commands.at(command)();
+ }
+}
+
+void
+Model::process_config(winsys::ConfigMessage message)
+{
+}
+
+void
+Model::process_client(winsys::WindowMessage message)
+{
+}
+
+void
+Model::process_workspace(winsys::WorkspaceMessage message)
+{
+}
+
+void
+Model::process_query(winsys::QueryMessage message)
+{
+}
+
+
// static methods
void
diff --git a/src/core/model.hh b/src/core/model.hh
@@ -55,6 +55,12 @@ private:
void handle_frame_extents_request(winsys::FrameExtentsRequestEvent);
void handle_screen_change();
+ void process_command(winsys::CommandMessage);
+ void process_config(winsys::ConfigMessage);
+ void process_client(winsys::WindowMessage);
+ void process_workspace(winsys::WorkspaceMessage);
+ void process_query(winsys::QueryMessage);
+
void acquire_partitions();
const winsys::Screen& active_screen() const;
@@ -324,7 +330,38 @@ private:
private:
Model& m_model;
- } event_visitor = EventVisitor(*this);
+ } m_event_visitor = EventVisitor(*this);
+
+ struct MessageVisitor final
+ {
+ MessageVisitor(Model& model): m_model(model) {}
+
+ void operator()(std::monostate) {}
+
+ void operator()(winsys::CommandMessage message) {
+ m_model.process_command(message);
+ }
+
+ void operator()(winsys::ConfigMessage message) {
+ m_model.process_config(message);
+ }
+
+ void operator()(winsys::WindowMessage message) {
+ m_model.process_client(message);
+ }
+
+ void operator()(winsys::WorkspaceMessage message) {
+ m_model.process_workspace(message);
+ }
+
+ void operator()(winsys::QueryMessage message) {
+ m_model.process_query(message);
+ }
+
+ private:
+ Model& m_model;
+
+ } m_message_visitor = MessageVisitor(*this);
};
diff --git a/src/winsys/connection.hh b/src/winsys/connection.hh
@@ -1,18 +1,20 @@
#ifndef __WINSYS_CONNECTION_H_GUARD__
#define __WINSYS_CONNECTION_H_GUARD__
-#include <optional>
-#include <string>
-#include <tuple>
-#include <unordered_set>
-#include <vector>
-
#include "common.hh"
#include "event.hh"
#include "hints.hh"
+#include "message.hh"
#include "screen.hh"
#include "window.hh"
+#include <functional>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <unordered_set>
+#include <vector>
+
namespace winsys
{
@@ -22,7 +24,9 @@ namespace winsys
virtual ~Connection() {};
virtual bool flush() = 0;
- virtual Event step() = 0;
+ virtual bool block() = 0;
+ virtual void process_events(std::function<void(Event)>) = 0;
+ virtual void process_messages(std::function<void(Message)>) = 0;
virtual std::vector<Screen> connected_outputs() = 0;
virtual std::vector<Window> top_level_windows() = 0;
virtual Pos get_pointer_position() = 0;
diff --git a/src/winsys/event.hh b/src/winsys/event.hh
@@ -10,8 +10,6 @@
#include <optional>
#include <variant>
-#include "spdlog/spdlog.h"
-
namespace winsys
{
diff --git a/src/winsys/message.hh b/src/winsys/message.hh
@@ -0,0 +1,52 @@
+#ifndef __WINSYS_MESSAGE_H_GUARD__
+#define __WINSYS_MESSAGE_H_GUARD__
+
+#include "common.hh"
+#include "geometry.hh"
+#include "input.hh"
+#include "window.hh"
+
+#include <string>
+#include <variant>
+#include <deque>
+
+namespace winsys
+{
+
+ struct CommandMessage final
+ {
+ std::deque<std::string> args;
+ };
+
+ struct ConfigMessage final
+ {
+ std::deque<std::string> args;
+ };
+
+ struct WindowMessage final
+ {
+ std::deque<std::string> args;
+ };
+
+ struct WorkspaceMessage final
+ {
+ std::deque<std::string> args;
+ };
+
+ struct QueryMessage final
+ {
+ std::deque<std::string> args;
+ };
+
+ typedef std::variant<
+ std::monostate,
+ CommandMessage,
+ ConfigMessage,
+ WindowMessage,
+ WorkspaceMessage,
+ QueryMessage
+ > Message;
+
+}
+
+#endif//__WINSYS_MESSAGE_H_GUARD__
diff --git a/src/winsys/util.cc b/src/winsys/util.cc
@@ -1,6 +1,8 @@
#include "util.hh"
+extern "C" {
#include <unistd.h>
+}
void
Util::die(const std::string&& msg)
diff --git a/src/winsys/xdata/xconnection.cc b/src/winsys/xdata/xconnection.cc
@@ -2,12 +2,13 @@
#include "../util.hh"
#include "xconnection.hh"
+#include <algorithm>
#include <cstring>
+#include <iomanip>
#include <iterator>
#include <numeric>
-#include <proc/readproc.h>
#include <sstream>
-#include <unistd.h>
+#include <deque>
extern "C" {
#include <X11/XF86keysym.h>
@@ -18,18 +19,22 @@ extern "C" {
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
#include <X11/keysymdef.h>
+#include <fcntl.h>
+#include <proc/readproc.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <xcb/xcb.h>
+#include <xcb/xproto.h>
#include <xkbcommon/xkbcommon-keysyms.h>
}
-#include <xcb/xcb.h>
-#include <xcb/xproto.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
-XConnection::XConnection()
+XConnection::XConnection(const std::string_view wm_name)
: mp_dpy(XOpenDisplay(NULL)),
- m_conn_number(XConnectionNumber(mp_dpy)),
m_root(XDefaultRootWindow(mp_dpy)),
m_check_window(XCreateWindow(
mp_dpy,
@@ -39,7 +44,10 @@ XConnection::XConnection()
CopyFromParent, 0, InputOnly,
CopyFromParent, 0, NULL
)),
- m_conn(XConnectionNumber(mp_dpy)),
+ m_dpy_fd(XConnectionNumber(mp_dpy)),
+ m_sock_fd(-1),
+ m_client_fd(-1),
+ m_max_fd(-1),
m_interned_atoms({}),
m_atom_names({}),
m_keys({}),
@@ -118,6 +126,76 @@ XConnection::XConnection()
m_event_dispatcher[event_base + RRScreenChangeNotify]
= &XConnection::on_screen_change;
}
+
+ std::string socket_name = wm_name.data() + std::string("_SOCKET");
+ std::string wm_socket;
+
+ if (const char* env_wm_socket = std::getenv(socket_name.c_str()))
+ wm_socket = std::string(env_wm_socket);
+ else {
+ static const std::string delim = "_";
+ std::string display_string = XDisplayString(mp_dpy);
+ int default_screen = XDefaultScreen(mp_dpy);
+
+ std::string host_name = "localhost";
+ std::string display_nr;
+ std::string screen_nr = std::to_string(default_screen);
+
+ std::string::size_type display_nr_pos = display_string.find_last_of(':');
+
+ if (display_nr_pos != std::string::npos) {
+ if (display_nr_pos != 0)
+ host_name = display_string.substr(0, display_nr_pos);
+
+ display_string = display_string.substr(display_nr_pos + 1);
+ }
+
+ std::string::size_type screen_nr_pos = display_string.find_last_of('.');
+
+ if (screen_nr_pos != std::string::npos
+ && display_nr_pos != std::string::npos
+ && display_nr_pos < screen_nr_pos)
+ {
+ display_nr = display_string.substr(display_nr_pos + 1, screen_nr_pos - (display_nr_pos + 1));
+ } else
+ display_nr = display_string.substr(0, screen_nr_pos);
+
+ wm_socket = "/tmp/"
+ + std::string(wm_name)
+ + delim + host_name
+ + delim + display_nr
+ + delim + screen_nr;
+ }
+
+ memset(m_sock_path, 0, 256);
+ memset(&m_sock_addr, 0, sizeof(m_sock_addr));
+
+ strncpy(m_sock_path,
+ wm_socket.c_str(),
+ wm_socket.length() < 256
+ ? wm_socket.length()
+ : 255
+ );
+
+ m_sock_addr.sun_family = AF_UNIX;
+
+ if (snprintf(m_sock_addr.sun_path, sizeof(m_sock_addr.sun_path), "%s", m_sock_path) < 0)
+ Util::die("unable to write IPC socket path");
+
+ m_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (m_sock_fd == -1)
+ Util::die("unable to set up IPC socket");
+
+ unlink(m_sock_path);
+
+ if (bind(m_sock_fd, (struct sockaddr*)&m_sock_addr, sizeof(m_sock_addr)) == -1)
+ Util::die("unable to bind name to IPC socket");
+
+ if (listen(m_sock_fd, SOMAXCONN) == -1)
+ Util::die("unable to listen to IPC socket");
+
+ fcntl(m_sock_fd, F_SETFD, FD_CLOEXEC | fcntl(m_sock_fd, F_GETFD));
}
XConnection::~XConnection()
@@ -131,15 +209,98 @@ XConnection::flush()
return true;
}
-winsys::Event
-XConnection::step()
+bool
+XConnection::block()
{
- next_event(m_current_event);
+ XFlush(mp_dpy);
- if (m_current_event.type >= 0 && m_current_event.type <= 256)
- return (this->*(m_event_dispatcher[m_current_event.type]))();
+ FD_ZERO(&m_descr);
+ FD_SET(m_sock_fd, &m_descr);
+ FD_SET(m_dpy_fd, &m_descr);
+ m_max_fd = std::max(m_sock_fd, m_dpy_fd);
- return std::monostate{};
+ return select(m_max_fd + 1, &m_descr, NULL, NULL, NULL) > 0;
+}
+
+void
+XConnection::process_events(std::function<void(winsys::Event)> callback)
+{
+ if (FD_ISSET(m_dpy_fd, &m_descr)) {
+ while (XPending(mp_dpy)) {
+ next_event(m_current_event);
+
+ if (m_current_event.type >= 0 && m_current_event.type <= 256)
+ callback((this->*(m_event_dispatcher[m_current_event.type]))());
+ }
+ }
+}
+
+void
+XConnection::process_messages(std::function<void(winsys::Message)> callback)
+{
+ enum MessageType {
+ Command,
+ Config,
+ Window,
+ Workspace,
+ Query
+ };
+
+ static const std::unordered_map<std::string_view, MessageType> message_types = {
+ { "command", Command },
+ { "cmd", Command },
+ { "config", Config },
+ { "conf", Config },
+ { "cfg", Config },
+ { "client", Window },
+ { "cli", Window },
+ { "workspace", Workspace },
+ { "ws", Workspace },
+ { "query", Query },
+ { "qr", Query },
+ };
+
+ static int n = 0;
+ static char msg[BUFSIZ] = { 0 };
+
+ if (FD_ISSET(m_sock_fd, &m_descr)) {
+ m_client_fd = accept(m_sock_fd, NULL, 0);
+
+ if (m_client_fd > 0 && (n = recv(m_client_fd, msg, sizeof(msg) - 1, 0)) > 0) {
+ msg[n] = '\0';
+ FILE* rply = fdopen(m_client_fd, "w");
+
+ if (rply != NULL) {
+ winsys::Message message = std::monostate{};
+
+ std::deque<std::string> words = {};
+ std::istringstream word_stream(msg);
+ std::string word;
+
+ while (word_stream >> std::quoted(word))
+ words.push_back(word);
+
+ if (!words.empty()) {
+ std::string_view area = words.front();
+
+ if (message_types.count(area) > 0) {
+ words.pop_front();
+
+ switch (message_types.at(area)) {
+ case Command: message = winsys::CommandMessage{words}; break;
+ case Config: message = winsys::ConfigMessage{words}; break;
+ case Window: message = winsys::WindowMessage{words}; break;
+ case Workspace: message = winsys::WorkspaceMessage{words}; break;
+ case Query: message = winsys::QueryMessage{words}; break;
+ }
+ }
+ }
+
+ callback(message);
+ } else
+ close(m_client_fd);
+ }
+ }
}
std::vector<winsys::Screen>
@@ -319,7 +480,7 @@ XConnection::call_external_command(std::string& command)
{
if (!fork()) {
if (mp_dpy)
- close(m_conn_number);
+ close(m_dpy_fd);
setsid();
execl("/bin/sh", "/bin/sh", "-c", ("exec " + command).c_str(), NULL);
diff --git a/src/winsys/xdata/xconnection.hh b/src/winsys/xdata/xconnection.hh
@@ -6,26 +6,30 @@
#include "../input.hh"
#include <cstddef>
+#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
#include <variant>
extern "C" {
-#include <X11/Xlib.h>
-#include <X11/cursorfont.h>
#include <X11/Xatom.h>
+#include <X11/Xlib.h>
#include <X11/Xmd.h>
+#include <X11/cursorfont.h>
+#include <sys/un.h>
}
class XConnection final: public winsys::Connection
{
public:
- XConnection();
+ XConnection(const std::string_view);
~XConnection();
virtual bool flush() override;
- virtual winsys::Event step() override;
+ virtual bool block() override;
+ virtual void process_events(std::function<void(winsys::Event)>) override;
+ virtual void process_messages(std::function<void(winsys::Message)>) override;
virtual std::vector<winsys::Screen> connected_outputs() override;
virtual std::vector<winsys::Window> top_level_windows() override;
virtual winsys::Pos get_pointer_position() override;
@@ -175,10 +179,18 @@ private:
}
Display* mp_dpy;
- int m_conn_number;
winsys::Window m_root;
winsys::Window m_check_window;
- int m_conn;
+
+ int m_dpy_fd;
+ int m_sock_fd;
+ int m_client_fd;
+ int m_max_fd;
+
+ fd_set m_descr;
+ char m_sock_path[256];
+ char m_state_path[256] = { 0 };
+ struct sockaddr_un m_sock_addr;
XEvent m_current_event;