summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBossCode45 <human.cyborg42@gmail.com>2023-02-01 20:07:40 +1300
committerBossCode45 <human.cyborg42@gmail.com>2023-02-01 20:07:40 +1300
commit4b2507efc3721fbeb8282e1831273d0a9c445ae6 (patch)
tree6ab48529bb69ac460bd47f55839f215a6aa3cd8c
parent585af104691e91ad12adced578eabe8cfbfba9b8 (diff)
downloadYATwm-4b2507efc3721fbeb8282e1831273d0a9c445ae6.tar.gz
YATwm-4b2507efc3721fbeb8282e1831273d0a9c445ae6.zip
Better error checking for config
-rw-r--r--compile_flags.txt4
-rw-r--r--config.cpp256
-rw-r--r--config.h20
-rw-r--r--main.cpp84
-rw-r--r--makefile5
-rw-r--r--readme.org7
6 files changed, 320 insertions, 56 deletions
diff --git a/compile_flags.txt b/compile_flags.txt
deleted file mode 100644
index d7cdb32..0000000
--- a/compile_flags.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--xc++
--std=c++17
--I
-include
diff --git a/config.cpp b/config.cpp
index 5934f21..d1ec74f 100644
--- a/config.cpp
+++ b/config.cpp
@@ -1,7 +1,10 @@
#include "config.h"
+#include "error.h"
+#include "toml++/toml.hpp"
#include "util.h"
+#include <X11/X.h>
#include <X11/Xlib.h>
#include <string>
@@ -9,10 +12,9 @@
//Just for testing
#include <iostream>
+#include <vector>
-#include <toml++/toml.hpp>
-
-using std::map, std::string;
+using std::map, std::string, std::to_string;
// For testing
using std::cout, std::endl, std::cerr;
@@ -37,43 +39,190 @@ Config::Config()
{
}
-void Config::loadFromFile(string path)
+string to_string(string s)
{
- //free();
- toml::table tbl;
- try
+ return s;
+}
+string to_string(bool b)
+{
+ if(b)
+ return "true";
+ else
+ return "false";
+}
+
+template <typename T>
+T Config::getValue(string path, Err* err)
+{
+ std::optional<T> tblVal = tbl.at_path(path).value<T>();
+ if(tblVal)
+ return *tblVal;
+ else
{
- tbl = toml::parse_file(path);
+ err->code = ERR_CFG_NON_FATAL;
+ T val = *defaults.at_path(path).value<T>();
+ err->errorMessage += "\n\tValue for " + path + " is invalid, using default (" + to_string(val) + ")";
+ return val;
+ }
+}
+
+void Config::loadWorkspaceArrays(toml::table tbl, toml::table defaults, Err* err)
+{
+ if(!tbl["Workspaces"]["workspaceNames"].as_array())
+ {
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\n\tworkspaceNames invalid array, using defaults";
+ return loadWorkspaceArrays(defaults, defaults, err);
+ }
+ workspaceNamesc = tbl["Workspaces"]["workspaceNames"].as_array()->size();
+ workspaceNames = new string[workspaceNamesc];
+ for(int i = 0; i < workspaceNamesc; i++)
+ {
+ auto element = tbl["Workspaces"]["workspaceNames"][i].value<string>();
+ if(element)
+ workspaceNames[i] = *element;
+ else
+ {
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\nelement " + to_string(i) + " in workspaceNames invalid, using defaults";
+ delete[] workspaceNames;
+ return loadWorkspaceArrays(defaults, defaults, err);
+ }
+ }
+ if(!tbl["Workspaces"]["screenPreferences"].as_array())
+ {
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\nscreenPreferences invalid array, using default";
+ delete[] workspaceNames;
+ return loadWorkspaceArrays(defaults, defaults, err);
+ }
+ screenPreferencesc = tbl["Workspaces"]["screenPreferences"].as_array()->size();
+ if(screenPreferencesc != workspaceNamesc)
+ {
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\nworkspaceNames and screenPreferences aren't the same length, using defaults";
+ delete[] workspaceNames;
+ return loadWorkspaceArrays(defaults, defaults, err);
}
- catch (const toml::parse_error& err)
+ screenPreferences = new int*[screenPreferencesc];
+ for(int i = 0; i < screenPreferencesc; i++)
{
- throw err;
+ if(!tbl["Workspaces"]["screenPreferences"][i].as_array())
+ {
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\telement " + to_string(i) + " in screenPreferences in invalid, using defaults";
+ delete[] workspaceNames;
+ for(int k = 0; k < i; k++)
+ {
+ delete[] screenPreferences[k];
+ }
+ delete[] screenPreferences;
+ return loadWorkspaceArrays(defaults, defaults, err);
+ }
+ int* wsScreens = new int[maxMonitors];
+ for(int j = 0; j < maxMonitors; j++)
+ {
+ if(tbl["Workspaces"]["screenPreferences"][i].as_array()->size() <= j)
+ {
+ wsScreens[j] = 0;
+ continue;
+ }
+ auto element = tbl["Workspaces"]["screenPreferences"][i][j].value<int>();
+ if(element)
+ wsScreens[j] = *element;
+ else
+ {
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\telement " + to_string(i) + " " + to_string(j) + " in screenPreferences in invalid, using defaults";
+ delete[] workspaceNames;
+ for(int k = 0; k <= i; k++)
+ {
+ delete[] screenPreferences[k];
+ }
+ delete[] screenPreferences;
+ return loadWorkspaceArrays(defaults, defaults, err);
+ }
+ }
+ screenPreferences[i] = wsScreens;
}
+}
- //Startup
+void Config::loadStartupBash(toml::table tbl, toml::table defaults, Err* err)
+{
+ if(!tbl["Startup"]["startupBash"].as_array())
+ {
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\n\tstartupBash array invalid, using default";
+ return loadStartupBash(defaults, defaults, err);
+ }
startupBashc = tbl["Startup"]["startupBash"].as_array()->size();
- startupBash = new string[startupBashc];
+ std::vector<string> startupBash;
for(int i = 0; i < startupBashc; i++)
{
auto element = tbl["Startup"]["startupBash"][i].value<string>();
if(element)
- {
- startupBash[i] = *element;
- }
+ startupBash.push_back(*element);
else
{
- cerr << "Element " << i << " in `startupBash` is not a string" << endl;
- startupBash[i] = "";
+ err->code = ERR_CFG_NON_FATAL;
+ err->errorMessage += "\n\tstartupBash element " + to_string(i) + " invalid, skipping";
}
}
+ startupBashc = startupBash.size();
+ this->startupBash = new string[startupBashc];
+ for(int i = 0; i < startupBash.size(); i++)
+ {
+ this->startupBash[i] = startupBash[i];
+ }
+}
+
+Err Config::reload()
+{
+ if(!loaded)
+ return {ERR_CFG_FATAL, "Path not set yet, call loadFromFile before reload"};
+ return loadFromFile(path);
+}
+
+Err Config::loadFromFile(string path)
+{
+ if(loaded)
+ {
+ free();
+ }
+ loaded = true;
+ this->path = path;
+ Err err;
+ err.code = NOERR;
+ err.errorMessage = "";
+ defaults = toml::parse_file("/etc/YATwm/config.toml");
+ try
+ {
+ tbl = toml::parse_file(path);
+ }
+ catch (const toml::parse_error& parseErr)
+ {
+ err.code = ERR_CFG_FATAL;
+ string description = (string) parseErr.description();
+ string startCol = std::to_string(parseErr.source().begin.column);
+ string startLine = std::to_string(parseErr.source().begin.line);
+ string endCol = std::to_string(parseErr.source().end.column);
+ string endLine = std::to_string(parseErr.source().end.line);
+ string pos = "Line " + startLine;
+ string what = parseErr.what();
+ err.errorMessage += "\n\t" + description + "\t(" + pos + ")" + "\n\tUsing /etc/YATwm/config.toml instead";
+ tbl = defaults;
+ }
+
+ //Startup
+ loadStartupBash(tbl, defaults, &err);
//Main
- gaps = tbl["Main"]["gaps"].value_or<int>(3);
- outerGaps = tbl["Main"]["gaps"].value_or<int>(3);
- logFile = tbl["Main"]["gaps"].value_or<string>("/tmp/yatlog.txt");
+ gaps = getValue<int>("Main.gaps", &err);
+ outerGaps = getValue<int>("Main.outerGaps", &err);
+ logFile = getValue<string>("Main.logFile", &err);
//Workspaces
- numWS = tbl["Workspaces"]["numWS"].value_or<int>(10);
+ numWS = getValue<int>("Workspaces.numWS", &err);
workspaceNamesc = tbl["Workspaces"]["workspaceNames"].as_array()->size();
workspaceNames = new string[workspaceNamesc];
for(int i = 0; i < workspaceNamesc; i++)
@@ -89,7 +238,7 @@ void Config::loadFromFile(string path)
workspaceNames[i] = "";
}
}
- maxMonitors = tbl["Workspaces"]["maxMonitors"].value_or<int>(2);
+ maxMonitors = getValue<int>("Workspaces.maxMonitors", &err);
screenPreferencesc = tbl["Workspaces"]["screenPreferences"].as_array()->size();
screenPreferences = new int*[screenPreferencesc];
for(int i = 0; i < screenPreferencesc; i++)
@@ -115,14 +264,31 @@ void Config::loadFromFile(string path)
}
//Keybinds
- bool swapSuperAlt = tbl["Keybinds"]["swapSuperAlt"].value_or<bool>(false);
- bindsc = tbl["Keybinds"]["key"].as_array()->size();
- binds = new KeyBind[bindsc];
+ bool swapSuperAlt = getValue<bool>("Keybinds.swapSuperAlt", &err);
+
+ toml::node_view<toml::node> bindsArr = tbl["Keybinds"]["key"];
+ if(!bindsArr.is_array())
+ {
+ err.code = ERR_CFG_NON_FATAL;
+ err.errorMessage += "\n\tBinds array not valid, using default";
+ bindsArr = defaults["Keybinds"]["key"];
+ }
+ std::vector<KeyBind> keyBinds;
+ bindsc = bindsArr.as_array()->size();
for(int i = 0; i < bindsc; i++)
{
KeyBind bind;
bind.modifiers = 0;
- const string bindString = *tbl["Keybinds"]["key"][i]["bind"].value<string>();
+ const std::optional<string> potentialBindString = bindsArr[i]["bind"].value<string>();
+ string bindString;
+ if(potentialBindString)
+ bindString = *potentialBindString;
+ else
+ {
+ err.code = ERR_CFG_NON_FATAL;
+ err.errorMessage += "\n\tSkipping element " + to_string(i) + " of binds as the bind string is invalid";
+ continue;
+ }
std::vector<string> keys = split(bindString, '+');
for(string key : keys)
{
@@ -151,13 +317,34 @@ void Config::loadFromFile(string path)
else
{
bind.keysym = XStringToKeysym(key.c_str());
+ if(bind.keysym == NoSymbol)
+ {
+ err.code = ERR_CFG_NON_FATAL;
+ err.errorMessage += "\n\tSkipping element " + to_string(i) + " of binds as the bind string is invalid";
+ continue;
+ }
}
}
- string funcString = *tbl["Keybinds"]["key"][i]["func"].value<string>();
+ std::optional<string> potentialFuncString = bindsArr[i]["func"].value<string>();
+ string funcString;
+ if(potentialFuncString)
+ funcString = *potentialFuncString;
+ else
+ {
+ err.code = ERR_CFG_NON_FATAL;
+ err.errorMessage += "\n\tSkipping element " + to_string(i) + " of binds as the func string is invalid";
+ continue;
+ }
+ if(funcNameMap.count(funcString) == 0)
+ {
+ err.code = ERR_CFG_NON_FATAL;
+ err.errorMessage += "\n\tSkipping element " + to_string(i) + " of binds as the func string is invalid";
+ continue;
+ }
void(* func) (const KeyArg arg) = funcNameMap.find(funcString)->second;
bind.func = func;
- auto args = tbl["Keybinds"]["key"][i]["args"];
+ auto args = bindsArr[i]["args"];
if(args.is<int64_t>())
{
int num = *args.value<int>();
@@ -183,8 +370,17 @@ void Config::loadFromFile(string path)
{
bind.args = {NULL};
}
- binds[i] = bind;
+ keyBinds.push_back(bind);
+ }
+ bindsc = keyBinds.size();
+ binds = new KeyBind[bindsc];
+ for(int i = 0; i < bindsc; i++)
+ {
+ binds[i] = keyBinds[i];
}
+ if(err.code != NOERR)
+ err.errorMessage = err.errorMessage.substr(1);
+ return err;
}
Config::~Config()
@@ -200,4 +396,6 @@ void Config::free()
delete[] screenPreferences[i];
}
delete[] screenPreferences;
+ delete[] binds;
+ loaded = false;
}
diff --git a/config.h b/config.h
index cfcf5bd..31005e7 100644
--- a/config.h
+++ b/config.h
@@ -1,5 +1,9 @@
#pragma once
+#include "error.h"
+
+#include <toml++/toml.hpp>
+
#include <X11/X.h>
#include <X11/keysym.h>
@@ -51,7 +55,9 @@ class Config
~Config();
void free();
- void loadFromFile(std::string path);
+ Err loadFromFile(std::string path);
+ Err reload();
+
// Startup
std::string* startupBash;
int startupBashc;
@@ -72,4 +78,16 @@ class Config
// Keybinds
KeyBind* binds;
int bindsc;
+ private:
+ template <typename T>
+ T getValue(std::string path, Err* err);
+
+ void loadWorkspaceArrays(toml::table tbl, toml::table defaults, Err* err);
+ void loadStartupBash(toml::table tbl, toml::table defaults, Err* err);
+
+ toml::table tbl;
+ toml::table defaults;
+
+ bool loaded = false;
+ std::string path;
};
diff --git a/main.cpp b/main.cpp
index ff49223..8751dae 100644
--- a/main.cpp
+++ b/main.cpp
@@ -3,8 +3,11 @@
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
+#include <libnotify/notification.h>
#include <toml++/toml.hpp>
+#include <libnotify/notify.h>
+
#include <X11/Xutil.h>
#include <X11/extensions/Xrandr.h>
#include <chrono>
@@ -72,6 +75,7 @@ int FFCF(int sID);
void detectScreens();
void updateMousePos();
void focusRoot(int root);
+void handleConfigErrs(Err cfgErr);
void keyPress(XKeyEvent e);
void configureRequest(XConfigureRequestEvent e);
@@ -152,6 +156,40 @@ void focusRoot(int root)
//log("\tFocusing window: " << w);
XSetInputFocus(dpy, w, RevertToPointerRoot, CurrentTime);
}
+void handleConfigErrs(Err cfgErr)
+{
+ if(cfgErr.code!=NOERR)
+ {
+ if(cfgErr.code == ERR_CFG_FATAL)
+ {
+ log("YATwm fatal error (Code " << cfgErr.code << ")\n" << cfgErr.errorMessage);
+ std::string title = "YATwm fatal config error (Code " + std::to_string(cfgErr.code) + ")";
+ std::string body = cfgErr.errorMessage;
+ 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.errorMessage);
+ 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");
+ }
+ }
+ }
+}
//Keybind commands
void exit(const KeyArg arg)
@@ -467,6 +505,14 @@ void bashSpawn(const KeyArg arg)
void reload(const KeyArg arg)
{
detectScreens();
+
+ //Load config again
+ Err cfgErr = cfg.reload();
+ //Error check
+ handleConfigErrs(cfgErr);
+
+ //Re tile
+ tileRoots();
}
void wsDump(const KeyArg arg)
{
@@ -498,12 +544,12 @@ void keyPress(XKeyEvent e)
{
if(e.same_screen!=1) return;
updateMousePos();
- cout << "Keypress recieved\n";
+ //cout << "Keypress recieved\n";
KeySym keysym = XLookupKeysym(&e, 0);
- cout << "\t" << XKeysymToString(keysym) << " super: " << ((e.state & Mod4Mask) == Mod4Mask) << " alt: " << ((e.state & Mod1Mask) == Mod1Mask) << " shift: " << ((e.state & ShiftMask) == ShiftMask) << std::endl;
+ //cout << "\t" << XKeysymToString(keysym) << " super: " << ((e.state & Mod4Mask) == Mod4Mask) << " alt: " << ((e.state & Mod1Mask) == Mod1Mask) << " shift: " << ((e.state & ShiftMask) == ShiftMask) << std::endl;
for(int i = 0; i < cfg.bindsc; i++)
{
- if(cfg.binds[i].keysym == keysym)// && e.state == cfg.binds[i].modifiers)
+ if(cfg.binds[i].keysym == keysym && (e.state & cfg.binds[i].modifiers) == cfg.binds[i].modifiers)
{
cfg.binds[i].func(cfg.binds[i].args);
}
@@ -862,29 +908,33 @@ void untile(int frameID)
int main(int argc, char** argv)
{
- std::string home = getenv("HOME");
- std::string pathAfterHome = "/.config/YATwm/config.toml";
- std::string file = home + pathAfterHome;
- try
- {
- cfg.loadFromFile(file);
- }
- catch (const toml::parse_error& err)
- {
- std::cerr << "Parsing failed:\n" << err << "\n";
- return 1;
- }
-
+ //Important init stuff
mX = mY = 0;
dpy = XOpenDisplay(nullptr);
root = Window(DefaultRootWindow(dpy));
+
+ //Config
+ std::string home = getenv("HOME");
+ std::string pathAfterHome = "/.config/YATwm/config.toml";
+ std::string file = home + pathAfterHome;
+ Err cfgErr = cfg.loadFromFile(file);
+
+ //Log
yatlog.open(cfg.logFile, std::ios_base::app);
+ //Print starting message
auto timeUnformatted = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(timeUnformatted);
log("\nYAT STARTING: " << std::ctime(&time) << "--------------------------------------");
+ //Notifications
+ notify_init("YATwm");
+
+ //Error check config
+ handleConfigErrs(cfgErr);
+
+
screens = new ScreenInfo[1];
focusedWorkspaces = new int[1];
detectScreens();
@@ -924,8 +974,8 @@ int main(int argc, char** argv)
XSetInputFocus(dpy, root, RevertToNone, CurrentTime);
XWarpPointer(dpy, root, root, 0, 0, 0, 0, 960, 540);
- cout << "Begin mainloop\n";
+ log("Begin mainloop");
while(keepGoing)
{
XEvent e;
diff --git a/makefile b/makefile
index 2788185..a25cb21 100644
--- a/makefile
+++ b/makefile
@@ -1,6 +1,6 @@
.PHONY: clean
CXX := g++
-CXXFLAGS := -std=c++17 -Iinclude #-g -fsanitize=address -fno-omit-frame-pointer
+CXXFLAGS := -std=c++17 -Iinclude `pkg-config --cflags --libs libnotify`# -g -fsanitize=address -fno-omit-frame-pointer
LINKFLAGS := -lX11 -lXrandr
OBJS_DIR := .
OUT_DIR := .
@@ -19,10 +19,13 @@ $(OBJS_DIR)/%.o : $(SOURCE_DIR)/%.cpp
i: $(EXEC)
sudo mv $(EXEC) /usr/bin
sudo cp yat.desktop /usr/share/xsessions
+ sudo mkdir /etc/YATwm
+ sudo cp config.toml /etc/YATwm
install: i
r:
sudo rm /usr/bin/$(EXEC)
sudo rm /usr/share/xsessions/yat.desktop
+ sudo rm -rf /etc/YATwm
remove: r
#Files to be compiled
diff --git a/readme.org b/readme.org
index 5648678..46a4753 100644
--- a/readme.org
+++ b/readme.org
@@ -8,7 +8,7 @@ This only just works, multiple monitors aren't supported and floating windows ca
* Usage instructions
** Installation
*** Pre reqs
-- ~Xlib~ and ~g++~ to build the program
+- ~Xlib~ and ~g++~ and ~libnotify~ to build the program
- ~Xephyr~ for the test script
- ~rofi~, ~alacritty~, ~picom~, ~feh~, ~polybar~, ~qutebrowser~ and ~i3lock~ for the default config
- The current default config is currently just the configuration that I want to use (this will likely change)
@@ -16,10 +16,9 @@ This only just works, multiple monitors aren't supported and floating windows ca
- ~make i~ or ~make install~ to install
- ~make r~ or ~make remove~ to remove
- ~./test~ to test
-** Config (YOU MUST MAKE A WORKING CONFIG FILE OR ELSE YATwm WILL NOT RUN)
+** Config
You can configure YATwm with the config file in ~$HOME/.config/YATwm/config.toml~. I have provided an example config file in this directory that has all the variables set to their defaults.
-*** Problems with the config file
-The arrays (this includes the keybinds) must have values or else stuff breaks as they do not currently have defaults.
+It should alert you with a notification if you have an error, and put the error your log file. It will just use default values if you didn't provide them or provided invalid values (apart from keybinds and startupBash entries, which will just be skipped if invalid (but defaults will be used if the whole array is missing).
*** Startup
Add a bash command to the ~startupBash~ string array in your config and it will execute on startup.
*** General