Feat: made the status bar into a separate process.

This commit is contained in:
vaxerski 2021-11-27 13:28:30 +01:00
parent 11f3e23a94
commit ec865f0f8e
12 changed files with 409 additions and 67 deletions

View File

@ -24,7 +24,7 @@ Hypr is a Linux tiling window manager for Xorg. It's written in XCB with modern
- Moving / Fullscreening windows
## Roadmap v2 (not in order)
- [x] Upgrade the status bar rendering to Cairo ~ WARNING: only 99% stable (rarely crashes on wm startup)
- [x] Upgrade the status bar rendering to Cairo
- [ ] Better status bar configability
- [ ] Rounded corners
- [x] Replace default X11 cursor with the pointer

View File

@ -2,9 +2,90 @@
#include <codecvt>
#include <locale>
#include "../config/ConfigManager.hpp"
#include "../windowManager.hpp"
bool isParentDead() {
const auto PPID = getppid();
return PPID == 1;
}
int64_t barMainThread() {
// Main already created all the pipes
Debug::log(LOG, "Child says Hello World!");
// Well now this is the init
// it's pretty tricky because we only need to init the stuff we need
g_pWindowManager->DisplayConnection = xcb_connect(NULL, NULL);
if (const auto RET = xcb_connection_has_error(g_pWindowManager->DisplayConnection); RET != 0) {
Debug::log(CRIT, "Connection Failed! Return: " + std::to_string(RET));
return RET;
}
// Screen
g_pWindowManager->Screen = xcb_setup_roots_iterator(xcb_get_setup(g_pWindowManager->DisplayConnection)).data;
if (!g_pWindowManager->Screen) {
Debug::log(CRIT, "Screen was null!");
return 1;
}
Debug::log(LOG, "Bar init Phase 1 done.");
// Init randr for monitors.
g_pWindowManager->setupRandrMonitors();
// Init depth
g_pWindowManager->setupDepth();
// Setup our bar
CStatusBar STATUSBAR;
// Tell everyone we are in the child process.
g_pWindowManager->statusBar = &STATUSBAR;
Debug::log(LOG, "Bar init Phase 2 done.");
// Init config manager
ConfigManager::init();
STATUSBAR.setup(0);
Debug::log(LOG, "Bar setup finished!");
while (1) {
ConfigManager::tick();
// Recieve the message and send our reply
IPCRecieveMessageB(g_pWindowManager->m_sIPCBarPipeIn.szPipeName);
SIPCMessageBarToMain message;
message.windowID = STATUSBAR.getWindowID();
IPCSendMessage(g_pWindowManager->m_sIPCBarPipeOut.szPipeName, message);
//
// draw the bar
STATUSBAR.draw();
if (isParentDead()) {
// Just for debugging
SIPCMessageBarToMain message;
message.windowID = 0;
IPCSendMessage(g_pWindowManager->m_sIPCBarPipeOut.szPipeName, message);
Debug::log(LOG, "Bar parent died!");
return 0; // If the parent died, kill the bar too.
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / ConfigManager::getInt("max_fps")));
}
return 0;
}
void CStatusBar::setup(int MonitorID) {
Debug::log(LOG, "Creating the bar!");
@ -24,6 +105,11 @@ void CStatusBar::setup(int MonitorID) {
// window
m_iWindowID = (xcb_generate_id(g_pWindowManager->DisplayConnection));
// send the message IMMEDIATELY so that the main thread has time to update our WID.
SIPCMessageBarToMain message;
message.windowID = m_iWindowID;
IPCSendMessage(g_pWindowManager->m_sIPCBarPipeOut.szPipeName, message);
values[0] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
xcb_create_window(g_pWindowManager->DisplayConnection, g_pWindowManager->Depth, m_iWindowID,
@ -139,10 +225,10 @@ void CStatusBar::drawText(Vector2D pos, std::string text, uint32_t color) {
void CStatusBar::draw() {
const auto WORKSPACE = g_pWindowManager->getWorkspaceByID(g_pWindowManager->activeWorkspaces[m_iMonitorID]);
// const auto WORKSPACE = g_pWindowManager->getWorkspaceByID(g_pWindowManager->activeWorkspaces[m_iMonitorID]);
if (!WORKSPACE || WORKSPACE->getHasFullscreenWindow()) // TODO: fix this
return; // Do not draw a bar on a fullscreen window.
// if (!WORKSPACE || WORKSPACE->getHasFullscreenWindow()) // TODO: fix this
// return; // Do not draw a bar on a fullscreen window.
if (!m_pCairo) {
Debug::log(ERR, "Cairo is null but attempted to draw!");

View File

@ -3,7 +3,7 @@
#include <unordered_map>
#include "../defines.hpp"
#include "../utilities/AnimationUtil.hpp"
#include "../ipc/ipc.hpp"
struct SDrawingContext {
xcb_gcontext_t GContext;
@ -40,4 +40,7 @@ private:
int getTextWidth(std::string);
std::unordered_map<std::string, SDrawingContext> m_mContexts;
};
};
// Main thread for the bar. Is only initted once in main.cpp so we can do this.
int64_t barMainThread();

View File

@ -81,7 +81,8 @@ void handleRawExec(const std::string& command, const std::string& args) {
}
void handleStatusCommand(const std::string& command, const std::string& args) {
g_pWindowManager->statusBar.setStatusCommand(args);
if (g_pWindowManager->statusBar)
g_pWindowManager->statusBar->setStatusCommand(args);
}
void parseLine(std::string& line) {
@ -174,22 +175,28 @@ void ConfigManager::loadConfigLoadVars() {
}
}
}
ifs.close();
ifs.close();
}
g_pWindowManager->setAllWindowsDirty();
// Reload the bar as well, don't load it before the default is loaded.
if (loadBar) {
g_pWindowManager->statusBar.destroy();
g_pWindowManager->statusBar.setup(configValues["bar_monitor"].intValue);
if (loadBar && g_pWindowManager->statusBar) {
g_pWindowManager->statusBar->destroy();
g_pWindowManager->statusBar->setup(configValues["bar_monitor"].intValue);
}
loadBar = true;
}
void ConfigManager::applyKeybindsToX() {
if (g_pWindowManager->statusBar) {
Debug::log(LOG, "Not applying the keybinds because status bar not null");
return; // If we are in the status bar don't do this.
}
xcb_ungrab_key(g_pWindowManager->DisplayConnection, XCB_GRAB_ANY, g_pWindowManager->Screen->root, XCB_MOD_MASK_ANY);
for (auto& keybind : KeybindManager::keybinds) {

View File

@ -1,4 +1,7 @@
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <xcb/randr.h>
@ -27,6 +30,12 @@
#include "./helpers/Vector.hpp"
#include "./utilities/Debug.hpp"
#ifndef NDEBUG
#define ISDEBUG true
#else
#define ISDEBUG false
#endif
#define EXPOSED_MEMBER(var, type, prefix) \
private: \
type m_##prefix##var; \

View File

@ -10,9 +10,6 @@ gpointer handle(gpointer data) {
// set state to let the main thread know to wait.
g_pWindowManager->animationUtilBusy = true;
// draw bar
g_pWindowManager->statusBar.draw();
// check config
ConfigManager::tick();
@ -223,13 +220,13 @@ CWindow* Events::remapWindow(int windowID, bool wasfloating, int forcemonitor) {
void Events::eventMapWindow(xcb_generic_event_t* event) {
const auto E = reinterpret_cast<xcb_map_request_event_t*>(event);
// make sure it's not the bar!
if (E->window == g_pWindowManager->statusBar.getWindowID())
return;
// Map the window
xcb_map_window(g_pWindowManager->DisplayConnection, E->window);
// make sure it's not the bar!
if (E->window == g_pWindowManager->barWindowID)
return;
// We check if the window is not on our tile-blacklist and if it is, we have a special treatment procedure for it.
// this func also sets some stuff
if (g_pWindowManager->shouldBeFloatedOnInit(E->window)) {
@ -344,8 +341,5 @@ void Events::eventMotionNotify(xcb_generic_event_t* event) {
void Events::eventExpose(xcb_generic_event_t* event) {
const auto E = reinterpret_cast<xcb_expose_event_t*>(event);
// Draw the bar, disable thread warn
g_pWindowManager->mainThreadBusy = false;
g_pWindowManager->statusBar.draw();
g_pWindowManager->mainThreadBusy = true;
// nothing
}

172
src/ipc/ipc.cpp Normal file
View File

@ -0,0 +1,172 @@
#include "ipc.hpp"
#include "../windowManager.hpp"
#include <string.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
std::string readFromIPCChannel(std::string path) {
std::ifstream is;
is.open(path.c_str());
std::string resultString = std::string((std::istreambuf_iterator<char>(is)), std::istreambuf_iterator<char>());
is.close();
return resultString;
};
int writeToIPCChannel(const std::string path, std::string text) {
std::ofstream of;
of.open(path, std::ios::trunc);
of << text;
of.close();
return 0;
}
void IPCSendMessage(const std::string path, SIPCMessageBarToMain smessage) {
if (!g_pWindowManager->statusBar) {
Debug::log(ERR, "Tried to write as a bar from the main thread?!");
return;
}
try {
std::string message = "";
// write the WID
message += "wid" + IPC_MESSAGE_EQUALITY + std::to_string(g_pWindowManager->statusBar->getWindowID()) + IPC_MESSAGE_SEPARATOR;
// append the EOF
message += IPC_END_OF_FILE;
writeToIPCChannel(path, message);
} catch (...) {
Debug::log(WARN, "Error in sending Message B!");
}
}
void IPCSendMessage(const std::string path, SIPCMessageMainToBar smessage) {
if (g_pWindowManager->statusBar) {
Debug::log(ERR, "Tried to write as main from the bar thread?!");
return;
}
try {
std::string message = "";
// write active workspace data
message += "active" + IPC_MESSAGE_EQUALITY + std::to_string(smessage.activeWorkspace) + IPC_MESSAGE_SEPARATOR;
// write workspace data
message += "workspaces" + IPC_MESSAGE_EQUALITY;
for (const auto &w : smessage.openWorkspaces) {
message += std::to_string(w) + ",";
}
// append the EOF
message += IPC_MESSAGE_SEPARATOR + IPC_END_OF_FILE;
// Send
writeToIPCChannel(path, message);
} catch (...) {
Debug::log(WARN, "Error in sending Message M!");
}
}
void IPCRecieveMessageB(const std::string path) {
// recieve message as bar
if (!g_pWindowManager->statusBar) {
Debug::log(ERR, "Tried to read as a bar from the main thread?!");
return;
}
try {
std::string message = readFromIPCChannel(path);
const auto EOFPOS = message.find_first_of(IPC_END_OF_FILE);
if (EOFPOS == std::string::npos)
return;
message = message.substr(0, EOFPOS);
while (message.find_first_of(IPC_MESSAGE_SEPARATOR) != 0 && message.find_first_of(IPC_MESSAGE_SEPARATOR) != std::string::npos) {
// read until done.
const auto PROP = message.substr(0, message.find_first_of(IPC_MESSAGE_SEPARATOR));
message = message.substr(message.find_first_of(IPC_MESSAGE_SEPARATOR) + 1);
// Get the name and value
const auto PROPNAME = PROP.substr(0, PROP.find_first_of(IPC_MESSAGE_EQUALITY));
const auto PROPVALUE = PROP.substr(PROP.find_first_of(IPC_MESSAGE_EQUALITY) + 1);
if (PROPNAME == "active") {
try {
g_pWindowManager->statusBar->setCurrentWorkspace(stoi(PROPVALUE));
} catch (...) {
} // Try Catch because stoi can be weird
} else if (PROPNAME == "workspaces") {
// Read all
auto propvalue = PROPVALUE;
g_pWindowManager->statusBar->openWorkspaces.clear();
while (propvalue.find_first_of(',') != 0 && propvalue.find_first_of(',') != std::string::npos) {
const auto WORKSPACE = propvalue.substr(0, propvalue.find_first_of(','));
propvalue = propvalue.substr(propvalue.find_first_of(',') + 1);
try {
g_pWindowManager->statusBar->openWorkspaces.push_back(stoi(WORKSPACE));
} catch (...) {
} // Try Catch because stoi can be weird
}
// sort
std::sort(g_pWindowManager->statusBar->openWorkspaces.begin(), g_pWindowManager->statusBar->openWorkspaces.end());
}
}
} catch(...) {
Debug::log(WARN, "Error in reading Message B!");
}
}
void IPCRecieveMessageM(const std::string path) {
// recieve message as main
if (g_pWindowManager->statusBar) {
Debug::log(ERR, "Tried to read as main from the bar thread?!");
return;
}
try {
std::string message = readFromIPCChannel(path);
const auto EOFPOS = message.find_first_of(IPC_END_OF_FILE);
if (EOFPOS == std::string::npos)
return;
message = message.substr(0, EOFPOS);
while (message.find_first_of(IPC_MESSAGE_SEPARATOR) != 0 && message.find_first_of(IPC_MESSAGE_SEPARATOR) != std::string::npos) {
// read until done.
const auto PROP = message.substr(0, message.find_first_of(IPC_MESSAGE_SEPARATOR));
message = message.substr(message.find_first_of(IPC_MESSAGE_SEPARATOR) + 1);
// Get the name and value
const auto PROPNAME = PROP.substr(0, PROP.find_first_of(IPC_MESSAGE_EQUALITY));
const auto PROPVALUE = PROP.substr(PROP.find_first_of(IPC_MESSAGE_EQUALITY) + 1);
if (PROPNAME == "wid") {
try {
g_pWindowManager->barWindowID = stoi(PROPVALUE);
} catch (...) {
} // Try Catch because stoi can be weird
}
}
} catch (...) {
Debug::log(WARN, "Error in reading Message M!");
}
}

31
src/ipc/ipc.hpp Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "../defines.hpp"
std::string readFromIPCChannel(const std::string);
int writeToIPCChannel(const std::string, std::string);
#define IPC_END_OF_FILE (std::string)"HYPR_END_OF_FILE"
#define IPC_MESSAGE_SEPARATOR (std::string)"\t"
#define IPC_MESSAGE_EQUALITY (std::string)"="
struct SIPCMessageMainToBar {
std::vector<int> openWorkspaces;
uint64_t activeWorkspace;
};
struct SIPCMessageBarToMain {
uint64_t windowID;
};
struct SIPCPipe {
std::string szPipeName = "";
uint64_t iPipeFD = 0;
};
// /tmp/ is RAM so the speeds will be decent, if anyone wants to implement
// actual pipes feel free.
void IPCSendMessage(const std::string, SIPCMessageMainToBar);
void IPCSendMessage(const std::string, SIPCMessageBarToMain);
void IPCRecieveMessageB(const std::string);
void IPCRecieveMessageM(const std::string);

View File

@ -8,12 +8,33 @@ Started by Vaxry on 2021 / 11 / 17
#include <fstream>
#include "windowManager.hpp"
#include "defines.hpp"
#include "bar/Bar.hpp"
int main(int argc, char** argv) {
clearLogs();
Debug::log(LOG, "Hypr debug log. Built on " + std::string(__DATE__) + " at " + std::string(__TIME__));
// Create all pipes
g_pWindowManager->createAndOpenAllPipes();
Debug::log(LOG, "Pipes done! Forking!");
if (fork() == 0) {
// Child. Bar.
// Sleep for 2 seconds. When launching on a real Xorg session there is some race condition there
// I don't know where it is but this will fix it for now.
// Feel free to search for it.
std::this_thread::sleep_for(std::chrono::seconds(2));
const int BARRET = barMainThread();
Debug::log(BARRET == 0 ? LOG : ERR, "Bar exited with code " + std::to_string(BARRET) + "!");
return 0;
}
Debug::log(LOG, "Parent continuing!");
g_pWindowManager->DisplayConnection = xcb_connect(NULL, NULL);
if (const auto RET = xcb_connection_has_error(g_pWindowManager->DisplayConnection); RET != 0) {
Debug::log(CRIT, "Connection Failed! Return: " + std::to_string(RET));

View File

@ -31,7 +31,7 @@ double parabolic(double from, double to, double incline) {
void emptyEvent() {
xcb_expose_event_t exposeEvent;
exposeEvent.window = g_pWindowManager->statusBar.getWindowID();
exposeEvent.window = 0;
exposeEvent.response_type = 0;
exposeEvent.x = 0;
exposeEvent.y = 0;

View File

@ -15,6 +15,25 @@ xcb_visualtype_t* CWindowManager::setupColors() {
return nullptr;
}
void CWindowManager::setupDepth() {
// init visual type, default 32 bit depth
// TODO: fix this, ugh
Depth = 24; //32
VisualType = setupColors();
if (VisualType == NULL) {
Depth = 24;
VisualType = setupColors();
}
}
void CWindowManager::createAndOpenAllPipes() {
system("mkdir -p /tmp/hypr");
system("cat \" \" > /tmp/hypr/hyprbarin");
system("cat \" \" > /tmp/hypr/hyprbarout");
system("cat \" \" > /tmp/hypr/hyprbarind");
system("cat \" \" > /tmp/hypr/hyprbaroutd");
}
void CWindowManager::updateRootCursor() {
if (xcb_cursor_context_new(DisplayConnection, Screen, &pointerContext) < 0) {
Debug::log(ERR, "Creating a cursor context failed!");
@ -99,11 +118,6 @@ void CWindowManager::setupRandrMonitors() {
}
xcb_flush(DisplayConnection);
}
void CWindowManager::setupManager() {
EWMH::setupInitEWMH();
setupRandrMonitors();
if (monitors.size() == 0) {
// RandR failed!
@ -119,6 +133,11 @@ void CWindowManager::setupManager() {
monitors[i].szName = "Screen" + std::to_string(i);
}
}
}
void CWindowManager::setupManager() {
EWMH::setupInitEWMH();
setupRandrMonitors();
Debug::log(LOG, "RandR done.");
@ -143,32 +162,14 @@ void CWindowManager::setupManager() {
Debug::log(LOG, "Workspace protos done.");
//
// init visual type, default 32 bit depth
// TODO: fix this, ugh
Depth = 24; //32
VisualType = setupColors();
if (VisualType == NULL) {
Depth = 24;
VisualType = setupColors();
}
setupDepth();
// ---- INIT THE BAR ---- //
// ---- INIT THE THREAD FOR ANIM & CONFIG ---- //
if (ConfigManager::getInt("bar_enabled") == 1) {
for (auto& monitor : monitors) {
if (monitor.primary) {
statusBar.setup(ConfigManager::configValues["bar_monitor"].intValue);
}
}
// start its' update thread
Events::setThread();
// Update bar info
updateBarInfo();
// start its' update thread
Events::setThread();
}
Debug::log(LOG, "Bar done.");
Debug::log(LOG, "Thread (Parent) done.");
ConfigManager::loadConfigLoadVars();
@ -193,6 +194,9 @@ bool CWindowManager::handleEvent() {
// Set thread state, halt animations until done.
mainThreadBusy = true;
// Read from the bar
IPCRecieveMessageM(m_sIPCBarPipeOut.szPipeName);
switch (ev->response_type & ~0x80) {
case XCB_ENTER_NOTIFY:
Events::eventEnter(ev);
@ -537,9 +541,9 @@ void CWindowManager::setEffectiveSizePosUsingConfig(CWindow* pWindow) {
pWindow->setEffectiveSize(pWindow->getSize() - (Vector2D(ConfigManager::getInt("border_size"), ConfigManager::getInt("border_size")) * 2));
// do gaps, set top left
pWindow->setEffectivePosition(pWindow->getEffectivePosition() + Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID == statusBar.getMonitorID() ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in")));
pWindow->setEffectivePosition(pWindow->getEffectivePosition() + Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID == ConfigManager::getInt("bar_monitor") ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in")));
// fix to old size bottom right
pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID == statusBar.getMonitorID() ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in")));
pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + (MONITOR->ID == ConfigManager::getInt("bar_monitor") ? ConfigManager::getInt("bar_height") : 0) : ConfigManager::getInt("gaps_in")));
// set bottom right
pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYRIGHT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYBOTTOM ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in")));
}
@ -1055,19 +1059,27 @@ bool CWindowManager::isWorkspaceVisible(int workspaceID) {
}
void CWindowManager::updateBarInfo() {
statusBar.openWorkspaces.clear();
for (auto& workspace : workspaces) {
statusBar.openWorkspaces.push_back(workspace.getID());
}
std::sort(statusBar.openWorkspaces.begin(), statusBar.openWorkspaces.end());
// IPC
// What we need to send:
// - Workspace data
// - Active Workspace
SIPCMessageMainToBar message;
if (!getMonitorFromCursor()) {
Debug::log(ERR, "Monitor was null! (updateBarInfo)");
return;
}
statusBar.setCurrentWorkspace(activeWorkspaces[getMonitorFromCursor()->ID]);
message.activeWorkspace = activeWorkspaces[getMonitorFromCursor()->ID];
for (auto& workspace : workspaces) {
message.openWorkspaces.push_back(workspace.getID());
}
IPCSendMessage(m_sIPCBarPipeIn.szPipeName, message);
}
void CWindowManager::setAllFloatingWindowsTop() {

View File

@ -9,13 +9,15 @@
#include "KeybindManager.hpp"
#include "utilities/Workspace.hpp"
#include "bar/Bar.hpp"
#include "config/ConfigManager.hpp"
#include "utilities/Monitor.hpp"
#include "utilities/Util.hpp"
#include "utilities/AnimationUtil.hpp"
#include "utilities/XCBProps.hpp"
#include "ewmh/ewmh.hpp"
#include "bar/Bar.hpp"
#include "ipc/ipc.hpp"
class CWindowManager {
public:
@ -40,8 +42,12 @@ public:
std::vector<CWorkspace> workspaces;
std::vector<int> activeWorkspaces;
CStatusBar statusBar;
GThread* barThread;
// Pipes
SIPCPipe m_sIPCBarPipeIn = {ISDEBUG ? "/tmp/hypr/hyprbarind" : "/tmp/hypr/hyprbarin", 0};
SIPCPipe m_sIPCBarPipeOut = {ISDEBUG ? "/tmp/hypr/hyprbaroutd" : "/tmp/hypr/hyprbarout", 0};
CStatusBar* statusBar = nullptr;
uint64_t barWindowID = 0;
GThread* barThread; /* Well right now anything but the bar but lol */
std::atomic<bool> mainThreadBusy = false;
std::atomic<bool> animationUtilBusy = false;
@ -86,12 +92,14 @@ public:
bool shouldBeFloatedOnInit(int64_t);
void setupRandrMonitors();
void createAndOpenAllPipes();
void setupDepth();
private:
// Internal WM functions that don't have to be exposed
void setupRandrMonitors();
void sanityCheckOnWorkspace(int);
CWindow* getNeighborInDir(char dir);
void eatWindow(CWindow* a, CWindow* toEat);
@ -104,7 +112,6 @@ public:
xcb_visualtype_t* setupColors();
void updateBarInfo();
void updateRootCursor();
};
inline std::unique_ptr<CWindowManager> g_pWindowManager = std::make_unique<CWindowManager>();