summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBossCode45 <human.cyborg42@gmail.com>2024-10-01 14:54:37 +1300
committerBossCode45 <human.cyborg42@gmail.com>2024-10-01 14:54:37 +1300
commit915532bf8fbda9ba2a36e04fcd6acc67c6c68fa5 (patch)
tree0d7a7569ab5fc30c90d5df91a54d312c764cf328 /src
parentf998705c5a0e50021875a811537962083b73ed26 (diff)
downloadYATwm-915532bf8fbda9ba2a36e04fcd6acc67c6c68fa5.tar.gz
YATwm-915532bf8fbda9ba2a36e04fcd6acc67c6c68fa5.zip
Restructure
Diffstat (limited to 'src')
-rw-r--r--src/IPC.cpp86
-rw-r--r--src/IPC.h26
-rw-r--r--src/commands.cpp335
-rw-r--r--src/commands.h89
-rw-r--r--src/config.cpp132
-rw-r--r--src/config.h53
-rw-r--r--src/error.h26
-rw-r--r--src/ewmh.cpp101
-rw-r--r--src/ewmh.h25
-rw-r--r--src/keybinds.cpp282
-rw-r--r--src/keybinds.h59
-rw-r--r--src/main.cpp1159
-rw-r--r--src/structs.h48
-rw-r--r--src/util.cpp32
-rw-r--r--src/util.h19
15 files changed, 2472 insertions, 0 deletions
diff --git a/src/IPC.cpp b/src/IPC.cpp
new file mode 100644
index 0000000..0aed97e
--- /dev/null
+++ b/src/IPC.cpp
@@ -0,0 +1,86 @@
+#include "IPC.h"
+#include "ewmh.h"
+
+#include <cstring>
+#include <string>
+#include <sys/socket.h>
+#include <iostream>
+#include <unistd.h>
+
+using std::cout, std::endl;
+
+static const char* path = "/tmp/YATwm.sock";
+
+IPCModule::IPCModule(CommandsModule& commandsModule, Config& cfg, Globals& globals)
+ :commandsModule(commandsModule),
+ cfg(cfg),
+ globals(globals)
+{
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ address.sun_family = AF_UNIX;
+ strcpy(address.sun_path, path);
+ unlink(address.sun_path);
+ len = strlen(address.sun_path) + sizeof(address.sun_family);
+
+ if(bind(sockfd, (sockaddr*)&address, len) == -1)
+ {
+ cout << "ERROR " << errno << endl;
+ }
+ cout << "SOCKETED" << endl;
+}
+
+void IPCModule::init()
+{
+ setIPCPath((unsigned char*)path, strlen(path));
+}
+
+void IPCModule::doListen()
+{
+ if(listen(sockfd, 1) != 0)
+ {
+ cout << "ERROR 2" << endl;
+ return;
+ }
+ if(first)
+ {
+ first = false;
+ return;
+ }
+ cout << "DOLISTEN" << endl;
+ unsigned int socklen = 0;
+ sockaddr_un remote;
+ int newsock = accept(sockfd, (sockaddr*)&remote, &socklen);
+ cout << "LISTENING" << endl;
+ char buffer[256];
+ memset(buffer, 0, 256);
+ read(newsock, buffer, 256);
+ std::string command(buffer);
+ while(command[command.size() - 1] == 0 || command[command.size() - 1] == '\n')
+ command = command.substr(0, command.size() - 1);
+ //cout << '"' << command << '"' << endl;
+ try
+ {
+ commandsModule.runCommand(command);
+ }
+ catch(Err e)
+ {
+ cout << e.code << " " << e.message << endl;
+ }
+ char* message = "RAN COMMAND";
+ send(newsock, message, strlen(message), 0);
+ cout << "RAN COMMAND" << endl;
+ shutdown(newsock, SHUT_RDWR);
+ close(newsock);
+}
+
+void IPCModule::quitIPC()
+{
+ close(sockfd);
+}
+
+int IPCModule::getFD()
+{
+ if(sockfd > 0)
+ return sockfd;
+ return -1;
+}
diff --git a/src/IPC.h b/src/IPC.h
new file mode 100644
index 0000000..89e67e5
--- /dev/null
+++ b/src/IPC.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "commands.h"
+#include "config.h"
+#include "util.h"
+
+class IPCModule
+{
+public:
+ IPCModule(CommandsModule& commandsModule, Config& cfg, Globals& globals);
+ void init();
+ void doListen();
+ void quitIPC();
+ int getFD();
+private:
+ CommandsModule& commandsModule;
+ Config& cfg;
+ Globals& globals;
+ int sockfd;
+ int len;
+ bool first = true;
+ sockaddr_un address;
+};
diff --git a/src/commands.cpp b/src/commands.cpp
new file mode 100644
index 0000000..57cfd0b
--- /dev/null
+++ b/src/commands.cpp
@@ -0,0 +1,335 @@
+#include "commands.h"
+#include "error.h"
+#include "util.h"
+
+#include <cctype>
+#include <iostream>
+#include <algorithm>
+#include <iterator>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+#include <regex>
+#include <cstring>
+
+using std::cout, std::endl, std::string, std::vector;
+
+const void CommandsModule::echo(const CommandArg* argv)
+{
+ cout << argv[0].str << endl;
+}
+
+CommandsModule::CommandsModule()
+{
+ addCommand("echo", &CommandsModule::echo, 1, {STR_REST}, this);
+}
+CommandsModule::~CommandsModule()
+{
+ for(Command c : commandList)
+ {
+ if(c.argc > 0)
+ delete[] c.argTypes;
+ }
+}
+
+void CommandsModule::addCommand(Command c)
+{
+ if(lookupCommand(c.name) != nullptr)
+ {
+ cout << "Duplicate command: " << c.name << endl;
+ }
+ commandList.push_back(c);
+}
+void CommandsModule::addCommand(std::string name, const void (*func)(const CommandArg *), const int argc, CommandArgType *argTypes)
+{
+ Command c = {name, nullptr, func, argc, argTypes, nullptr};
+ addCommand(c);
+}
+void CommandsModule::addCommand(std::string name, const void(*func)(const CommandArg*), const int argc, std::vector<CommandArgType> argTypes)
+{
+ CommandArgType* argTypesArr = new CommandArgType[argc];
+ for(int i = 0; i < argc; i++)
+ {
+ argTypesArr[i] = argTypes[i];
+ }
+ addCommand(name, func, argc, argTypesArr);
+}
+
+struct NameMatches
+{
+ NameMatches(string s): s_{s} {}
+ bool operator()(Command c) { return (c.name == s_); }
+ string s_;
+};
+
+Command* CommandsModule::lookupCommand(string name)
+{
+ auto elem = std::find_if(commandList.begin(), commandList.end(), NameMatches(name));
+ if (elem != commandList.end())
+ {
+ int i = elem - commandList.begin();
+ return &(commandList[i]);
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+vector<string> CommandsModule::splitCommand(string command)
+{
+ vector<string> v;
+ string arg = "";
+ bool inQuotes = false;
+ bool escapeNext = true;
+ char quoteType;
+ for(int i = 0; i < command.size(); i++)
+ {
+ if(escapeNext)
+ {
+ arg += command[i];
+ escapeNext = false;
+ }
+ else if(command[i] == '\\')
+ {
+ escapeNext = true;
+ }
+ else if(inQuotes)
+ {
+ if(command[i] == quoteType)
+ {
+ if(arg != "")
+ {
+ v.push_back(arg);
+ arg = "";
+ }
+ inQuotes = false;
+ }
+ else
+ {
+ arg += command[i];
+ }
+ }
+ else
+ {
+ if(command[i] == ' ')
+ {
+ if(arg != "")
+ {
+ v.push_back(arg);
+ arg = "";
+ }
+ }
+ else if(command[i] == '"' || command[i] == '\'')
+ {
+ inQuotes = true;
+ quoteType = command[i];
+ }
+ else
+ {
+ arg += command[i];
+ }
+ }
+ }
+ if(arg != "")
+ v.push_back(arg);
+ return v;
+}
+
+CommandArg* CommandsModule::getCommandArgs(vector<string>& split, const CommandArgType* argTypes, const int argc)
+{
+ CommandArg* args = new CommandArg[argc];
+ for(int i = 1; i < argc + 1; i++)
+ {
+ switch(argTypes[i-1])
+ {
+ case STR: args[i-1].str = (char*)split[i].c_str(); break;
+ case NUM:
+ {
+ try
+ {
+ args[i-1].num = std::stoi(split[i]);
+ break;
+ }
+ catch(std::invalid_argument e)
+ {
+ delete[] args;
+ throw Err(CMD_ERR_WRONG_ARGS, split[i] + " is not a number!");
+ }
+ }
+ case MOVDIR:
+ {
+ if(lowercase(split[i]) == "up")
+ args[i-1].dir = UP;
+ else if(lowercase(split[i]) == "down")
+ args[i-1].dir = DOWN;
+ else if(lowercase(split[i]) == "left")
+ args[i-1].dir = LEFT;
+ else if(lowercase(split[i]) == "right")
+ args[i-1].dir = RIGHT;
+ else
+ {
+ delete[] args;
+ throw Err(CMD_ERR_WRONG_ARGS, split[i] + " is not a direction!");
+ }
+ break;
+ }
+ case STR_REST:
+ {
+ string rest = "";
+ for(int j = i; j < split.size(); j++)
+ {
+ rest += split[j];
+ if(j != split.size() - 1)
+ rest += " ";
+ }
+ args[i-1].str = new char[rest.size()];
+ strcpy(args[i-1].str, rest.c_str());
+ return args;
+ }
+ case NUM_ARR_REST:
+ {
+ int* rest = new int[split.size() - i];
+ for(int j = 0; j < split.size() - i; j++)
+ {
+ try
+ {
+ rest[j] = std::stoi(split[j + i]);
+ }
+ catch(std::invalid_argument e)
+ {
+ delete[] rest;
+ delete[] args;
+ throw Err(CMD_ERR_WRONG_ARGS, split[i] + " is not a number!");
+ }
+ }
+ args[i-1].numArr = {rest, (int) split.size() - i};
+ return args;
+ }
+ default: cout << "UH OH SOMETHING IS VERY WRONG" << endl;
+ }
+ }
+ return args;
+}
+
+void CommandsModule::runCommand(string command)
+{
+ vector<string> split = splitCommand(command);
+ vector<string>::const_iterator start = split.begin();
+ int count = 0;
+ for(string s : split)
+ {
+ if(s == ";")
+ {
+ vector<string>::const_iterator end = start + count;
+ vector<string> partialCmd(start, end);
+ runCommand(partialCmd);
+ count = 0;
+ start = end + 1;
+ }
+ else
+ {
+ count++;
+ }
+ }
+ if(start != split.end())
+ {
+ vector<string> partialCmd(start, (vector<string>::const_iterator)split.end());
+ runCommand(partialCmd);
+ }
+}
+void CommandsModule::runCommand(vector<string> split)
+{
+ Command* cmd = lookupCommand(split[0]);
+ if(cmd == nullptr)
+ throw Err(CMD_ERR_NOT_FOUND, split[0] + " is not a valid command name");
+ if(cmd->argc > split.size() - 1)
+ throw Err(CMD_ERR_WRONG_ARGS, "wrong number of arguments");
+ CommandArg* args;
+ try
+ {
+ args = getCommandArgs(split, cmd->argTypes, cmd->argc);
+ }
+ catch(Err e)
+ {
+ throw e;
+ }
+ try
+ {
+ if(cmd->module == nullptr)
+ cmd->staticFunc(args);
+ else
+ cmd->func(*cmd->module, args);
+ }
+ catch (Err e)
+ {
+ for(int i = 0; i < cmd->argc; i++)
+ {
+ if(cmd->argTypes[i] == STR_REST)
+ delete[] args[i].str;
+ }
+ delete[] args;
+ throw e;
+ }
+ for(int i = 0; i < cmd->argc; i++)
+ {
+ if(cmd->argTypes[i] == STR_REST)
+ delete[] args[i].str;
+ }
+ delete[] args;
+}
+
+vector<Err> CommandsModule::checkCommand(string command)
+{
+ vector<Err> errs;
+ vector<string> split = splitCommand(command);
+ vector<string>::const_iterator start = split.begin();
+ int count = 0;
+ for(string s : split)
+ {
+ if(s == ";")
+ {
+ vector<string>::const_iterator end = start + count;
+ vector<string> partialCmd(start, end);
+ errs.push_back(checkCommand(partialCmd));
+ count = 0;
+ start = end + 1;
+ }
+ else
+ {
+ count++;
+ }
+ }
+ if(start != split.end())
+ {
+ vector<string> partialCmd(start, (vector<string>::const_iterator)split.end());
+ errs.push_back(checkCommand(partialCmd));
+ }
+ return errs;
+}
+
+Err CommandsModule::checkCommand(vector<string> split)
+{
+ Command* cmd = lookupCommand(split[0]);
+ if(cmd == nullptr)
+ return Err(CMD_ERR_NOT_FOUND, split[0] + " is not a valid command name");
+ if(cmd->argc > split.size())
+ return Err(CMD_ERR_WRONG_ARGS, "wrong number of arguments");
+ CommandArg* args;
+ try
+ {
+ args = getCommandArgs(split, cmd->argTypes, cmd->argc);
+ }
+ catch(Err e)
+ {
+ return e;
+ }
+ for(int i = 0; i < cmd->argc; i++)
+ {
+ if(cmd->argTypes[i] == STR_REST || cmd->argTypes[i] == NUM_ARR_REST)
+ delete[] args[i].str;
+ }
+ delete[] args;
+ return Err(NOERR, "");
+}
diff --git a/src/commands.h b/src/commands.h
new file mode 100644
index 0000000..af4a4a0
--- /dev/null
+++ b/src/commands.h
@@ -0,0 +1,89 @@
+#pragma once
+
+#include "error.h"
+
+#include <vector>
+#include <string>
+#include <any>
+#include <functional>
+
+enum MoveDir
+ {
+ UP,
+ RIGHT,
+ DOWN,
+ LEFT
+ };
+enum CommandArgType
+ {
+ STR,
+ NUM,
+ MOVDIR,
+ STR_REST,
+ NUM_ARR_REST
+ };
+
+struct NumArr
+{
+ int* arr;
+ int size;
+};
+typedef union
+{
+ char* str;
+ int num;
+ NumArr numArr;
+ MoveDir dir;
+} CommandArg;
+
+struct Command
+{
+ const std::string name;
+ const std::function<void(std::any&, const CommandArg* argv)> func;
+ const std::function<void(const CommandArg* argv)> staticFunc;
+ const int argc;
+ CommandArgType* argTypes;
+ std::any* module;
+};
+class CommandsModule
+{
+private:
+ std::vector<Command> commandList;
+ std::vector<std::string> splitCommand(std::string command);
+ CommandArg* getCommandArgs(std::vector<std::string>& args, const CommandArgType* argTypes, const int argc);
+ const void echo(const CommandArg* argv);
+public:
+ CommandsModule();
+ ~CommandsModule();
+ template <class T>
+ void addCommand(std::string name, const void(T::*func)(const CommandArg*), const int argc, CommandArgType* argTypes, T* module);
+ void addCommand(std::string name, const void(*func)(const CommandArg*), const int argc, CommandArgType* argTypes);
+ template <class T>
+ void addCommand(std::string name, const void(T::*func)(const CommandArg*), const int argc, std::vector<CommandArgType> argTypes, T* module);
+ void addCommand(std::string name, const void(*func)(const CommandArg*), const int argc, std::vector<CommandArgType> argTypes);
+ void addCommand(Command c);
+ Command* lookupCommand(std::string name);
+ void runCommand(std::string command);
+ void runCommand(std::vector<std::string> split);
+ std::vector<Err> checkCommand(std::string command);
+ Err checkCommand(std::vector<std::string> split);
+};
+
+// YES I KNOW THIS IS BAD
+// but it needs to be done this way
+template <class T>
+void CommandsModule::addCommand(std::string name, const void(T::*func)(const CommandArg*), const int argc, CommandArgType* argTypes, T* module)
+{
+ Command c = {name, (const void*(std::any::*)(const CommandArg* argv)) func, nullptr, argc, argTypes, (std::any*)module};
+ addCommand(c);
+}
+template <class T>
+void CommandsModule::addCommand(std::string name, const void(T::*func)(const CommandArg*), const int argc, std::vector<CommandArgType> argTypes, T* module)
+{
+ CommandArgType* argTypesArr = new CommandArgType[argc];
+ for(int i = 0; i < argc; i++)
+ {
+ argTypesArr[i] = argTypes[i];
+ }
+ addCommand(name, func, argc, argTypesArr, module);
+}
diff --git a/src/config.cpp b/src/config.cpp
new file mode 100644
index 0000000..3af2491
--- /dev/null
+++ b/src/config.cpp
@@ -0,0 +1,132 @@
+#include "config.h"
+#include "commands.h"
+#include "error.h"
+
+#include <X11/Xlib.h>
+
+#include <cstdio>
+#include <cstring>
+#include <fstream>
+#include <ios>
+#include <string>
+#include <vector>
+#include <sstream>
+#include <unistd.h>
+#include <fcntl.h>
+
+//Just for testing
+#include <iostream>
+
+using std::string;
+
+// For testing
+using std::cout, std::endl;
+
+const void Config::gapsCmd(const CommandArg* argv)
+{
+ gaps = argv[0].num;
+}
+
+const void Config::outerGapsCmd(const CommandArg* argv)
+{
+ outerGaps = argv[0].num;
+}
+
+const void Config::logFileCmd(const CommandArg* argv)
+{
+ logFile = argv[0].str;
+}
+
+const void Config::addWorkspaceCmd(const CommandArg* argv)
+{
+ int* prefs = new int[argv[1].numArr.size];
+ for(int i = 0; i < argv[1].numArr.size; i++)
+ {
+ prefs[i] = argv[1].numArr.arr[i] - 1;
+ }
+ workspaces.push_back({argv[0].str, prefs, argv[1].numArr.size});
+ numWS++;
+}
+
+const void Config::swapSuperAltCmd(const CommandArg* argv)
+{
+ swapSuperAlt ^= true;
+}
+
+Config::Config(CommandsModule& commandsModule)
+ : commandsModule(commandsModule)
+{
+ //Register commands for config
+ commandsModule.addCommand("gaps", &Config::gapsCmd, 1, {NUM}, this);
+ commandsModule.addCommand("outergaps", &Config::outerGapsCmd, 1, {NUM}, this);
+ commandsModule.addCommand("logfile", &Config::logFileCmd, 1, {STR_REST}, this);
+ commandsModule.addCommand("addworkspace", &Config::addWorkspaceCmd, 2, {STR, NUM_ARR_REST}, this);
+ commandsModule.addCommand("swapmods", &Config::swapSuperAltCmd, 0, {}, this);
+}
+
+std::vector<Err> Config::reloadFile()
+{
+ if(!loaded)
+ return {{CFG_ERR_NON_FATAL, "Not loaded config yet"}};
+ return loadFromFile(file);
+}
+
+std::vector<Err> Config::loadFromFile(std::string path)
+{
+ std::vector<Err> errs;
+
+ file = path;
+
+ std::ifstream config(path);
+ if(!config.good())
+ {
+ config = std::ifstream("/etc/YATwm/config");
+ errs.push_back({CFG_ERR_FATAL, "Using default config: /etc/YATwm/config"});
+ }
+
+ //Set defaults
+ gaps = 10;
+ outerGaps = 10;
+ logFile = "/tmp/yatlog.txt";
+ numWS = 0;
+ swapSuperAlt = false;
+ workspaces = std::vector<Workspace>();
+
+ //Probably need something for workspaces and binds too...
+
+ string cmd;
+ int line = 0;
+ while(getline(config, cmd))
+ {
+ line++;
+ if(cmd.size() == 0)
+ continue;
+ if(cmd.at(0) == '#')
+ continue;
+ try
+ {
+ commandsModule.runCommand(cmd);
+ }
+ catch (Err e)
+ {
+ errs.push_back({e.code, "Error in config (line " + std::to_string(line) + "): " + std::to_string(e.code) + "\n\tMessage: " + e.message});
+
+ }
+ }
+ loaded = true;
+ return errs;
+}
+
+Config::~Config()
+{
+ free();
+}
+void Config::free()
+{
+ if(!loaded)
+ return;
+ for(Workspace w : workspaces)
+ {
+ delete [] w.screenPreferences;
+ }
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..452db9c
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include "commands.h"
+#include <X11/X.h>
+#include <X11/keysym.h>
+
+#include <string>
+#include <vector>
+
+struct Workspace
+{
+ std::string name;
+ int* screenPreferences;
+ int screenPreferencesc;
+};
+
+#define COMMAND(X) \
+ const void X (const CommandArg* argv)
+
+class Config
+{
+public:
+ Config(CommandsModule& commandsModule);
+ ~Config();
+ void free();
+
+ std::vector<Err> loadFromFile(std::string path);
+ std::vector<Err> reloadFile();
+
+ // Main
+ int gaps;
+ int outerGaps;
+ std::string logFile;
+
+ // Workspaces
+ std::vector<Workspace> workspaces;
+ int numWS;
+ bool loaded = false;
+
+ // Binds
+ bool swapSuperAlt;
+
+ // Config Commands
+ COMMAND(gapsCmd);
+ COMMAND(outerGapsCmd);
+ COMMAND(logFileCmd);
+ COMMAND(addWorkspaceCmd);
+ COMMAND(swapSuperAltCmd);
+
+private:
+ CommandsModule& commandsModule;
+ std::string file;
+};
diff --git a/src/error.h b/src/error.h
new file mode 100644
index 0000000..f0a67a5
--- /dev/null
+++ b/src/error.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <string>
+
+typedef unsigned int ErrCode;
+
+#define NOERR 0
+#define ERR_NON_FATAL 110
+#define ERR_FATAL 120
+#define CFG_ERR_NON_FATAL 210
+#define CFG_ERR_KEYBIND 211
+#define CFG_ERR_FATAL 220
+#define CMD_ERR_NON_FATAL 310
+#define CMD_ERR_NOT_FOUND 311
+#define CMD_ERR_WRONG_ARGS 312
+#define CMD_ERR_FATAL 320
+struct Err
+{
+ ErrCode code;
+ std::string message;
+ Err(ErrCode code, std::string message)
+ {
+ this->code = code;
+ this->message = message;
+ }
+};
diff --git a/src/ewmh.cpp b/src/ewmh.cpp
new file mode 100644
index 0000000..a3cc505
--- /dev/null
+++ b/src/ewmh.cpp
@@ -0,0 +1,101 @@
+#include "ewmh.h"
+#include <X11/X.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <iostream>
+#include <ostream>
+#include <string>
+
+Display** dpy_;
+Window* root_;
+
+void initEWMH(Display** dpy, Window* root, int numWS, std::vector<Workspace> workspaces)
+{
+ dpy_ = dpy;
+ root_ = root;
+
+ Atom supported[] = {XInternAtom(*dpy_, "_NET_NUMBER_OF_DESKTOPS", false), XInternAtom(*dpy_, "_NET_DESKTOP_NAMES", false), XInternAtom(*dpy_, "_NET_CLIENT_LIST", false), XInternAtom(*dpy_, "_NET_CURRENT_DESKTOP", false)};
+ int wsNamesLen = numWS; //For null bytes
+ for(int i = 0; i < numWS; i++)
+ {
+ wsNamesLen += workspaces[i].name.length();
+ }
+ char wsNames[wsNamesLen];
+ int pos = 0;
+ for(int i = 0; i < numWS; i++)
+ {
+ for(char toAdd : workspaces[i].name)
+ {
+ wsNames[pos++] = toAdd;
+ }
+ wsNames[pos++] = '\0';
+ }
+ unsigned long numDesktops = numWS;
+ Atom netSupportedAtom = XInternAtom(*dpy_, "_NET_SUPPORTED", false);
+ Atom netNumDesktopsAtom = XInternAtom(*dpy_, "_NET_NUMBER_OF_DESKTOPS", false);
+ Atom netDesktopNamesAtom = XInternAtom(*dpy_, "_NET_DESKTOP_NAMES", false);
+ Atom XA_UTF8STRING = XInternAtom(*dpy_, "UTF8_STRING", false);
+ XChangeProperty(*dpy_, *root_, netSupportedAtom, XA_ATOM, 32, PropModeReplace, (unsigned char*)supported, 3);
+ XChangeProperty(*dpy_, *root_, netDesktopNamesAtom, XA_UTF8STRING, 8, PropModeReplace, (unsigned char*)&wsNames, wsNamesLen);
+ XChangeProperty(*dpy_, *root_, netNumDesktopsAtom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&numDesktops, 1);
+
+
+}
+
+void updateClientList(std::map<int, Client> clients)
+{
+ Atom netClientList = XInternAtom(*dpy_, "_NET_CLIENT_LIST", false);
+ XDeleteProperty(*dpy_, *root_, netClientList);
+
+ std::map<int, Client>::iterator cItr;
+ for(cItr = clients.begin(); cItr != clients.end(); cItr++)
+ {
+ XChangeProperty(*dpy_, *root_, netClientList, XA_WINDOW, 32, PropModeAppend, (unsigned char*)&cItr->second.w, 1);
+ }
+
+}
+
+void setWindowDesktop(Window w, int desktop)
+{
+ unsigned long currDesktop = desktop - 1;
+ Atom netWMDesktop = XInternAtom(*dpy_, "_NET_WM_DESKTOP", false);
+ XChangeProperty(*dpy_, w, netWMDesktop, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&currDesktop, 1);
+}
+
+void setCurrentDesktop(int desktop)
+{
+ unsigned long currDesktop = desktop - 1;
+ Atom netCurrentDesktop = XInternAtom(*dpy_, "_NET_CURRENT_DESKTOP", false);
+ XChangeProperty(*dpy_, *root_, netCurrentDesktop, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&currDesktop, 1);
+}
+
+void setFullscreen(Window w, bool fullscreen)
+{
+ Atom netWMState = XInternAtom(*dpy_, "_NET_WM_STATE", false);
+ Atom netWMStateVal;
+ if(fullscreen)
+ netWMStateVal = XInternAtom(*dpy_, "_NET_WM_STATE_FULLSCREEN", false);
+ else
+ netWMStateVal = XInternAtom(*dpy_, "", false);
+ XChangeProperty(*dpy_, w, netWMState, XA_ATOM, 32, PropModeReplace, (unsigned char*)&netWMStateVal, 1);
+
+}
+
+void setIPCPath(unsigned char* path, int len)
+{
+ Atom socketPathAtom = XInternAtom(*dpy_, "YATWM_SOCKET_PATH", false);
+ XChangeProperty(*dpy_, *root_, socketPathAtom, XA_STRING, 8, PropModeReplace, path, len);
+}
+
+int getProp(Window w, char* propName, Atom* type, unsigned char** data)
+{
+ Atom prop_type = XInternAtom(*dpy_, propName, false);
+ int format;
+ unsigned long length;
+ unsigned long after;
+ int status = XGetWindowProperty(*dpy_, w, prop_type,
+ 0L, 1L, False,
+ AnyPropertyType, type, &format,
+ &length, &after, data);
+ return(status);
+}
diff --git a/src/ewmh.h b/src/ewmh.h
new file mode 100644
index 0000000..9473d5d
--- /dev/null
+++ b/src/ewmh.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "structs.h"
+#include "config.h"
+
+void initEWMH(Display** dpy, Window* root, int numWS, std::vector<Workspace> workspaces);
+
+void updateClientList(std::map<int, Client> clients);
+
+void setWindowDesktop(Window w, int desktop);
+
+void setCurrentDesktop(int desktop);
+
+void setFullscreen(Window w, bool fullscreen);
+
+void setIPCPath(unsigned char* path, int len);
+
+int getProp(Window w, char* propName, Atom* type, unsigned char** data);
diff --git a/src/keybinds.cpp b/src/keybinds.cpp
new file mode 100644
index 0000000..c41452f
--- /dev/null
+++ b/src/keybinds.cpp
@@ -0,0 +1,282 @@
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <cctype>
+#include <iostream>
+#include <sstream>
+#include <utility>
+#include <vector>
+#include <regex>
+
+#include "commands.h"
+#include "error.h"
+#include "keybinds.h"
+#include "util.h"
+
+using std::string;
+
+bool Keybind::operator<(const Keybind &o) const {
+ if(key != o.key)
+ {
+ return key < o.key;
+ }
+ else return modifiers < o.modifiers;
+}
+bool Keybind::operator==(const Keybind &o) const {
+ return (key == o.key && modifiers == o.modifiers);
+}
+
+KeybindsModule::KeybindsModule(CommandsModule& commandsModule, Config& cfg, Globals& globals, void (*updateMousePos)())
+ :commandsModule(commandsModule),
+ globals(globals),
+ cfg(cfg)
+{
+ commandsModule.addCommand("bind", &KeybindsModule::bind, 2, {STR, STR_REST}, this);
+ commandsModule.addCommand("quitkey", &KeybindsModule::quitKey, 1, {STR}, this);
+ commandsModule.addCommand("bindmode", &KeybindsModule::bindMode, 1, {STR}, this);
+
+ bindModes = {
+ {"normal", &KeybindsModule::normalBindMode},
+ {"emacs", &KeybindsModule::emacsBindMode}
+ };
+ bindFunc = &KeybindsModule::normalBindMode;
+
+ this->updateMousePos = updateMousePos;
+ keyMaps.insert({0, std::map<Keybind, KeyFunction>()});
+}
+
+void KeybindsModule::changeMap(int newMapID)
+{
+ if(currentMapID == newMapID)
+ return;
+ if(currentMapID != 0)
+ XUngrabKeyboard(globals.dpy, CurrentTime);
+ XUngrabButton(globals.dpy, AnyKey, AnyModifier, globals.root);
+ currentMapID = newMapID;
+ if(newMapID == 0)
+ {
+ for(std::pair<Keybind, KeyFunction> pair : getKeymap(currentMapID))
+ {
+ Keybind bind = pair.first;
+ XGrabKey(globals.dpy, bind.key, bind.modifiers, globals.root, false, GrabModeAsync, GrabModeAsync);
+ }
+ }
+ else
+ {
+ XGrabKeyboard(globals.dpy, globals.root, false, GrabModeAsync, GrabModeAsync, CurrentTime);
+ }
+}
+
+const void KeybindsModule::handleKeypress(XKeyEvent e)
+{
+ if(e.same_screen!=1) return;
+ // cout << "Key Pressed" << endl;
+ // cout << "\tState: " << e.state << endl;
+ // cout << "\tCode: " << XKeysymToString(XKeycodeToKeysym(globals.dpy, e.keycode, 0)) << endl;
+ updateMousePos();
+
+ const unsigned int masks = ShiftMask | ControlMask | Mod1Mask | Mod4Mask;
+ Keybind k = {(KeyCode)e.keycode, e.state & masks};
+ if(k == exitBind)
+ {
+ changeMap(0);
+ }
+ else if(getKeymap(currentMapID).count(k) > 0)
+ {
+ KeyFunction& c = getKeymap(currentMapID).find(k)->second;
+ if(getKeymap(c.mapID).size() == 0)
+ {
+ commandsModule.runCommand(c.command);
+ changeMap(0);
+ }
+ else
+ {
+ XUngrabButton(globals.dpy, AnyKey, AnyModifier, globals.root);
+ changeMap(c.mapID);
+ }
+ }
+ else if(std::find(std::begin(ignoredKeys), std::end(ignoredKeys), e.keycode) == std::end(ignoredKeys))
+ {
+ changeMap(0);
+ }
+}
+
+bool isUpper(const std::string& s) {
+ return std::all_of(s.begin(), s.end(), [](unsigned char c){ return std::isupper(c); });
+}
+
+const void KeybindsModule::bindMode(const CommandArg* argv)
+{
+ if(bindModes.count(argv[0].str) < 1)
+ {
+ throw Err(CFG_ERR_KEYBIND, "Bind mode: " + string(argv[0].str) + " does not exist");
+ }
+ else
+ {
+ bindFunc = bindModes.find(argv[0].str)->second;
+ }
+}
+
+Keybind KeybindsModule::getKeybind(std::string bindString)
+{
+ return (this->*bindFunc)(bindString);
+}
+
+const Keybind KeybindsModule::normalBindMode(string bindString)
+{
+ std::vector<string> keys = split(bindString, '+');
+ Keybind bind;
+ bind.modifiers = 0;
+ for(string key : keys)
+ {
+ if(key == "mod")
+ {
+ bind.modifiers |= Mod4Mask >> 3 * cfg.swapSuperAlt;
+ }
+ else if(key == "alt")
+ {
+ bind.modifiers |= Mod1Mask << 3 * cfg.swapSuperAlt;
+ }
+ else if(key == "shift")
+ {
+ bind.modifiers |= ShiftMask;
+ }
+ else if(key == "control")
+ {
+ bind.modifiers |= ControlMask;
+ }
+ else
+ {
+ if(isUpper(key))
+ {
+ bind.modifiers |= ShiftMask;
+ }
+ KeySym s = XStringToKeysym(key.c_str());
+ if(s == NoSymbol)
+ {
+ throw Err(CFG_ERR_KEYBIND, "Keybind '" + bindString + "' is invalid!");
+ }
+ bind.key = XKeysymToKeycode(globals.dpy, s);
+ }
+ }
+ if(!bind.key)
+ throw Err(CFG_ERR_KEYBIND, "Keybind '" + bindString + "' is invalid!");
+ return bind;
+}
+
+const Keybind KeybindsModule::emacsBindMode(string bindString)
+{
+ Keybind bind;
+ bind.modifiers = 0;
+
+ const std::regex keyRegex("^(?:([CMs])-)?(?:([CMs])-)?(?:([CMs])-)?([^\\s]|(SPC|ESC|RET|))$");
+ std::smatch keyMatch;
+ if(std::regex_match(bindString, keyMatch, keyRegex))
+ {
+ for (int i = 1; i < 3; i++)
+ {
+ std::ssub_match modifierMatch = keyMatch[i];
+ if(modifierMatch.matched)
+ {
+ std::string modifier = modifierMatch.str();
+ if(modifier == "s")
+ {
+ bind.modifiers |= Mod4Mask >> 3 * cfg.swapSuperAlt;
+ }
+ else if(modifier == "M")
+ {
+ bind.modifiers |= Mod1Mask << 3 * cfg.swapSuperAlt;
+ }
+ else if(modifier == "C")
+ {
+ bind.modifiers |= ControlMask;
+ }
+ }
+ }
+ }
+ KeySym keySym = XStringToKeysym(keyMatch[4].str().c_str());
+
+ if(isUpper(keyMatch[4].str().c_str()) && keySym != NoSymbol)
+ {
+ bind.modifiers |= ShiftMask;
+ }
+ if(keySym == NoSymbol)
+ {
+ if(keyMatch[4].str() == "RET")
+ keySym = XK_Return;
+ else if(keyMatch[4].str() == "ESC")
+ keySym = XK_Escape;
+ else if(keyMatch[4].str() == "SPC")
+ keySym = XK_space;
+ else if(keyMatch[4].str() == "-")
+ keySym = XK_minus;
+ else if(keyMatch[4].str() == "+")
+ keySym = XK_plus;
+ else
+ throw Err(CFG_ERR_KEYBIND, "Keybind '" + bindString + "' is invalid");
+ }
+ bind.key = XKeysymToKeycode(globals.dpy, keySym);
+
+ return bind;
+}
+
+const void KeybindsModule::bind(const CommandArg* argv)
+{
+ std::vector<Err> errs = commandsModule.checkCommand(argv[1].str);
+ for(Err e : errs)
+ {
+ if(e.code != NOERR)
+ {
+ e.message = "Binding fail - " + e.message;
+ throw e;
+ }
+ }
+ std::vector<string> keys = split(argv[0].str, ' ');
+ int currentBindingMap = 0;
+ for(int i = 0; i < keys.size() - 1; i++)
+ {
+ Keybind bind = getKeybind(keys[i]);
+ if(getKeymap(currentBindingMap).count(bind) > 0)
+ {
+ currentBindingMap = getKeymap(currentBindingMap).find(bind)->second.mapID;
+ }
+ else
+ {
+ KeyFunction newMap = {"", nextKeymapID};
+ keyMaps.insert({nextKeymapID, std::map<Keybind, KeyFunction>()});
+ nextKeymapID++;
+ getKeymap(currentBindingMap).insert({bind, newMap});
+ currentBindingMap = getKeymap(currentBindingMap).find(bind)->second.mapID;
+ }
+ }
+ Keybind bind = getKeybind(keys[keys.size() - 1]);
+ if(getKeymap(currentBindingMap).count(bind) <= 0)
+ {
+ KeyFunction function = {argv[1].str, nextKeymapID};
+ keyMaps.insert({nextKeymapID, std::map<Keybind, KeyFunction>()});
+ nextKeymapID++;
+ getKeymap(currentBindingMap).insert({bind, function});
+ if(currentBindingMap == currentMapID)
+ {
+ KeyCode c = XKeysymToKeycode(globals.dpy, bind.key);
+ XGrabKey(globals.dpy, c, bind.modifiers, globals.root, false, GrabModeAsync, GrabModeAsync);
+ }
+ }
+ else
+ {
+ throw Err(CFG_ERR_KEYBIND, "Bind is a keymap already!");
+ }
+ // cout << "Added bind" << endl;
+ // cout << "\t" << argv[0].str << endl;
+}
+
+const void KeybindsModule::quitKey(const CommandArg* argv)
+{
+ exitBind = getKeybind(argv[0].str);
+}
+
+const void KeybindsModule::clearKeybinds()
+{
+ XUngrabButton(globals.dpy, AnyKey, AnyModifier, globals.root);
+ keyMaps = std::map<int, std::map<Keybind, KeyFunction>>();
+ keyMaps.insert({0, std::map<Keybind, KeyFunction>()});
+}
diff --git a/src/keybinds.h b/src/keybinds.h
new file mode 100644
index 0000000..a742240
--- /dev/null
+++ b/src/keybinds.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include <map>
+#include <string>
+#include <X11/keysym.h>
+#include <vector>
+
+#include "commands.h"
+#include "config.h"
+#include "util.h"
+
+struct Keybind {
+ KeyCode key;
+ unsigned int modifiers;
+ bool operator<(const Keybind &o) const;
+ bool operator==(const Keybind &o) const;
+};
+
+struct KeyFunction
+{
+ std::string command;
+ int mapID;
+};
+
+#define getKeymap(X) \
+ keyMaps.find(X)->second
+
+
+class KeybindsModule
+{
+public:
+ KeybindsModule(CommandsModule& commandsModule, Config& cfg, Globals& globals, void (*updateMousePos)());
+ ~KeybindsModule() = default;
+ const void bind(const CommandArg* argv);
+ const void quitKey(const CommandArg* argv);
+ const void bindMode(const CommandArg* argv);
+ const void handleKeypress(XKeyEvent e);
+ const void clearKeybinds();
+private:
+ Keybind getKeybind(std::string bindString);
+ void changeMap(int newMapID);
+ std::map<int, std::map<Keybind, KeyFunction>> keyMaps;
+ const Keybind emacsBindMode(std::string bindString);
+ const Keybind normalBindMode(std::string bindString);
+ std::map<std::string, const Keybind(KeybindsModule::*)(std::string bindString)> bindModes;
+ const Keybind(KeybindsModule::* bindFunc)(std::string bindString);
+ // Modifier keys to ignore when canceling a keymap
+ KeyCode ignoredKeys[8] = {50, 37, 133, 64, 62, 105, 134, 108};
+ int currentMapID = 0;
+ int nextKeymapID = 1;
+ Keybind exitBind = {42, 0x40};
+ CommandsModule& commandsModule;
+ Config& cfg;
+ Globals& globals;
+ void (*updateMousePos)();
+};
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..1f3edc2
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,1159 @@
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <libnotify/notification.h>
+#include <libnotify/notify.h>
+
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+#include <fstream>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <ostream>
+#include <string>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <vector>
+#include <unistd.h>
+#include <cstring>
+#include <algorithm>
+#include <fcntl.h>
+#include <poll.h>
+
+#include "IPC.h"
+#include "commands.h"
+#include "keybinds.h"
+#include "structs.h"
+#include "config.h"
+#include "util.h"
+#include "ewmh.h"
+#include "error.h"
+
+using std::cout;
+using std::string;
+using std::endl;
+using std::map;
+using std::pair;
+using std::vector;
+
+std::ofstream yatlog;
+std::time_t timeNow;
+std::tm *now;
+char nowString[80];
+
+#define log(x) \
+ updateTime(); \
+ yatlog << nowString << x << std::endl
+
+Display* dpy;
+Window root;
+
+Globals globals = {dpy, root};
+
+void updateMousePos();
+
+CommandsModule commandsModule;
+Config cfg(commandsModule);
+KeybindsModule keybindsModule(commandsModule, cfg, globals, &updateMousePos);
+IPCModule ipc(commandsModule, cfg, globals);
+
+int sW, sH;
+int bH;
+TileDir nextDir = horizontal;
+
+bool keepGoing = true;
+
+map<int, Client> clients;
+int currClientID = 0;
+map<int, Frame> frames;
+int currFrameID = 1;
+map<Window, int> frameIDS;
+
+ScreenInfo* screens;
+int* focusedWorkspaces;
+int focusedScreen = 0;
+int nscreens;
+int mX, mY;
+
+#define getClient(c) clients.find(c)->second
+#define getFrame(f) frames.find(f)->second
+#define getFrameID(w) frameIDS.find(w)->second
+
+Window bar;
+
+int currWS = 1;
+
+
+// Usefull functions
+int FFCF(int sID);
+void detectScreens();
+void focusRoot(int root);
+void handleConfigErrs(vector<Err> cfgErrs);
+void updateTime();
+
+void configureRequest(XConfigureRequestEvent e);
+void mapRequest(XMapRequestEvent e);
+void destroyNotify(XDestroyWindowEvent e);
+void enterNotify(XEnterWindowEvent e);
+void clientMessage(XClientMessageEvent e);
+
+static int OnXError(Display* display, XErrorEvent* e);
+
+// Tiling
+// Call this one to tile everything (it does all the fancy stuff trust me just call this one)
+void tileRoots();
+// Call this one to until everything (it handles multiple monitors)
+void untileRoots();
+// This is to be called by tileRoots, it takes in the x, y, w, and h of where it's allowed to tile windows to, and returns the ID of a fullscreen client if one is found, or noID (-1) if none are found
+int tile(int frameID, int x, int y, int w, int h);
+// This is to be called by tileRoots, it takes in a frameID and recursively unmaps all its children
+void untile(int frameID);
+
+// Usefull functions
+int FFCF(int sID)
+{
+ if(frames.find(sID)->second.isClient)
+ return sID;
+ return FFCF(frames.find(sID)->second.subFrameIDs[0]);
+}
+void detectScreens()
+{
+ delete[] screens;
+ delete[] focusedWorkspaces;
+ log("Detecting screens: ");
+ XRRMonitorInfo* monitors = XRRGetMonitors(dpy, root, true, &nscreens);
+ log("\t" << nscreens << " monitors");
+ screens = new ScreenInfo[nscreens];
+ focusedWorkspaces = new int[nscreens];
+ for(int i = 0; i < nscreens; i++)
+ {
+ focusedWorkspaces[i] = i * 5 + 1;
+ char* name = XGetAtomName(dpy, monitors[i].name);
+ screens[i] = {name, monitors[i].x, monitors[i].y, monitors[i].width, monitors[i].height};
+ log("\tMonitor " << i + 1 << " - " << screens[i].name);
+ log("\t\tx: " << screens[i].x << ", y: " << screens[i].y);
+ log("\t\tw: " << screens[i].w << ", h: " << screens[i].h);
+ XFree(name);
+ }
+ for(int i = 0; i < cfg.workspaces.size(); i++)
+ {
+ if(cfg.workspaces[i].screenPreferences[0] < nscreens && focusedWorkspaces[cfg.workspaces[i].screenPreferences[0]] == 0)
+ {
+ //focusedWorkspaces[screenPreferences[i][0]] = i+1;
+ }
+ }
+ XFree(monitors);
+}
+void updateMousePos()
+{
+ Window rootRet, childRet;
+ int rX, rY, cX, cY;
+ unsigned int maskRet;
+ XQueryPointer(dpy, root, &rootRet, &childRet, &rX, &rY, &cX, &cY, &maskRet);
+ mX = rX;
+ mY = rY;
+}
+int getClientChild(int fID)
+{
+ if(getFrame(fID).isClient)
+ return fID;
+ else
+ return getClientChild(getFrame(fID).subFrameIDs[0]);
+}
+void focusRoot(int root)
+{
+ //log("Focusing root: " << root);
+ if(getFrame(root).subFrameIDs.size() == 0)
+ {
+ //log("\tRoot has no children");
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ return;
+ }
+ int client = getFrame(getClientChild(root)).cID;
+ Window w = getClient(client).w;
+ //log("\tFocusing window: " << w);
+ XSetInputFocus(dpy, w, RevertToPointerRoot, CurrentTime);
+}
+void handleConfigErrs(vector<Err> cfgErrs)
+{
+ for(Err cfgErr : cfgErrs)
+ {
+ if(cfgErr.code == CFG_ERR_FATAL)
+ {
+ log("YATwm fatal error (Code " << cfgErr.code << ")\n" << cfgErr.message);
+ std::string title = "YATwm fatal config error (Code " + std::to_string(cfgErr.code) + ")";
+ std::string body = cfgErr.message;
+ NotifyNotification* n = notify_notification_new(title.c_str(),
+ body.c_str(),
+ 0);
+ notify_notification_set_timeout(n, 10000);
+ if(!notify_notification_show(n, 0))
+ {
+ log("notification failed");
+ }
+ }
+ else
+ {
+ log("YATwm non fatal error (Code " << cfgErr.code << ")\n" << cfgErr.message);
+ std::string title = "YATwm non fatal config error (Code " + std::to_string(cfgErr.code) + ")";
+ std::string body = "Check logs for more information";
+ NotifyNotification* n = notify_notification_new(title.c_str(),
+ body.c_str(),
+ 0);
+ notify_notification_set_timeout(n, 10000);
+ if(!notify_notification_show(n, 0))
+ {
+ log("notification failed");
+ }
+ }
+ }
+}
+void updateTime()
+{
+ timeNow = std::time(0);
+ now = std::localtime(&timeNow);
+ strftime(nowString, sizeof(nowString), "[%H:%M:%S] ", now);
+}
+
+//Keybind commands
+const void exit(const CommandArg* argv)
+{
+ keepGoing = false;
+}
+const void spawn(const CommandArg* argv)
+{
+ if(fork() == 0)
+ {
+ const std::string argsStr = argv[0].str;
+ vector<std::string> args = split(argsStr, ' ');
+ char** execvpArgs = new char*[args.size()];
+ for(int i = 0; i < args.size(); i++)
+ {
+ execvpArgs[i] = strdup(args[i].c_str());
+ }
+ int null = open("/dev/null", O_WRONLY);
+ dup2(null, 0);
+ dup2(null, 1);
+ dup2(null, 2);
+ execvp(execvpArgs[0], execvpArgs);
+ exit(0);
+ }
+}
+const void spawnOnce(const CommandArg* argv)
+{
+ if(cfg.loaded)
+ return;
+ else spawn(argv);
+}
+const void toggle(const CommandArg* argv)
+{
+ nextDir = nextDir = (nextDir==horizontal)? vertical : horizontal;
+}
+const void kill(const CommandArg* argv)
+{
+ Window w;
+ int revertToReturn;
+ XGetInputFocus(dpy, &w, &revertToReturn);
+ Atom* supported_protocols;
+ int num_supported_protocols;
+ if (XGetWMProtocols(dpy,
+ w,
+ &supported_protocols,
+ &num_supported_protocols) &&
+ (std::find(supported_protocols,
+ supported_protocols + num_supported_protocols,
+ XInternAtom(dpy, "WM_DELETE_WINDOW", false)) !=
+ supported_protocols + num_supported_protocols)) {
+ // 1. Construct message.
+ XEvent msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.xclient.type = ClientMessage;
+ msg.xclient.message_type = XInternAtom(dpy, "WM_PROTOCOLS", false);
+ msg.xclient.window = w;
+ msg.xclient.format = 32;
+ msg.xclient.data.l[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", false);
+ // 2. Send message to window to be closed.
+ cout << "Nice kill\n";
+ XSendEvent(dpy, w, false, 0, &msg);
+ } else {
+ cout << "Mean kill\n";
+ XKillClient(dpy, w);
+ }
+}
+// Took this out as it is used commonly
+void cWS(int newWS)
+{
+ int prevWS = currWS;
+
+ currWS = newWS;
+ if(prevWS == currWS)
+ return;
+ untileRoots();
+
+ //log("Changing WS with keybind");
+
+ for(int i = 0; i < cfg.workspaces[newWS - 1].screenPreferencesc; i++)
+ {
+ if(nscreens > cfg.workspaces[newWS - 1].screenPreferences[i])
+ {
+ int screen = cfg.workspaces[newWS - 1].screenPreferences[i];
+ //log("Found screen (screen " << screenPreferences[arg.num - 1][i] << ")");
+ prevWS = focusedWorkspaces[screen];
+ //log("Changed prevWS");
+ focusedWorkspaces[screen] = newWS;
+ //log("Changed focusedWorkspaces");
+ if(focusedScreen != screen)
+ {
+ focusedScreen = screen;
+ XWarpPointer(dpy, root, root, 0, 0, 0, 0, screens[screen].x + screens[screen].w/2, screens[screen].y + screens[screen].h/2);
+ }
+ //log("Changed focusedScreen");
+ break;
+ }
+ }
+
+ //log("Finished changes");
+
+ //log(prevWS);
+ // LOOK: what is this for?????
+ if(prevWS < 1 || prevWS > cfg.workspaces.size())
+ {
+ //untile(prevWS);
+ }
+ //log("Untiled");
+ //tile(currWS, outerGaps, outerGaps, sW - outerGaps*2, sH - outerGaps*2 - bH);
+ tileRoots();
+ //log("Roots tiled");
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+
+ cout << focusedWorkspaces[0] << endl;
+
+ //EWMH
+ setCurrentDesktop(currWS);
+}
+const void changeWS(const CommandArg* argv)
+{
+ cWS(argv[0].num);
+}
+const void wToWS(const CommandArg* argv)
+{
+ Window focusedWindow;
+ int revertToReturn;
+ XGetInputFocus(dpy, &focusedWindow, &revertToReturn);
+ if(focusedWindow == root)
+ return;
+
+ int fID = frameIDS.find(focusedWindow)->second;
+ //TODO: make floating windows move WS
+ if(clients.find(frames.find(fID)->second.cID)->second.floating)
+ return;
+ vector<int>& pSF = frames.find(frames.find(fID)->second.pID)->second.subFrameIDs;
+ for(int i = 0; i < pSF.size(); i++)
+ {
+ if(pSF[i] == fID)
+ {
+ //Frame disolve
+ pSF.erase(pSF.begin() + i);
+ int pID = frames.find(fID)->second.pID;
+ if(pSF.size() < 2 && !frames.find(pID)->second.isRoot)
+ {
+ //Erase parent frame
+ int lastChildID = frames.find(frames.find(pID)->second.subFrameIDs[0])->second.ID;
+ int parentParentID = frames.find(pID)->second.pID;
+ vector<int>& parentParentSubFrameIDs = frames.find(parentParentID)->second.subFrameIDs;
+ for(int j = 0; j < parentParentSubFrameIDs.size(); j++)
+ {
+ if(parentParentSubFrameIDs[j] == pID)
+ {
+ parentParentSubFrameIDs[j] = lastChildID;
+ frames.find(lastChildID)->second.pID = parentParentID;
+ frames.erase(pID);
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ frames.find(fID)->second.pID = argv[0].num;
+ frames.find(argv[0].num)->second.subFrameIDs.push_back(fID);
+
+ //EWMH
+ setWindowDesktop(focusedWindow, argv[0].num);
+
+ XUnmapWindow(dpy, focusedWindow);
+ //tile(currWS, outerGaps, outerGaps, sW - outerGaps*2, sH - outerGaps*2 - bH);
+ untileRoots();
+ tileRoots();
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+}
+int dirFind(int fID, MoveDir dir)
+{
+ vector<int>& pSF = frames.find(frames.find(fID)->second.pID)->second.subFrameIDs;
+ TileDir pDir = frames.find(frames.find(fID)->second.pID)->second.dir;
+
+ int i = 0;
+ for(int f : pSF)
+ {
+ if(f == fID)
+ {
+ break;
+ }
+ i++;
+ }
+
+ if(pDir == vertical)
+ {
+ switch(dir)
+ {
+ case UP: i--; break;
+ case DOWN: i++; break;
+ case LEFT: return (frames.find(fID)->second.pID > cfg.numWS)? dirFind(frames.find(fID)->second.pID, dir) : fID;
+ case RIGHT: return (frames.find(fID)->second.pID > cfg.numWS)? dirFind(frames.find(fID)->second.pID, dir) : fID;
+ }
+ }
+ else if(pDir == horizontal)
+ {
+ switch(dir)
+ {
+ case LEFT: i--; break;
+ case RIGHT: i++; break;
+ case UP: return (frames.find(fID)->second.pID > cfg.numWS)? dirFind(frames.find(fID)->second.pID, dir) : fID;
+ case DOWN: return (frames.find(fID)->second.pID > cfg.numWS)? dirFind(frames.find(fID)->second.pID, dir) : fID;
+ }
+ }
+ if(i < 0)
+ i = pSF.size() - 1;
+ if(i == pSF.size())
+ i = 0;
+
+ return pSF[i];
+}
+const void focChange(const CommandArg* argv)
+{
+ Window focusedWindow;
+ int revertToReturn;
+ XGetInputFocus(dpy, &focusedWindow, &revertToReturn);
+ if(focusedWindow == root)
+ return;
+
+ int fID = frameIDS.find(focusedWindow)->second;
+ int nID = dirFind(fID, argv[0].dir);
+ int fNID = FFCF(nID);
+ Window w = clients.find(frames.find(fNID)->second.cID)->second.w;
+ XSetInputFocus(dpy, w, RevertToPointerRoot, CurrentTime);
+}
+const void wMove(const CommandArg* argv)
+{
+ Window focusedWindow;
+ int revertToReturn;
+ XGetInputFocus(dpy, &focusedWindow, &revertToReturn);
+ if(focusedWindow == root)
+ return;
+
+ int fID = frameIDS.find(focusedWindow)->second;
+ if(clients.find(frames.find(fID)->second.cID)->second.floating)
+ return;
+ int nID = dirFind(fID, argv[0].dir);
+ int fNID = FFCF(nID);
+ int pID = frames.find(fNID)->second.pID;
+ int oPID = frames.find(fID)->second.pID;
+
+ vector<int>& pSF = frames.find(pID)->second.subFrameIDs;
+ vector<int>& oPSF = frames.find(oPID)->second.subFrameIDs;
+
+ for(int i = 0; i < frames.find(oPID)->second.subFrameIDs.size(); i++)
+ {
+ if(oPSF[i] != fID)
+ continue;
+
+ if(pID!=oPID)
+ {
+ //Frame dissolve
+ oPSF.erase(oPSF.begin() + i);
+ if(oPSF.size() < 2 && !frames.find(oPID)->second.isRoot)
+ {
+ //Erase parent frame
+ int lastChildID = frames.find(frames.find(oPID)->second.subFrameIDs[0])->second.ID;
+ int parentParentID = frames.find(oPID)->second.pID;
+ vector<int>& parentParentSubFrameIDs = frames.find(parentParentID)->second.subFrameIDs;
+ for(int j = 0; j < parentParentSubFrameIDs.size(); j++)
+ {
+ if(parentParentSubFrameIDs[j] == oPID)
+ {
+ parentParentSubFrameIDs[j] = lastChildID;
+ frames.find(lastChildID)->second.pID = parentParentID;
+ frames.erase(oPID);
+ break;
+ }
+ }
+ }
+
+ frames.find(fID)->second.pID = pID;
+ pSF.push_back(fID);
+ }
+ else
+ {
+ if(frames.find(pID)->second.dir == vertical)
+ {
+ if(argv[0].dir == LEFT || argv[0].dir == RIGHT)
+ return;
+ }
+ else
+ {
+ if(argv[0].dir == UP || argv[0].dir == DOWN)
+ return;
+ }
+
+ int offset;
+ if(argv[0].dir == UP || argv[0].dir == LEFT)
+ offset = -1;
+ else
+ offset = 1;
+
+ int swapPos = i + offset;
+
+ if(swapPos == pSF.size())
+ swapPos = 0;
+ else if(swapPos == -1)
+ swapPos = pSF.size() - 1;
+
+ std::swap(pSF[i], pSF[swapPos]);
+ }
+ untileRoots();
+ tileRoots();
+ //tile(currWS, outerGaps, outerGaps, sW - outerGaps*2, sH - outerGaps*2 - bH);
+ XSetInputFocus(dpy, focusedWindow, RevertToPointerRoot, CurrentTime);
+ return;
+ }
+ XSetInputFocus(dpy, focusedWindow, RevertToPointerRoot, CurrentTime);
+}
+const void bashSpawn(const CommandArg* argv)
+{
+ if(fork() == 0)
+ {
+ int null = open("/dev/null", O_WRONLY);
+ dup2(null, 0);
+ dup2(null, 1);
+ dup2(null, 2);
+ system(argv[0].str);
+ exit(0);
+ }
+}
+const void bashSpawnOnce(const CommandArg* argv)
+{
+ if(cfg.loaded)
+ return;
+ else bashSpawn(argv);
+}
+const void reload(const CommandArg* argv)
+{
+ detectScreens();
+
+ //Clear keybinds
+ keybindsModule.clearKeybinds();
+
+ //Load config again
+ vector<Err> cfgErr = cfg.reloadFile();
+ //Error check
+ handleConfigErrs(cfgErr);
+
+ //Re tile
+ untileRoots();
+ tileRoots();
+}
+const void wsDump(const CommandArg* argv)
+{
+ log("Workspace dump:");
+ for(int i = 1; i < currFrameID; i++)
+ {
+ if(getFrame(i).isClient)
+ {
+ int id = i;
+ while(!getFrame(id).isRoot)
+ {
+ id=getFrame(id).pID;
+ }
+ log("\tClient with ID: " << getClient(getFrame(i).cID).w << ", on worskapce " << id);
+ }
+ }
+}
+const void nextMonitor(const CommandArg* argv)
+{
+ focusedScreen++;
+ if(focusedScreen >= nscreens)
+ focusedScreen = 0;
+
+ XWarpPointer(dpy, root, root, 0, 0, 0, 0, screens[focusedScreen].x + screens[focusedScreen].w/2, screens[focusedScreen].y + screens[focusedScreen].h/2);
+ focusRoot(focusedWorkspaces[focusedScreen]);
+}
+const void fullscreen(const CommandArg* arg)
+{
+ Window focusedWindow;
+ int focusedRevert;
+ XGetInputFocus(dpy, &focusedWindow, &focusedRevert);
+
+ int fID = getFrameID(focusedWindow);
+ int cID = getFrame(fID).cID;
+ getClient(cID).fullscreen ^= true;
+ tileRoots();
+ setFullscreen(focusedWindow, getClient(cID).fullscreen);
+}
+
+void configureRequest(XConfigureRequestEvent e)
+{
+ XWindowChanges changes;
+ changes.x = e.x;
+ changes.y = e.y;
+ changes.width = e.width;
+ changes.height = e.height;
+ changes.border_width = e.border_width;
+ changes.sibling = e.above;
+ changes.stack_mode = e.detail;
+ XConfigureWindow(dpy, e.window, (unsigned int) e.value_mask, &changes);
+ log("Configure request: " << e.window);
+ //XSetInputFocus(dpy, e.window, RevertToNone, CurrentTime);
+ //tileRoots();
+}
+
+void mapRequest(XMapRequestEvent e)
+{
+ XMapWindow(dpy, e.window);
+
+ XTextProperty name;
+ bool gotName = XGetWMName(dpy, e.window, &name);
+ XWindowAttributes attr;
+ XGetWindowAttributes(dpy, e.window, &attr);
+ if(gotName)
+ {
+ log("Mapping window: " << name.value);
+ }
+ else
+ {
+ log("Mapping window with unknown name (its probably mpv, mpv is annoying)");
+ }
+ log("\tWindow ID: " << e.window);
+
+ Window focusedWindow;
+ int revertToReturn;
+ int pID;
+ XGetInputFocus(dpy, &focusedWindow, &revertToReturn);
+ if(focusedWindow && focusedWindow != root && frameIDS.count(focusedWindow)>0)
+ {
+ //Use focused to determine parent
+ pID = frames.find(frameIDS.find(focusedWindow)->second)->second.pID;
+ }
+ else
+ {
+ Window rootRet, childRet;
+ int rX, rY, cX, cY;
+ unsigned int maskRet;
+ XQueryPointer(dpy, root, &rootRet, &childRet, &rX, &rY, &cX, &cY, &maskRet);
+ mX = rX;
+ mY = rY;
+ int monitor = 0;
+ for(int i = 0; i < nscreens; i++)
+ {
+ if(screens[i].x <= mX && mX < screens[i].x + screens[i].w)
+ {
+ if(screens[i].y <= mY && mY < screens[i].y + screens[i].h)
+ {
+ monitor = i;
+ }
+ }
+ }
+ pID = focusedWorkspaces[monitor];
+ focusedScreen = monitor;
+ /*
+ if(mX == rX && mY == rY)
+ {
+ //Use focused screen
+ log("\tFocused screen is: " << focusedScreen);
+ }
+ else
+ {
+ //Use mouse
+ //TODO: Make this find the monitor
+ log("\tMouse is at x: " << rX << ", y: " << rY);
+ mX = rX;
+ mY = rY;
+ }
+ */
+ }
+
+ unsigned char* data;
+ Atom type;
+ int status = getProp(e.window, "_NET_WM_WINDOW_TYPE", &type, &data);
+ if (status == Success && type != None && ((Atom*)data)[0] == XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", false))
+ {
+ log("\tWindow was bar");
+ bH = attr.height;
+ bar = e.window;
+ XFree(data);
+ return;
+ }
+ XFree(data);
+
+
+
+
+ XSelectInput(dpy, e.window, EnterWindowMask);
+
+ //Make client
+ Client c = {currClientID, e.window, false, false};
+ currClientID++;
+
+ //Add to clients map
+ clients.insert(pair<int, Client>(c.ID, c));
+
+ //Make frame
+ //pID = (frameIDS.count(focusedWindow)>0)? frames.find(frameIDS.find(focusedWindow)->second)->second.pID : currWS;
+ vector<int> v;
+ vector<int> floating;
+ Frame f = {currFrameID, pID, true, c.ID, noDir, v, false, floating};
+ currFrameID++;
+
+
+ //Add ID to frameIDS map
+ frameIDS.insert(pair<Window, int>(e.window, f.ID));
+
+ status = getProp(e.window, "_NET_WM_STATE", &type, &data);
+ if(status == Success && type!=None && (((Atom*)data)[0] == XInternAtom(dpy, "_NET_WM_STATE_MODAL", false) || ((Atom*)data)[0] == XInternAtom(dpy, "_NET_WM_STATE_ABOVE", false)))
+ {
+ cout << "Floating" << endl;
+ clients.find(c.ID)->second.floating = true;
+ frames.find(pID)->second.floatingFrameIDs.push_back(f.ID);
+ frames.insert(pair<int, Frame>(f.ID, f));
+ setWindowDesktop(e.window, currWS);
+ updateClientList(clients);
+ XFree(data);
+ //tile(currWS, outerGaps, outerGaps, sW - outerGaps*2, sH - outerGaps*2 - bH);
+ tileRoots();
+ return;
+ }
+ XFree(data);
+
+ //Check how to add
+ if(nextDir == frames.find(pID)->second.dir || frameIDS.count(focusedWindow)==0)
+ {
+ //Add to focused parent
+ frames.find(pID)->second.subFrameIDs.push_back(f.ID);
+ }
+ else
+ {
+ //Get parent sub frames for later use
+ vector<int>& pS = frames.find(pID)->second.subFrameIDs;
+
+ //Get index of focused frame in parent sub frames
+ int index;
+ for(index = 0; index < pS.size(); index++)
+ {
+ if(pS[index] == frames.find(frameIDS.find(focusedWindow)->second)->second.ID)
+ break;
+ }
+
+ //Make new frame
+ vector<int> v;
+ v.push_back(frames.find(frameIDS.find(focusedWindow)->second)->second.ID);
+ v.push_back(f.ID);
+ Frame pF = {currFrameID, pID, false, noID, nextDir, v, false, floating};
+
+ //Update the IDS
+ f.pID = currFrameID;
+ frames.find(frames.find(frameIDS.find(focusedWindow)->second)->second.ID)->second.pID = currFrameID;
+ pS[index] = currFrameID;
+
+ currFrameID++;
+
+ //Insert the new frame into the frames map
+ frames.insert(pair<int, Frame>(pF.ID, pF));
+ }
+
+ //Add to frames map
+ frames.insert(pair<int, Frame>(f.ID, f));
+
+ setWindowDesktop(e.window, currWS);
+ updateClientList(clients);
+
+ //tile(currWS, outerGaps, outerGaps, sW - outerGaps*2, sH - outerGaps*2 - bH);
+ XSetInputFocus(dpy, e.window, RevertToNone, CurrentTime);
+ tileRoots();
+}
+
+void destroyNotify(XDestroyWindowEvent e)
+{
+ if(frameIDS.count(e.window)<1)
+ return;
+ log("Destroy notif");
+ log("\tWindow ID: " << e.window);
+ int fID = frameIDS.find(e.window)->second;
+ int pID = frames.find(fID)->second.pID;
+ vector<int>& pS = frames.find(pID)->second.subFrameIDs;
+ if(clients.find(frames.find(fID)->second.cID)->second.floating)
+ {
+ pS = frames.find(pID)->second.floatingFrameIDs;
+ }
+ for(int i = 0; i < pS.size(); i++)
+ {
+ if(frames.find(pS[i])->second.ID == fID)
+ {
+ pS.erase(pS.begin() + i);
+ clients.erase(frames.find(fID)->second.cID);
+ frames.erase(fID);
+ frameIDS.erase(e.window);
+
+ if(pS.size() < 2 && !frames.find(pID)->second.isRoot)
+ {
+ //Erase parent frame
+ int lastChildID = frames.find(frames.find(pID)->second.subFrameIDs[0])->second.ID;
+ int parentParentID = frames.find(pID)->second.pID;
+ vector<int>& parentParentSubFrameIDs = frames.find(parentParentID)->second.subFrameIDs;
+ for(int j = 0; j < parentParentSubFrameIDs.size(); j++)
+ {
+ if(parentParentSubFrameIDs[j] == pID)
+ {
+ parentParentSubFrameIDs[j] = lastChildID;
+ frames.find(lastChildID)->second.pID = parentParentID;
+ frames.erase(pID);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+ //tile(currWS, outerGaps, outerGaps, sW - outerGaps*2, sH - outerGaps*2 - bH);
+ tileRoots();
+
+ updateClientList(clients);
+}
+void enterNotify(XEnterWindowEvent e)
+{
+ //log(e.xcrossing.x);
+ //Cancel if crossing into root
+ if(e.window == root)
+ return;
+
+ XWindowAttributes attr;
+ XGetWindowAttributes(dpy, e.window, &attr);
+ int monitor = 0;
+ for(int i = 0; i < nscreens; i++)
+ {
+ if(screens[i].x <= attr.x && attr.x < screens[i].x + screens[i].w)
+ {
+ if(screens[i].y <= attr.y && attr.y < screens[i].y + screens[i].h)
+ {
+ monitor = i;
+ }
+ }
+ }
+ focusedScreen = monitor;
+ XSetInputFocus(dpy, e.window, RevertToNone, CurrentTime);
+}
+void clientMessage(XClientMessageEvent e)
+{
+ char* name = XGetAtomName(dpy, e.message_type);
+ log("Client message: " << name);
+ if(e.message_type == XInternAtom(dpy, "_NET_CURRENT_DESKTOP", false))
+ {
+ cWS(e.data.l[0] + 1);
+ /*
+ //Change desktop
+ int nextWS = (long)e.data.l[0] + 1;
+ int prevWS = currWS;
+ currWS = nextWS;
+
+ if(prevWS == currWS)
+ return;
+
+ untile(prevWS);
+ tile(currWS, outerGaps, outerGaps, sW - outerGaps*2, sH - outerGaps*2 - bH);
+ XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
+
+ //EWMH
+ setCurrentDesktop(currWS);
+ */
+ }
+ else if(e.message_type == XInternAtom(dpy, "_NET_WM_STATE", false))
+ {
+ if((Atom)e.data.l[0] == 0)
+ log("\tremove");
+ if((Atom)e.data.l[0] == 1)
+ log("\ttoggle");
+ if((Atom)e.data.l[0] == 2)
+ log("\tadd");
+ char* prop1 = XGetAtomName(dpy, (Atom)e.data.l[1]);
+ if((Atom)e.data.l[1] == XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", false))
+ {
+ int fID = getFrameID(e.window);
+ int cID = getFrame(fID).cID;
+ getClient(cID).fullscreen = (Atom) e.data.l[0] > 0;
+ setFullscreen(e.window, (Atom) e.data.l[0] > 0);
+ tileRoots();
+ }
+ XFree(prop1);
+ }
+ XFree(name);
+}
+
+static int OnXError(Display* display, XErrorEvent* e)
+{
+ char* error = new char[50];
+ XGetErrorText(dpy, e->type, error, 50);
+ log("XError " << error);
+ delete[] error;
+ return 0;
+}
+
+void tileRoots()
+{
+ for(int i = 0; i < nscreens; i++)
+ {
+ int fullscreenClientID = tile(focusedWorkspaces[i], screens[i].x + cfg.outerGaps, screens[i].y + cfg.outerGaps, screens[i].w - cfg.outerGaps*2, screens[i].h - cfg.outerGaps*2 - bH);
+ if(fullscreenClientID!=noID)
+ {
+ untile(focusedWorkspaces[i]);
+ Client c = getClient(fullscreenClientID);
+ XMapWindow(dpy, c.w);
+ XMoveWindow(dpy, c.w,
+ screens[i].x, screens[i].y);
+ XResizeWindow(dpy, c.w,
+ screens[i].w, screens[i].h);
+ }
+ }
+}
+void untileRoots()
+{
+ for(int i = 0; i < nscreens; i++)
+ {
+ untile(focusedWorkspaces[i]);
+ }
+}
+int tile(int frameID, int x, int y, int w, int h)
+{
+ for(int fID : frames.find(frameID)->second.floatingFrameIDs)
+ {
+ Window w = clients.find(frames.find(fID)->second.cID)->second.w;
+ XMapWindow(dpy, w);
+ }
+ TileDir dir = frames.find(frameID)->second.dir;
+ int i = 0;
+ vector<int>& subFrameIDs = frames.find(frameID)->second.subFrameIDs;
+ for(int fID : subFrameIDs)
+ {
+ Frame f = frames.find(fID)->second;
+ int wX = (dir==horizontal) ? x + i * (w/subFrameIDs.size()) : x;
+ int wY = (dir==vertical) ? y + i * (h/subFrameIDs.size()) : y;
+ int wW = (dir==horizontal) ? w/subFrameIDs.size() : w;
+ int wH = (dir==vertical) ? h/subFrameIDs.size() : h;
+ i++;
+ if(i==subFrameIDs.size())
+ {
+ wW = (dir==horizontal) ? w - (wX - x) : w;
+ wH = (dir==vertical) ? h - (wY - y) : h;
+ }
+ if(!f.isClient)
+ {
+ int fullscreenClientID = tile(fID, wX, wY, wW, wH);
+ if(fullscreenClientID == noID)
+ return fullscreenClientID;
+ continue;
+ }
+ Client c = clients.find(f.cID)->second;
+ if(c.fullscreen)
+ return c.ID;
+ wX += cfg.gaps;
+ wY += cfg.gaps;
+ wW -= cfg.gaps * 2;
+ wH -= cfg.gaps * 2;
+ XMapWindow(dpy, c.w);
+ XMoveWindow(dpy, c.w,
+ wX, wY);
+ XResizeWindow(dpy, c.w,
+ wW, wH);
+ }
+ return noID;
+}
+
+void untile(int frameID)
+{
+ for(int fID : frames.find(frameID)->second.floatingFrameIDs)
+ {
+ Window w = clients.find(frames.find(fID)->second.cID)->second.w;
+ XUnmapWindow(dpy, w);
+ }
+ vector<int>& subFrameIDs = frames.find(frameID)->second.subFrameIDs;
+ TileDir dir = frames.find(frameID)->second.dir;
+ for(int fID : subFrameIDs)
+ {
+ Frame f = frames.find(fID)->second;
+ if(!f.isClient)
+ {
+ untile(fID);
+ continue;
+ }
+ Client c = clients.find(f.cID)->second;
+ XUnmapWindow(dpy, c.w);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ if(argc > 1)
+ {
+ if(strcmp(argv[1], "--version") == 0)
+ {
+ const char* version =
+ "YATwm for X\n"
+ "version 0.1.0";
+ cout << version << endl;
+ return 0;
+ }
+ }
+ //Important init stuff
+ mX = mY = 0;
+ dpy = XOpenDisplay(nullptr);
+ root = Window(DefaultRootWindow(dpy));
+
+ // Adding commands
+ commandsModule.addCommand("exit", exit, 0, {});
+ commandsModule.addCommand("spawn", spawn, 1, {STR_REST});
+ commandsModule.addCommand("spawnOnce", spawnOnce, 1, {STR_REST});
+ commandsModule.addCommand("toggle", toggle, 0, {});
+ commandsModule.addCommand("kill", kill, 0, {});
+ commandsModule.addCommand("changeWS", changeWS, 1, {NUM});
+ commandsModule.addCommand("wToWS", wToWS, 1, {NUM});
+ commandsModule.addCommand("focChange", focChange, 1, {MOVDIR});
+ commandsModule.addCommand("bashSpawn", bashSpawn, 1, {STR_REST});
+ commandsModule.addCommand("bashSpawnOnce", bashSpawnOnce, 1, {STR_REST});
+ commandsModule.addCommand("reload", reload, 0, {});
+ commandsModule.addCommand("wsDump", wsDump, 0, {});
+ commandsModule.addCommand("nextMonitor", nextMonitor, 0, {});
+ commandsModule.addCommand("fullscreen", fullscreen, 0, {});
+
+ //Config
+ std::vector<Err> cfgErr;
+
+ char* confDir = getenv("XDG_CONFIG_HOME");
+ if(confDir != NULL)
+ {
+ cfgErr = cfg.loadFromFile(string(confDir) + "/YATwm/config");
+ }
+ else
+ {
+ string home = getenv("HOME");
+ cfgErr = cfg.loadFromFile(home + "/.config/YATwm/config");
+ }
+
+ //Log
+ yatlog.open(cfg.logFile, std::ios_base::app);
+ yatlog << "\n" << endl;
+
+ //Print starting message
+ log("-------- YATWM STARTING --------");
+
+ //Notifications
+ notify_init("YATwm");
+
+ //Error check config
+ handleConfigErrs(cfgErr);
+
+ screens = new ScreenInfo[1];
+ focusedWorkspaces = new int[1];
+ detectScreens();
+
+ int screenNum = DefaultScreen(dpy);
+ sW = DisplayWidth(dpy, screenNum);
+ sH = DisplayHeight(dpy, screenNum);
+
+ //XSetErrorHandler(OnXError);
+ XSelectInput(dpy, root, SubstructureRedirectMask | SubstructureNotifyMask | KeyPressMask | EnterWindowMask);
+
+ XDefineCursor(dpy, root, XCreateFontCursor(dpy, XC_top_left_arrow));
+ //EWMH
+ initEWMH(&dpy, &root, cfg.workspaces.size(), cfg.workspaces);
+ setCurrentDesktop(1);
+
+ ipc.init();
+
+ for(int i = 1; i < cfg.numWS + 1; i++)
+ {
+ vector<int> v;
+ Frame rootFrame = {i, noID, false, noID, horizontal, v, true};
+ frames.insert(pair<int, Frame>(i, rootFrame));
+ currFrameID++;
+ }
+
+ XSetInputFocus(dpy, root, RevertToNone, CurrentTime);
+ XWarpPointer(dpy, root, root, 0, 0, 0, 0, 960, 540);
+
+ fd_set fdset;
+ int x11fd = ConnectionNumber(dpy);
+ FD_ZERO(&fdset);
+ FD_SET(x11fd, &fdset);
+ FD_SET(ipc.getFD(), &fdset);
+
+ log("Begin mainloop");
+ while(keepGoing)
+ {
+ FD_ZERO(&fdset);
+ FD_SET(x11fd, &fdset);
+ FD_SET(ipc.getFD(), &fdset);
+ int ready = select(x11fd + 1, &fdset, NULL, NULL, NULL);
+ if(FD_ISSET(ipc.getFD(), &fdset))
+ {
+ ipc.doListen();
+ }
+ if(FD_ISSET(x11fd, &fdset))
+ {
+ XEvent e;
+ while(XPending(dpy))
+ {
+ XNextEvent(dpy, &e);
+
+ switch(e.type)
+ {
+ case KeyPress:
+ keybindsModule.handleKeypress(e.xkey);
+ break;
+ case ConfigureRequest:
+ configureRequest(e.xconfigurerequest);
+ break;
+ case MapRequest:
+ mapRequest(e.xmaprequest);
+ break;
+ case DestroyNotify:
+ destroyNotify(e.xdestroywindow);
+ break;
+ case EnterNotify:
+ enterNotify(e.xcrossing);
+ break;
+ case ClientMessage:
+ clientMessage(e.xclient);
+ break;
+ default:
+ // cout << "Unhandled event: " << getEventName(e.type) << endl;
+ break;
+ }
+ }
+ }
+ if(ready == -1)
+ {
+ cout << "E" << endl;
+ log("ERROR");
+ }
+ }
+
+ //Kill children
+ ipc.quitIPC();
+ XCloseDisplay(dpy);
+}
diff --git a/src/structs.h b/src/structs.h
new file mode 100644
index 0000000..8215bf5
--- /dev/null
+++ b/src/structs.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <X11/Xlib.h>
+
+#include <X11/extensions/Xrandr.h>
+#include <string>
+#include <vector>
+
+#define noID -1
+
+struct Client
+{
+ int ID;
+ Window w;
+ bool floating;
+ bool fullscreen;
+};
+
+enum TileDir
+{
+ horizontal,
+ vertical,
+ noDir
+};
+
+struct Frame
+{
+ int ID;
+ int pID;
+
+ bool isClient;
+
+ //If its a client (window)
+ int cID;
+
+ //If it isn't a client
+ TileDir dir;
+ std::vector<int> subFrameIDs;
+ bool isRoot;
+ std::vector<int> floatingFrameIDs;
+ //int whichChildFocused = 0;
+};
+
+struct ScreenInfo
+{
+ std::string name;
+ int x, y, w, h;
+};
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..58116d0
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,32 @@
+#include "util.h"
+
+#include <sstream>
+#include <algorithm>
+
+using std::string;
+
+std::vector<string> split (const string &s, char delim) {
+ std::vector<string> result;
+ std::stringstream ss (s);
+ string item;
+
+ while (getline (ss, item, delim)) {
+ result.push_back (item);
+ }
+
+ return result;
+}
+
+const string evNames[] = {"", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify", "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify", "GenericEvent", "LASTEvent"};
+
+string getEventName(int e)
+{
+ return evNames[e];
+}
+
+string lowercase(string s)
+{
+ string s2 = s;
+ std::transform(s2.begin(), s2.end(), s2.begin(), [](unsigned char c){ return std::tolower(c); });
+ return s2;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..66ecf76
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <X11/Xlib.h>
+
+
+std::string getEventName(int e);
+
+
+std::vector<std::string> split (const std::string &s, char delim);
+
+std::string lowercase(std::string s);
+
+
+struct Globals {
+ Display*& dpy;
+ Window& root;
+};