From 6224ffb0780941ea5402f07e64509073228c8337 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Sat, 27 Nov 2021 19:07:33 +0100 Subject: [PATCH] Bar modules upgrade --- README.md | 2 +- example/hypr.conf | 18 +++- src/bar/Bar.cpp | 177 +++++++++++++++++++++----------- src/bar/Bar.hpp | 33 ++++++ src/bar/BarCommands.cpp | 178 +++++++++++++++++++++++++++++++++ src/bar/BarCommands.hpp | 10 ++ src/config/ConfigManager.cpp | 189 +++++++++++++++++++++++++++++------ src/config/ConfigManager.hpp | 4 +- src/events/events.cpp | 31 +++++- src/ipc/ipc.cpp | 37 ++++--- src/ipc/ipc.hpp | 5 +- src/utilities/Util.cpp | 18 ++++ src/utilities/Util.hpp | 4 +- src/utilities/XCBProps.cpp | 13 +++ src/utilities/XCBProps.hpp | 3 +- src/window.cpp | 2 +- src/windowManager.cpp | 38 ++++++- src/windowManager.hpp | 4 +- 18 files changed, 648 insertions(+), 118 deletions(-) create mode 100644 src/bar/BarCommands.cpp create mode 100644 src/bar/BarCommands.hpp diff --git a/README.md b/README.md index 5d23957..ee74c8a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Hypr is a Linux tiling window manager for Xorg. It's written in XCB with modern ## Roadmap v2 (not in order) - [x] Upgrade the status bar rendering to Cairo -- [ ] Better status bar configability +- [x] Better status bar configability ~ WIP - [ ] Rounded corners - [x] Replace default X11 cursor with the pointer - [x] Fix ghost windows once and for all diff --git a/example/hypr.conf b/example/hypr.conf index 7c58be3..77588a1 100644 --- a/example/hypr.conf +++ b/example/hypr.conf @@ -6,10 +6,20 @@ gaps_in=5 border_size=1 gaps_out=20 -bar_height=20 -bar_monitor=0 -bar_enabled=1 -max_fps=60 +max_fps=60 # max fps for updates of config & animations + +# Bar config +Bar { + height=20 + monitor=0 + enabled=1 + + col.bg=0xff111111 + col.high=0xffff3333 + + module=left,0xff8000ff,0xffffffff,1,workspaces + module=right,0xffffffff,0xff00ff33,1000,$date +%a,\ %b\ %Y\ \ %I:%M\ %p$ +} # colors col.active_border=0x77ff3333 diff --git a/src/bar/Bar.cpp b/src/bar/Bar.cpp index e7852c1..9f2c2ae 100644 --- a/src/bar/Bar.cpp +++ b/src/bar/Bar.cpp @@ -55,8 +55,18 @@ int64_t barMainThread() { Debug::log(LOG, "Bar setup finished!"); + int lazyUpdateCounter = 0; + while (1) { - ConfigManager::tick(); + + // Don't spam these + if (lazyUpdateCounter > 10) { + ConfigManager::tick(); + + lazyUpdateCounter = 0; + } + + ++lazyUpdateCounter; // Recieve the message and send our reply IPCRecieveMessageB(g_pWindowManager->m_sIPCBarPipeIn.szPipeName); @@ -86,6 +96,19 @@ int64_t barMainThread() { return 0; } +void CStatusBar::setupModule(SBarModule* module) { + uint32_t values[2]; + module->bgcontext = xcb_generate_id(g_pWindowManager->DisplayConnection); + values[0] = module->bgcolor; + values[1] = module->bgcolor; + xcb_create_gc(g_pWindowManager->DisplayConnection, module->bgcontext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, values); +} + +void CStatusBar::destroyModule(SBarModule* module) { + if (module->bgcontext) + xcb_free_gc(g_pWindowManager->DisplayConnection, module->bgcontext); +} + void CStatusBar::setup(int MonitorID) { Debug::log(LOG, "Creating the bar!"); @@ -98,7 +121,7 @@ void CStatusBar::setup(int MonitorID) { m_iMonitorID = MonitorID; m_vecPosition = MONITOR.vecPosition; - m_vecSize = Vector2D(MONITOR.vecSize.x, ConfigManager::getInt("bar_height")); + m_vecSize = Vector2D(MONITOR.vecSize.x, ConfigManager::getInt("bar:height")); uint32_t values[4]; @@ -129,8 +152,8 @@ void CStatusBar::setup(int MonitorID) { auto contextBG = &m_mContexts["BG"]; contextBG->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); - values[0] = 0xFF111111; - values[1] = 0xFF111111; + values[0] = ConfigManager::getInt("bar:col.bg"); + values[1] = ConfigManager::getInt("bar:col.bg"); xcb_create_gc(g_pWindowManager->DisplayConnection, contextBG->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, values); // @@ -145,43 +168,16 @@ void CStatusBar::setup(int MonitorID) { // // + auto contextHIGH = &m_mContexts["HIGH"]; + contextHIGH->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); - auto contextBASETEXT = &m_mContexts["BASETEXT"]; - - contextBASETEXT->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); - contextBASETEXT->Font = xcb_generate_id(g_pWindowManager->DisplayConnection); - xcb_open_font(g_pWindowManager->DisplayConnection, contextBASETEXT->Font, 5, "fixed"); - values[0] = 0xFFFFFFFF; - values[1] = 0xFF111111; - values[2] = contextBASETEXT->Font; - - xcb_create_gc(g_pWindowManager->DisplayConnection, contextBASETEXT->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND | XCB_GC_FONT, values); + values[0] = ConfigManager::getInt("bar:col.high"); + values[1] = ConfigManager::getInt("bar:col.high"); + xcb_create_gc(g_pWindowManager->DisplayConnection, contextHIGH->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, values); // // - auto contextHITEXT = &m_mContexts["HITEXT"]; - contextHITEXT->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); - contextHITEXT->Font = contextBASETEXT->Font; - values[0] = 0xFF000000; - values[1] = 0xFFFF3333; - values[2] = contextHITEXT->Font; - - xcb_create_gc(g_pWindowManager->DisplayConnection, contextHITEXT->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND | XCB_GC_FONT, values); - - // - // - - auto contextMEDBG = &m_mContexts["MEDBG"]; - contextMEDBG->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); - - values[0] = 0xFFFF3333; - values[1] = 0xFF111111; - xcb_create_gc(g_pWindowManager->DisplayConnection, contextMEDBG->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, values); - - // don't, i use it later - //xcb_close_font(g_pWindowManager->DisplayConnection, contextBASETEXT->Font); - m_pCairoSurface = cairo_xcb_surface_create(g_pWindowManager->DisplayConnection, m_iPixmap, g_pWindowManager->VisualType, m_vecSize.x, m_vecSize.y); m_pCairo = cairo_create(m_pCairoSurface); @@ -223,6 +219,10 @@ void CStatusBar::drawText(Vector2D pos, std::string text, uint32_t color) { cairo_show_text(m_pCairo, text.c_str()); } +int CStatusBar::getTextHalfY() { + return m_vecSize.y - (m_vecSize.y - 9) / 2.f; +} + void CStatusBar::draw() { // const auto WORKSPACE = g_pWindowManager->getWorkspaceByID(g_pWindowManager->activeWorkspaces[m_iMonitorID]); @@ -235,13 +235,53 @@ void CStatusBar::draw() { return; } - xcb_rectangle_t rectangles[] = {{(int)0, (int)0, (int)m_vecSize.x, (int)m_vecSize.y}}; - xcb_poly_fill_rectangle(g_pWindowManager->DisplayConnection, m_iPixmap, m_mContexts["BG"].GContext, 1, rectangles); + if (ALPHA((uint32_t)ConfigManager::getInt("bar:col.bg")) != 0) { + xcb_rectangle_t rectangles[] = {{(int)0, (int)0, (int)m_vecSize.x, (int)m_vecSize.y}}; + xcb_poly_fill_rectangle(g_pWindowManager->DisplayConnection, m_iPixmap, m_mContexts["BG"].GContext, 1, rectangles); + } + // + // + // DRAW ALL MODULES + + int offLeft = 0; + int offRight = 0; + + for (auto& module : modules) { + + if (!module.bgcontext && !module.isPad) + setupModule(&module); + + if (module.value == "workspaces") { + offLeft += drawWorkspacesModule(&module, offLeft); + } else { + if (module.alignment == LEFT) { + offLeft += drawModule(&module, offLeft); + } else if (module.alignment == RIGHT) { + offRight += drawModule(&module, offRight); + } else { + drawModule(&module, 0); + } + } + } + + // + // + // + + cairo_surface_flush(m_pCairoSurface); + + xcb_copy_area(g_pWindowManager->DisplayConnection, m_iPixmap, m_iWindowID, m_mContexts["BG"].GContext, + 0, 0, 0, 0, m_vecSize.x, m_vecSize.y); + + xcb_flush(g_pWindowManager->DisplayConnection); +} + +// Returns the width +int CStatusBar::drawWorkspacesModule(SBarModule* mod, int off) { // Draw workspaces int drawnWorkspaces = 0; for (long unsigned int i = 0; i < openWorkspaces.size(); ++i) { - const auto WORKSPACE = openWorkspaces[i]; // The LastWindow may be on a different one. This is where the mouse is. @@ -252,32 +292,57 @@ void CStatusBar::draw() { std::string workspaceName = std::to_string(openWorkspaces[i]); - xcb_rectangle_t rectangleActive[] = { { m_vecSize.y * drawnWorkspaces, 0, m_vecSize.y, m_vecSize.y } }; - xcb_poly_fill_rectangle(g_pWindowManager->DisplayConnection, m_iPixmap, WORKSPACE == MOUSEWORKSPACEID ? m_mContexts["MEDBG"].GContext : m_mContexts["BG"].GContext, 1, rectangleActive); + xcb_rectangle_t rectangleActive[] = {{off + m_vecSize.y * drawnWorkspaces, 0, m_vecSize.y, m_vecSize.y}}; + xcb_poly_fill_rectangle(g_pWindowManager->DisplayConnection, m_iPixmap, WORKSPACE == MOUSEWORKSPACEID ? m_mContexts["HIGH"].GContext : m_mContexts["BG"].GContext, 1, rectangleActive); - drawText(Vector2D(m_vecSize.y * drawnWorkspaces + m_vecSize.y / 2.f - getTextWidth(workspaceName) / 2.f, m_vecSize.y - (m_vecSize.y - 9) / 2.f), + drawText(Vector2D(off + m_vecSize.y * drawnWorkspaces + m_vecSize.y / 2.f - getTextWidth(workspaceName) / 2.f, getTextHalfY()), workspaceName, WORKSPACE == MOUSEWORKSPACEID ? 0xFF111111 : 0xFFFFFFFF); drawnWorkspaces++; } - // Draw STATUS to the right - std::string STATUS = exec(m_szStatusCommand.c_str()); - STATUS = STATUS.substr(0, (STATUS.length() > 0 ? STATUS.length() - 1 : 9999999)); - if (STATUS != "") { - drawText(Vector2D(m_vecSize.x - getTextWidth(STATUS), m_vecSize.y - (m_vecSize.y - 9) / 2.f), - STATUS, 0xFFFFFFFF); + return drawnWorkspaces * m_vecSize.y; +} + +int CStatusBar::drawModule(SBarModule* mod, int off) { + + if (mod->isPad) + return mod->pad; + + const int PAD = 4; + + // check if we need to update + if (std::chrono::duration_cast(std::chrono::system_clock::now() - mod->updateLast).count() > mod->updateEveryMs) { + // Yes. Set the new last and do it. + mod->updateLast = std::chrono::system_clock::now(); + + mod->valueCalculated = BarCommands::parseCommand(mod->value); } - - cairo_surface_flush(m_pCairoSurface); + // We have the value, draw the module! - // clear before copying - //xcb_clear_area(g_pWindowManager->DisplayConnection, 0, m_iWindowID, 0, 0, m_vecSize.x, m_vecSize.y); - //xcb_flush(g_pWindowManager->DisplayConnection); + const auto MODULEWIDTH = getTextWidth(mod->valueCalculated) + PAD; - xcb_copy_area(g_pWindowManager->DisplayConnection, m_iPixmap, m_iWindowID, m_mContexts["BG"].GContext, - 0, 0, 0, 0, m_vecSize.x, m_vecSize.y); + if (!MODULEWIDTH || mod->valueCalculated == "") + return 0; // empty module - xcb_flush(g_pWindowManager->DisplayConnection); + Vector2D position; + switch (mod->alignment) { + case LEFT: + position = Vector2D(off, 0); + break; + case RIGHT: + position = Vector2D(m_vecSize.x - off - MODULEWIDTH, 0); + break; + case CENTER: + position = Vector2D(m_vecSize.x / 2.f - MODULEWIDTH / 2.f, 0); + break; + } + + xcb_rectangle_t rects[] = {{ position.x, position.y, MODULEWIDTH, m_vecSize.y }}; + xcb_poly_fill_rectangle(g_pWindowManager->DisplayConnection, m_iPixmap, mod->bgcontext, 1, rects); + + drawText(position + Vector2D(PAD / 2, getTextHalfY()), mod->valueCalculated, mod->color); + + return MODULEWIDTH; } \ No newline at end of file diff --git a/src/bar/Bar.hpp b/src/bar/Bar.hpp index ee0d5ed..f10c086 100644 --- a/src/bar/Bar.hpp +++ b/src/bar/Bar.hpp @@ -4,26 +4,56 @@ #include "../defines.hpp" #include "../ipc/ipc.hpp" +#include "BarCommands.hpp" +#include struct SDrawingContext { xcb_gcontext_t GContext; xcb_font_t Font; }; +enum ModuleAlignment { + LEFT = 0, + CENTER, + RIGHT +}; + +struct SBarModule { + ModuleAlignment alignment; + std::string value; + std::string valueCalculated = ""; + uint64_t color; + uint64_t bgcolor; + + uint64_t updateEveryMs; + std::chrono::system_clock::time_point updateLast; + + xcb_gcontext_t bgcontext = NULL; + + // PADS + bool isPad = false; + int pad = 0; +}; + class CStatusBar { public: EXPOSED_MEMBER(WindowID, xcb_window_t, i); EXPOSED_MEMBER(MonitorID, int, i); EXPOSED_MEMBER(StatusCommand, std::string, sz); // TODO: make the bar better + EXPOSED_MEMBER(LastWindowName, std::string, sz); void draw(); void setup(int MonitorID); void destroy(); + void setupModule(SBarModule*); + void destroyModule(SBarModule*); std::vector openWorkspaces; EXPOSED_MEMBER(CurrentWorkspace, int, i); + std::vector modules; + private: Vector2D m_vecSize; Vector2D m_vecPosition; @@ -38,6 +68,9 @@ private: void drawText(Vector2D, std::string, uint32_t); int getTextWidth(std::string); + int drawModule(SBarModule*, int); + int drawWorkspacesModule(SBarModule*, int); + int getTextHalfY(); std::unordered_map m_mContexts; }; diff --git a/src/bar/BarCommands.cpp b/src/bar/BarCommands.cpp new file mode 100644 index 0000000..24fcc45 --- /dev/null +++ b/src/bar/BarCommands.cpp @@ -0,0 +1,178 @@ +#include "BarCommands.hpp" +#include "../windowManager.hpp" + +std::vector> lastReads; + +std::string getCpuString() { + std::vector> usageRead; + + std::ifstream cpuif; + cpuif.open("/proc/stat"); + + std::string line; + while (std::getline(cpuif, line)) { + // parse line + const auto stats = splitString(line, ' '); + + if (stats.size() < 1) + continue; + + if (stats[0].find("cpu") == std::string::npos) + break; + + // get the percent + try { + const auto user = stol(stats[1]); + const auto nice = stol(stats[2]); + const auto kern = stol(stats[3]); + + // get total + long total = 0; + for (const auto& t : stats) { + if (t.find("c") != std::string::npos) + continue; + + total += stol(t); + } + + usageRead.push_back({user + nice + kern, total}); + } catch( ... ) { ; } // oops + } + + // Compare the values + std::pair lastReadsTotal = {0, 0}; + for (const auto& lr : lastReads) { + lastReadsTotal.first += lr.first; + lastReadsTotal.second += lr.second; + } + + std::pair newReadsTotal = {0, 0}; + for (const auto& nr : usageRead) { + newReadsTotal.first += nr.first; + newReadsTotal.second += nr.second; + } + + std::pair difference = {newReadsTotal.first - lastReadsTotal.first, newReadsTotal.second - lastReadsTotal.second}; + + float percUsage = (float)difference.first / (float)difference.second; + + lastReads.clear(); + + for (const auto& nr : usageRead) { + lastReads.push_back(nr); + } + + return std::to_string((int)(percUsage * 100.f)) + "%"; +} + +std::string getRamString() { + float available = 0; + float total = 0; + + std::ifstream ramif; + ramif.open("/proc/meminfo"); + + std::string line; + while (std::getline(ramif, line)) { + // parse line + const auto KEY = line.substr(0, line.find_first_of(':')); + + int startValue = 0; + for (int i = 0; i < line.length(); ++i) { + const auto& c = line[i]; + if (c >= '0' && c <= '9') { + startValue = i; + break; + } + } + + float VALUE = 0.f; + try { + std::string toVal = line.substr(startValue); + toVal = toVal.substr(0, line.find_first_of(' ', startValue)); + VALUE = stol(toVal); + } catch (...) { ; } // oops + VALUE /= 1024.f; + + if (KEY == "MemTotal") { + total = VALUE; + } else if (KEY == "MemAvailable") { + available = VALUE; + } + } + + return std::to_string((int)(total - available)) + "MB/" + std::to_string((int)total) + "MB"; +} + +std::string getCurrentWindowName() { + return g_pWindowManager->statusBar->getLastWindowName(); +} + +std::string BarCommands::parsePercent(std::string token) { + // check what the token is and act accordingly. + + if (token == "RAM") return getRamString(); + else if (token == "CPU") return getCpuString(); + else if (token == "WINNAME") return getCurrentWindowName(); + + Debug::log(ERR, "Unknown token while parsing module: %" + token + "%"); + + return "Error"; +} + +std::string BarCommands::parseDollar(std::string token) { + const auto result = exec(token.c_str()); + return result.substr(0, result.length() - 1); +} + +std::string BarCommands::parseCommand(std::string command) { + + std::string result = ""; + + for (int i = 0; i < command.length(); ++i) { + + const auto c = command[i]; + + if (c == '%') { + // find the next one + for (int j = i + 1; i < command.length(); ++j) { + if (command[j] == '%') { + // found! + auto toSend = command.substr(i + 1); + toSend = toSend.substr(0, toSend.find_first_of('%')); + result += parsePercent(toSend); + i = j; + break; + } + + if (command[j] == ' ') + break; // if there is a space it's not a token + } + } + + else if (c == '$') { + // find the next one + for (int j = i + 1; i < command.length(); ++j) { + if (command[j] == '$') { + // found! + auto toSend = command.substr(i + 1); + toSend = toSend.substr(0, toSend.find_first_of('$')); + result += parseDollar(toSend); + i = j; + break; + } + + if (j + 1 == command.length()) { + Debug::log(ERR, "Unescaped $ in a module, module command: "); + Debug::log(NONE, command); + } + } + } + + else { + result += command[i]; + } + } + + return result; +} \ No newline at end of file diff --git a/src/bar/BarCommands.hpp b/src/bar/BarCommands.hpp new file mode 100644 index 0000000..6f64d62 --- /dev/null +++ b/src/bar/BarCommands.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "../defines.hpp" +#include "../utilities/Util.hpp" + +namespace BarCommands { + std::string parseCommand(std::string); + + std::string parsePercent(std::string); + std::string parseDollar(std::string); +}; \ No newline at end of file diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 921ee00..3552759 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -16,9 +16,11 @@ void ConfigManager::init() { configValues["max_fps"].intValue = 60; - configValues["bar_monitor"].intValue = 0; - configValues["bar_enabled"].intValue = 1; - configValues["bar_height"].intValue = 15; + configValues["bar:monitor"].intValue = 0; + configValues["bar:enabled"].intValue = 1; + configValues["bar:height"].intValue = 15; + configValues["bar:col.bg"].intValue = 0xFF111111; + configValues["bar:col.high"].intValue = 0xFFFF3333; configValues["status_command"].strValue = "date +%I:%M\\ %p"; // Time @@ -36,6 +38,37 @@ void ConfigManager::init() { applyKeybindsToX(); } +void configSetValueSafe(const std::string& COMMAND, const std::string& VALUE) { + if (ConfigManager::configValues.find(COMMAND) == ConfigManager::configValues.end()) + return; + + auto& CONFIGENTRY = ConfigManager::configValues.at(COMMAND); + if (CONFIGENTRY.intValue != -1) { + try { + if (VALUE.find("0x") == 0) { + // Values with 0x are hex + const auto VALUEWITHOUTHEX = VALUE.substr(2); + CONFIGENTRY.intValue = stol(VALUEWITHOUTHEX, nullptr, 16); + } else + CONFIGENTRY.intValue = stol(VALUE); + } catch (...) { + Debug::log(WARN, "Error reading value of " + COMMAND); + } + } else if (CONFIGENTRY.floatValue != -1) { + try { + CONFIGENTRY.floatValue = stof(VALUE); + } catch (...) { + Debug::log(WARN, "Error reading value of " + COMMAND); + } + } else if (CONFIGENTRY.strValue != "") { + try { + CONFIGENTRY.strValue = VALUE; + } catch (...) { + Debug::log(WARN, "Error reading value of " + COMMAND); + } + } +} + void handleBind(const std::string& command, const std::string& value) { // example: @@ -85,6 +118,96 @@ void handleStatusCommand(const std::string& command, const std::string& args) { g_pWindowManager->statusBar->setStatusCommand(args); } +void parseModule(const std::string& COMMANDC, const std::string& VALUE) { + SBarModule module; + + auto valueCopy = VALUE; + + const auto ALIGN = valueCopy.substr(0, valueCopy.find_first_of(",")); + valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1); + + if (ALIGN == "pad") { + const auto ALIGNR = valueCopy.substr(0, valueCopy.find_first_of(",")); + valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1); + + const auto PADW = valueCopy; + + if (ALIGNR == "left") module.alignment = LEFT; + else if (ALIGNR == "right") module.alignment = RIGHT; + else if (ALIGNR == "center") module.alignment = CENTER; + + try { + module.pad = stol(PADW); + } catch (...) { + Debug::log(ERR, "Module creation pad error: invalid pad"); + return; + } + + module.isPad = true; + + module.color = 0; + module.bgcolor = 0; + + g_pWindowManager->statusBar->modules.push_back(module); + + return; + } + + const auto COL1 = valueCopy.substr(0, valueCopy.find_first_of(",")); + valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1); + + const auto COL2 = valueCopy.substr(0, valueCopy.find_first_of(",")); + valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1); + + const auto UPDATE = valueCopy.substr(0, valueCopy.find_first_of(",")); + valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1); + + const auto COMMAND = valueCopy; + + if (ALIGN == "left") module.alignment = LEFT; + else if (ALIGN == "right") module.alignment = RIGHT; + else if (ALIGN == "center") module.alignment = CENTER; + + try { + module.color = stol(COL1.substr(2), nullptr, 16); + module.bgcolor = stol(COL2.substr(2), nullptr, 16); + } catch (...) { + Debug::log(ERR, "Module creation color error: invalid color"); + return; + } + + try { + module.updateEveryMs = stol(UPDATE); + } catch (...) { + Debug::log(ERR, "Module creation error: invalid update interval"); + return; + } + + module.value = COMMAND; + + g_pWindowManager->statusBar->modules.push_back(module); +} + +void parseBarLine(const std::string& line) { + + // And parse + // check if command + const auto EQUALSPLACE = line.find_first_of('='); + + if (EQUALSPLACE == std::string::npos) + return; + + const auto COMMAND = line.substr(0, EQUALSPLACE); + const auto VALUE = line.substr(EQUALSPLACE + 1); + + // Now check commands + if (COMMAND == "module") { + parseModule(COMMAND, VALUE); + } else { + configSetValueSafe("bar:" + COMMAND, VALUE); + } +} + void parseLine(std::string& line) { // first check if its not a comment const auto COMMENTSTART = line.find_first_of('#'); @@ -95,6 +218,27 @@ void parseLine(std::string& line) { if (COMMENTSTART != std::string::npos) line = line.substr(COMMENTSTART); + // remove shit at the beginning + while (line[0] == ' ' || line[0] == '\t') { + line = line.substr(1); + } + + if (line.find("Bar {") != std::string::npos) { + ConfigManager::isBar = true; + return; + } + + if (line.find("}") != std::string::npos && ConfigManager::isBar) { + ConfigManager::isBar = false; + return; + } + + if (ConfigManager::isBar) { + if (g_pWindowManager->statusBar) + parseBarLine(line); + return; + } + // And parse // check if command const auto EQUALSPLACE = line.find_first_of('='); @@ -116,39 +260,20 @@ void parseLine(std::string& line) { return; } - if (ConfigManager::configValues.find(COMMAND) == ConfigManager::configValues.end()) - return; - - auto& CONFIGENTRY = ConfigManager::configValues.at(COMMAND); - if (CONFIGENTRY.intValue != -1) { - try { - if (VALUE.find("0x") == 0) { - // Values with 0x are hex - const auto VALUEWITHOUTHEX = VALUE.substr(2); - CONFIGENTRY.intValue = stoi(VALUEWITHOUTHEX, nullptr, 16); - } else - CONFIGENTRY.intValue = stoi(VALUE); - } catch (...) { - Debug::log(WARN, "Error reading value of " + COMMAND); - } - } else if (CONFIGENTRY.floatValue != -1) { - try { - CONFIGENTRY.floatValue = stof(VALUE); - } catch (...) { - Debug::log(WARN, "Error reading value of " + COMMAND); - } - } else if (CONFIGENTRY.strValue != "") { - try { - CONFIGENTRY.strValue = VALUE; - } catch (...) { - Debug::log(WARN, "Error reading value of " + COMMAND); - } - } + configSetValueSafe(COMMAND, VALUE); } void ConfigManager::loadConfigLoadVars() { Debug::log(LOG, "Reloading the config!"); + if (loadBar && g_pWindowManager->statusBar) { + // clear modules as we overwrite them + for (auto& m : g_pWindowManager->statusBar->modules) { + g_pWindowManager->statusBar->destroyModule(&m); + } + g_pWindowManager->statusBar->modules.clear(); + } + KeybindManager::keybinds.clear(); const char* const ENVHOME = getenv("HOME"); @@ -184,7 +309,7 @@ void ConfigManager::loadConfigLoadVars() { // Reload the bar as well, don't load it before the default is loaded. if (loadBar && g_pWindowManager->statusBar) { g_pWindowManager->statusBar->destroy(); - g_pWindowManager->statusBar->setup(configValues["bar_monitor"].intValue); + g_pWindowManager->statusBar->setup(configValues["bar:monitor"].intValue); } loadBar = true; diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 3087d3c..c617d9e 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -4,7 +4,7 @@ #include "../utilities/Debug.hpp" struct SConfigValue { - int intValue = -1; + int64_t intValue = -1; float floatValue = -1; std::string strValue = ""; }; @@ -15,6 +15,8 @@ namespace ConfigManager { inline bool loadBar = false; + inline bool isBar = false; // If true we send the command to the bar parser + void init(); void loadConfigLoadVars(); void tick(); diff --git a/src/events/events.cpp b/src/events/events.cpp index 4ed4b2a..084ec75 100644 --- a/src/events/events.cpp +++ b/src/events/events.cpp @@ -1,6 +1,8 @@ #include "events.hpp" gpointer handle(gpointer data) { + int lazyUpdateCounter = 0; + while (1) { // wait for the main thread to be idle while (g_pWindowManager->mainThreadBusy) { @@ -10,13 +12,26 @@ gpointer handle(gpointer data) { // set state to let the main thread know to wait. g_pWindowManager->animationUtilBusy = true; - // check config - ConfigManager::tick(); - // update animations. AnimationUtil::move(); // + // Don't spam these + if (lazyUpdateCounter > 10){ + // Update the active window name + g_pWindowManager->updateActiveWindowName(); + + // Update the bar + g_pWindowManager->updateBarInfo(); + + // check config + ConfigManager::tick(); + + lazyUpdateCounter = 0; + } + + ++lazyUpdateCounter; + // restore anim state g_pWindowManager->animationUtilBusy = false; @@ -80,6 +95,11 @@ CWindow* Events::remapFloatingWindow(int windowID, int forcemonitor) { window.setWorkspaceID(g_pWindowManager->activeWorkspaces[CURRENTSCREEN]); window.setMonitor(CURRENTSCREEN); + // Window name + const auto WINNAME = getWindowName(windowID); + Debug::log(LOG, "New window got name: " + WINNAME); + window.setName(WINNAME); + // For all floating windows, get their default size const auto GEOMETRYCOOKIE = xcb_get_geometry(g_pWindowManager->DisplayConnection, windowID); const auto GEOMETRY = xcb_get_geometry_reply(g_pWindowManager->DisplayConnection, GEOMETRYCOOKIE, 0); @@ -132,6 +152,11 @@ CWindow* Events::remapWindow(int windowID, bool wasfloating, int forcemonitor) { window.setWorkspaceID(g_pWindowManager->activeWorkspaces[CURRENTSCREEN]); window.setMonitor(CURRENTSCREEN); + // Window name + const auto WINNAME = getWindowName(windowID); + Debug::log(LOG, "New window got name: " + WINNAME); + window.setName(WINNAME); + // For all floating windows, get their default size const auto GEOMETRYCOOKIE = xcb_get_geometry(g_pWindowManager->DisplayConnection, windowID); const auto GEOMETRY = xcb_get_geometry_reply(g_pWindowManager->DisplayConnection, GEOMETRYCOOKIE, 0); diff --git a/src/ipc/ipc.cpp b/src/ipc/ipc.cpp index 724998f..c6a57e5 100644 --- a/src/ipc/ipc.cpp +++ b/src/ipc/ipc.cpp @@ -65,8 +65,16 @@ void IPCSendMessage(const std::string path, SIPCMessageMainToBar smessage) { message += std::to_string(w) + ","; } + message += IPC_MESSAGE_SEPARATOR + "lastwindowname" + IPC_MESSAGE_EQUALITY; + + if (const auto PLASTWINDOW = g_pWindowManager->getWindowFromDrawable(g_pWindowManager->LastWindow); PLASTWINDOW) { + message += PLASTWINDOW->getName() + IPC_MESSAGE_SEPARATOR; + } else { + message += IPC_MESSAGE_SEPARATOR; + } + // append the EOF - message += IPC_MESSAGE_SEPARATOR + IPC_END_OF_FILE; + message += IPC_END_OF_FILE; // Send writeToIPCChannel(path, message); @@ -86,21 +94,21 @@ void IPCRecieveMessageB(const std::string path) { try { std::string message = readFromIPCChannel(path); - const auto EOFPOS = message.find_first_of(IPC_END_OF_FILE); + const auto EOFPOS = message.find(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) { + while (message.find(IPC_MESSAGE_SEPARATOR) != std::string::npos && message.find(IPC_MESSAGE_SEPARATOR) != 0) { // 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); + const auto PROP = message.substr(0, message.find(IPC_MESSAGE_SEPARATOR)); + message = message.substr(message.find(IPC_MESSAGE_SEPARATOR) + IPC_MESSAGE_SEPARATOR.length()); // 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); + const auto PROPNAME = PROP.substr(0, PROP.find(IPC_MESSAGE_EQUALITY)); + const auto PROPVALUE = PROP.substr(PROP.find(IPC_MESSAGE_EQUALITY) + 1); if (PROPNAME == "active") { try { @@ -125,6 +133,9 @@ void IPCRecieveMessageB(const std::string path) { // sort std::sort(g_pWindowManager->statusBar->openWorkspaces.begin(), g_pWindowManager->statusBar->openWorkspaces.end()); + } else if (PROPNAME == "lastwindowname") { + g_pWindowManager->statusBar->setLastWindowName(PROPVALUE); + Debug::log(LOG, "update window name to " + PROPVALUE); } } } catch(...) { @@ -143,21 +154,21 @@ void IPCRecieveMessageM(const std::string path) { try { std::string message = readFromIPCChannel(path); - const auto EOFPOS = message.find_first_of(IPC_END_OF_FILE); + const auto EOFPOS = message.find(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) { + while (message.find(IPC_MESSAGE_SEPARATOR) != std::string::npos && message.find(IPC_MESSAGE_SEPARATOR) != 0) { // 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); + const auto PROP = message.substr(0, message.find(IPC_MESSAGE_SEPARATOR)); + message = message.substr(message.find(IPC_MESSAGE_SEPARATOR) + IPC_MESSAGE_SEPARATOR.length()); // 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); + const auto PROPNAME = PROP.substr(0, PROP.find(IPC_MESSAGE_EQUALITY)); + const auto PROPVALUE = PROP.substr(PROP.find(IPC_MESSAGE_EQUALITY) + 1); if (PROPNAME == "wid") { try { diff --git a/src/ipc/ipc.hpp b/src/ipc/ipc.hpp index d6791de..63f02e3 100644 --- a/src/ipc/ipc.hpp +++ b/src/ipc/ipc.hpp @@ -5,12 +5,13 @@ 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)"=" +#define IPC_MESSAGE_SEPARATOR std::string("\t") +#define IPC_MESSAGE_EQUALITY std::string("=") struct SIPCMessageMainToBar { std::vector openWorkspaces; uint64_t activeWorkspace; + std::string lastWindowName; }; struct SIPCMessageBarToMain { diff --git a/src/utilities/Util.cpp b/src/utilities/Util.cpp index 98893e6..cfe12b4 100644 --- a/src/utilities/Util.cpp +++ b/src/utilities/Util.cpp @@ -54,4 +54,22 @@ bool xcbContainsAtom(xcb_get_property_reply_t* PROP, xcb_atom_t ATOM) { return true; return false; +} + +std::vector splitString(std::string in, char c) { + std::vector returns; + + while(in.length() > 0) { + std::string toPush = in.substr(0, in.find_first_of(c)); + if (toPush != "") { + returns.push_back(toPush); + } + + if (in.find_first_of(c) != std::string::npos) + in = in.substr(in.find_first_of(c) + 1); + else + in = ""; + } + + return returns; } \ No newline at end of file diff --git a/src/utilities/Util.hpp b/src/utilities/Util.hpp index 4791022..dc31513 100644 --- a/src/utilities/Util.hpp +++ b/src/utilities/Util.hpp @@ -8,4 +8,6 @@ void clearLogs(); void emptyEvent(); bool xcbContainsAtom(xcb_get_property_reply_t* PROP, xcb_atom_t ATOM); -double parabolic(double from, double to, double incline); \ No newline at end of file +double parabolic(double from, double to, double incline); + +std::vector splitString(std::string, char); \ No newline at end of file diff --git a/src/utilities/XCBProps.cpp b/src/utilities/XCBProps.cpp index 9239f06..30cb990 100644 --- a/src/utilities/XCBProps.cpp +++ b/src/utilities/XCBProps.cpp @@ -43,4 +43,17 @@ std::string getRoleName(int64_t window) { free(role_cookiereply); return returns; +} + +std::string getWindowName(uint64_t window) { + PROP(name_cookie, HYPRATOMS["_NET_WM_NAME"], 128); + + const int len = xcb_get_property_value_length(name_cookiereply); + char* name = strndup((const char*)xcb_get_property_value(name_cookiereply), len); + std::string stringname(name); + free(name); + + free(name_cookiereply); + + return stringname; } \ No newline at end of file diff --git a/src/utilities/XCBProps.hpp b/src/utilities/XCBProps.hpp index 67d6c99..1cffdc7 100644 --- a/src/utilities/XCBProps.hpp +++ b/src/utilities/XCBProps.hpp @@ -5,4 +5,5 @@ std::pair getClassName(int64_t window); -std::string getRoleName(int64_t window); \ No newline at end of file +std::string getRoleName(int64_t window); +std::string getWindowName(uint64_t window); \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index a1b6199..f17687e 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,7 +1,7 @@ #include "window.hpp" #include "windowManager.hpp" -CWindow::CWindow() { this->setDirty(true); this->setFullscreen(false); this->setIsFloating(false); this->setParentNodeID(0); this->setChildNodeAID(0); this->setChildNodeBID(0); } +CWindow::CWindow() { this->setDirty(true); this->setFullscreen(false); this->setIsFloating(false); this->setParentNodeID(0); this->setChildNodeAID(0); this->setChildNodeBID(0); this->setName(""); } CWindow::~CWindow() { } void CWindow::generateNodeID() { diff --git a/src/windowManager.cpp b/src/windowManager.cpp index 6e1d406..af8d56e 100644 --- a/src/windowManager.cpp +++ b/src/windowManager.cpp @@ -255,6 +255,12 @@ bool CWindowManager::handleEvent() { // remove unused workspaces cleanupUnusedWorkspaces(); + // Update last window name + updateActiveWindowName(); + + // Update the bar with the freshest stuff + updateBarInfo(); + xcb_flush(DisplayConnection); // Restore thread state @@ -541,9 +547,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 == ConfigManager::getInt("bar_monitor") ? 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 == ConfigManager::getInt("bar_monitor") ? 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"))); } @@ -1073,8 +1079,22 @@ void CWindowManager::updateBarInfo() { return; } + + message.activeWorkspace = activeWorkspaces[getMonitorFromCursor()->ID]; + auto winname = getWindowFromDrawable(LastWindow) ? getWindowFromDrawable(LastWindow)->getName() : ""; + + for (auto& c : winname) { + // Remove illegal chars + if (c == '=') + c = ' '; + else if (c == '\t') + c = ' '; + } + + message.lastWindowName = winname; + for (auto& workspace : workspaces) { message.openWorkspaces.push_back(workspace.getID()); } @@ -1121,6 +1141,7 @@ bool CWindowManager::shouldBeFloatedOnInit(int64_t window) { return true; } + // // Type stuff // @@ -1146,4 +1167,17 @@ bool CWindowManager::shouldBeFloatedOnInit(int64_t window) { // return false; +} + +void CWindowManager::updateActiveWindowName() { + if (!getWindowFromDrawable(LastWindow)) + return; + + const auto PLASTWINDOW = getWindowFromDrawable(LastWindow); + + auto WINNAME = getWindowName(LastWindow); + if (WINNAME != PLASTWINDOW->getName()) { + Debug::log(LOG, "Update, window got name: " + WINNAME); + PLASTWINDOW->setName(WINNAME); + } } \ No newline at end of file diff --git a/src/windowManager.hpp b/src/windowManager.hpp index 84e69f3..d341aab 100644 --- a/src/windowManager.hpp +++ b/src/windowManager.hpp @@ -96,6 +96,9 @@ public: void createAndOpenAllPipes(); void setupDepth(); + void updateActiveWindowName(); + void updateBarInfo(); + private: // Internal WM functions that don't have to be exposed @@ -110,7 +113,6 @@ public: void setEffectiveSizePosUsingConfig(CWindow* pWindow); void cleanupUnusedWorkspaces(); xcb_visualtype_t* setupColors(); - void updateBarInfo(); void updateRootCursor(); };