From c8c879f1034005dae691c39ee3feeef1c0499ffe Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Fri, 26 Nov 2021 20:20:11 +0100 Subject: [PATCH] Added a cairo bar. READ THE COMMIT DESC. I have had this crash my WM on boot once in a blue moon. This happens rarely and should NOT happen when the WM is already running. If anyone has a solution, feel free to contribute. If you want to be 100% safe, use bar_enabled=0 in the config. --- CMakeLists.txt | 5 ++ README.md | 2 +- example/hypr.conf | 2 + src/KeybindManager.cpp | 4 +- src/bar/Bar.cpp | 113 ++++++++++++++++++-------------- src/bar/Bar.hpp | 9 +++ src/config/ConfigManager.cpp | 1 + src/defines.hpp | 10 +++ src/events/events.cpp | 47 ++++++++++--- src/main.cpp | 3 + src/utilities/AnimationUtil.cpp | 11 ---- src/windowManager.cpp | 22 ++++--- src/windowManager.hpp | 2 +- 13 files changed, 147 insertions(+), 84 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bc23c6..2472dda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,9 @@ add_compile_options(-std=c++17) add_compile_options(-Wall -Wextra) find_package(Threads REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0 harfbuzz cairo gdk) + file(GLOB_RECURSE SRCFILES "src/*.cpp") add_executable(Hypr ${SRCFILES}) @@ -18,6 +21,8 @@ set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) +target_link_libraries(Hypr PkgConfig::deps) + target_link_libraries(Hypr xcb xcb-ewmh diff --git a/README.md b/README.md index f4a8404..d079d83 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,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) -- [ ] Upgrade the status bar rendering to Cairo +- [x] Upgrade the status bar rendering to Cairo - [ ] Better status bar configability - [ ] Rounded corners - [x] Replace default X11 cursor with the pointer diff --git a/example/hypr.conf b/example/hypr.conf index 290706d..7c58be3 100644 --- a/example/hypr.conf +++ b/example/hypr.conf @@ -7,6 +7,8 @@ gaps_in=5 border_size=1 gaps_out=20 bar_height=20 +bar_monitor=0 +bar_enabled=1 max_fps=60 # colors diff --git a/src/KeybindManager.cpp b/src/KeybindManager.cpp index fc989bf..66aa9df 100644 --- a/src/KeybindManager.cpp +++ b/src/KeybindManager.cpp @@ -95,13 +95,13 @@ void KeybindManager::killactive(std::string args) { void KeybindManager::call(std::string args) { if (fork() == 0) { - //setsid(); + setsid(); execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr); _exit(0); } - //wait(NULL); + wait(NULL); } void KeybindManager::movewindow(std::string arg) { diff --git a/src/bar/Bar.cpp b/src/bar/Bar.cpp index c2c7bc1..b68bf83 100644 --- a/src/bar/Bar.cpp +++ b/src/bar/Bar.cpp @@ -6,6 +6,7 @@ #include "../windowManager.hpp" void CStatusBar::setup(int MonitorID) { + Debug::log(LOG, "Creating the bar!"); if (MonitorID > g_pWindowManager->monitors.size()) { MonitorID = 0; @@ -42,20 +43,30 @@ void CStatusBar::setup(int MonitorID) { auto contextBG = &m_mContexts["BG"]; contextBG->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); - values[0] = 0x111111; - values[1] = 0x111111; + values[0] = 0xFF111111; + values[1] = 0xFF111111; xcb_create_gc(g_pWindowManager->DisplayConnection, contextBG->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, values); // // + auto contextBGT = &m_mContexts["BGT"]; + contextBGT->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); + + values[0] = 0x00000000; + values[1] = 0x00000000; + xcb_create_gc(g_pWindowManager->DisplayConnection, contextBGT->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, values); + + // + // + 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] = 0xFFFFFF; - values[1] = 0x111111; + 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); @@ -66,8 +77,8 @@ void CStatusBar::setup(int MonitorID) { auto contextHITEXT = &m_mContexts["HITEXT"]; contextHITEXT->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); contextHITEXT->Font = contextBASETEXT->Font; - values[0] = 0x000000; - values[1] = 0xFF3333; + 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); @@ -78,20 +89,22 @@ void CStatusBar::setup(int MonitorID) { auto contextMEDBG = &m_mContexts["MEDBG"]; contextMEDBG->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); - values[0] = 0xFF3333; - values[1] = 0x111111; + 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); - - // Set the bar to be top - //values[0] = XCB_STACK_MODE_ABOVE; - //xcb_configure_window(g_pWindowManager->DisplayConnection, m_iWindowID, XCB_CONFIG_WINDOW_STACK_MODE, values); + 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); + cairo_surface_destroy(m_pCairoSurface); } void CStatusBar::destroy() { + Debug::log(LOG, "Destroying the bar!"); + xcb_close_font(g_pWindowManager->DisplayConnection, m_mContexts["HITEXT"].Font); xcb_destroy_window(g_pWindowManager->DisplayConnection, m_iWindowID); xcb_destroy_window(g_pWindowManager->DisplayConnection, m_iPixmap); @@ -100,52 +113,48 @@ void CStatusBar::destroy() { xcb_free_gc(g_pWindowManager->DisplayConnection, m_mContexts["MEDBG"].GContext); xcb_free_gc(g_pWindowManager->DisplayConnection, m_mContexts["TEXT"].GContext); xcb_free_gc(g_pWindowManager->DisplayConnection, m_mContexts["HITEXT"].GContext); + + // Free cairo + cairo_destroy(m_pCairo); + m_pCairo = nullptr; } -int getTextWidth(std::string text, xcb_font_t font) { +int CStatusBar::getTextWidth(std::string text) { + cairo_select_font_face(m_pCairo, "Noto Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(m_pCairo, 12); - // conv from utf8 to UCS-2 (what the fuck Xorg why) - std::wstring_convert> strCnv; - std::wstring wideString = strCnv.from_bytes(text); + cairo_text_extents_t textextents; + cairo_text_extents(m_pCairo, text.c_str(), &textextents); + + return textextents.width + 1 /* pad */; +} - // create a xcb string - xcb_char2b_t bytes[wideString.length()]; - - for (int i = 0; i < wideString.length(); ++i) { - bytes[i].byte1 = 0x0; // Only ASCII support. TODO: Maybe more? - bytes[i].byte2 = wideString[i] & 0xFF; - } - - xcb_generic_error_t* error; - const auto COOKIE = xcb_query_text_extents(g_pWindowManager->DisplayConnection, font, wideString.length() - 1, (xcb_char2b_t*)bytes); - xcb_query_text_extents_reply_t* reply = xcb_query_text_extents_reply(g_pWindowManager->DisplayConnection, COOKIE, &error); - if (!reply) { - Debug::log(ERR, "Text extent failed, code " + std::to_string(error->error_code)); - free(error); - return 0; - } - - const auto WIDTH = reply->overall_width; - free(reply); - return WIDTH + 5; +void CStatusBar::drawText(Vector2D pos, std::string text, uint32_t color) { + cairo_select_font_face(m_pCairo, "Noto Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(m_pCairo, 12); + cairo_set_source_rgba(m_pCairo, RED(color), GREEN(color), BLUE(color), ALPHA(color)); + cairo_move_to(m_pCairo, pos.x, pos.y); + cairo_show_text(m_pCairo, text.c_str()); } void CStatusBar::draw() { - // update animations. - AnimationUtil::move(); - 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 (!m_pCairo) { + Debug::log(ERR, "Cairo is null but attempted to 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); // Draw workspaces int drawnWorkspaces = 0; - for (int i = 0; i < openWorkspaces.size(); ++i) { + for (long unsigned int i = 0; i < openWorkspaces.size(); ++i) { const auto WORKSPACE = openWorkspaces[i]; @@ -157,14 +166,11 @@ void CStatusBar::draw() { std::string workspaceName = std::to_string(openWorkspaces[i]); - if (WORKSPACE == MOUSEWORKSPACEID) { - xcb_rectangle_t rectangleActive[] = { { m_vecSize.y * drawnWorkspaces, 0, m_vecSize.y, m_vecSize.y } }; - xcb_poly_fill_rectangle(g_pWindowManager->DisplayConnection, m_iPixmap, m_mContexts["MEDBG"].GContext, 1, rectangleActive); - } + 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_image_text_8(g_pWindowManager->DisplayConnection, workspaceName.length(), m_iPixmap, - WORKSPACE == MOUSEWORKSPACEID ? m_mContexts["HITEXT"].GContext : m_mContexts["BASETEXT"].GContext, - m_vecSize.y * drawnWorkspaces + m_vecSize.y / 2.f - (WORKSPACE > 9 ? 4 : 2), m_vecSize.y - (m_vecSize.y - 10) / 2, workspaceName.c_str()); + drawText(Vector2D(m_vecSize.y * drawnWorkspaces + m_vecSize.y / 2.f - getTextWidth(workspaceName) / 2.f, m_vecSize.y - (m_vecSize.y - 9) / 2.f), + workspaceName, WORKSPACE == MOUSEWORKSPACEID ? 0xFF111111 : 0xFFFFFFFF); drawnWorkspaces++; } @@ -173,14 +179,19 @@ void CStatusBar::draw() { std::string STATUS = exec(m_szStatusCommand.c_str()); STATUS = STATUS.substr(0, (STATUS.length() > 0 ? STATUS.length() - 1 : 9999999)); if (STATUS != "") { - xcb_image_text_8(g_pWindowManager->DisplayConnection, STATUS.length(), m_iPixmap, - m_mContexts["BASETEXT"].GContext, m_vecSize.x - getTextWidth(STATUS, m_mContexts["BASETEXT"].Font), (m_vecSize.y - (m_vecSize.y - 10) / 2), - STATUS.c_str()); + drawText(Vector2D(m_vecSize.x - getTextWidth(STATUS), m_vecSize.y - (m_vecSize.y - 9) / 2.f), + STATUS, 0xFFFFFFFF); } - xcb_flush(g_pWindowManager->DisplayConnection); + cairo_surface_flush(m_pCairoSurface); + + // 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); 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); } \ No newline at end of file diff --git a/src/bar/Bar.hpp b/src/bar/Bar.hpp index 04a4fb5..084389e 100644 --- a/src/bar/Bar.hpp +++ b/src/bar/Bar.hpp @@ -30,5 +30,14 @@ private: xcb_pixmap_t m_iPixmap; + + // Cairo + + cairo_surface_t* m_pCairoSurface = nullptr; + cairo_t* m_pCairo = nullptr; + + void drawText(Vector2D, std::string, uint32_t); + int getTextWidth(std::string); + std::unordered_map m_mContexts; }; \ No newline at end of file diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 9366909..cb9158e 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -17,6 +17,7 @@ void ConfigManager::init() { configValues["max_fps"].intValue = 60; configValues["bar_monitor"].intValue = 0; + configValues["bar_enabled"].intValue = 1; configValues["bar_height"].intValue = 15; configValues["status_command"].strValue = "date +%I:%M\\ %p"; // Time diff --git a/src/defines.hpp b/src/defines.hpp index 36bfb10..c10f0ca 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -13,6 +13,12 @@ #include #include +#include +#include +#include + +#include + #include #include #include @@ -58,3 +64,7 @@ #define HYPRATOM(name) {name, 0} +#define ALPHA(c) ((double)(((c) >> 24) & 0xff) / 255.0) +#define RED(c) ((double)(((c) >> 16) & 0xff) / 255.0) +#define GREEN(c) ((double)(((c) >> 8) & 0xff) / 255.0) +#define BLUE(c) ((double)(((c)) & 0xff) / 255.0) \ No newline at end of file diff --git a/src/events/events.cpp b/src/events/events.cpp index adbdb6f..992eec9 100644 --- a/src/events/events.cpp +++ b/src/events/events.cpp @@ -1,20 +1,50 @@ #include "events.hpp" -void handle() { - g_pWindowManager->statusBar.draw(); +gpointer handle(gpointer data) { + while (1) { + // wait for the main thread to be idle + while (g_pWindowManager->mainThreadBusy) { + ; + } - // check config - ConfigManager::tick(); + // set state to let the main thread know to wait. + g_pWindowManager->animationUtilBusy = true; + + // draw bar + g_pWindowManager->statusBar.draw(); + + // check config + ConfigManager::tick(); + + // update animations. + AnimationUtil::move(); + // + + // restore anim state + g_pWindowManager->animationUtilBusy = false; + + std::this_thread::sleep_for(std::chrono::milliseconds(1000 / ConfigManager::getInt("max_fps"))); + } } void Events::setThread() { - g_pWindowManager->barThread = new std::thread([&]() { + // Start a GTK thread so that Cairo does not complain. + gdk_threads_enter(); + + g_pWindowManager->barThread = g_thread_new("Bar", handle, nullptr); + + if (!g_pWindowManager->barThread) { + Debug::log(ERR, "Gthread failed!"); + return; + } + + /*g_pWindowManager->barThread = new std::thread([&]() { for (;;) { handle(); std::this_thread::sleep_for(std::chrono::milliseconds(1000 / ConfigManager::getInt("max_fps"))); } - }); + });*/ } void Events::eventEnter(xcb_generic_event_t* event) { @@ -23,8 +53,9 @@ void Events::eventEnter(xcb_generic_event_t* event) { // Just focus it and update. g_pWindowManager->setFocusedWindow(E->event); - // vvv insallah no segfaults - g_pWindowManager->getWindowFromDrawable(E->event)->setDirty(true); + if(const auto PENTERWINDOW = g_pWindowManager->getWindowFromDrawable(E->event)) { + PENTERWINDOW->setDirty(true); + } } void Events::eventLeave(xcb_generic_event_t* event) { diff --git a/src/main.cpp b/src/main.cpp index 72485f6..9385682 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ Started by Vaxry on 2021 / 11 / 17 #include #include "windowManager.hpp" +#include "defines.hpp" int main(int argc, char** argv) { clearLogs(); @@ -51,6 +52,8 @@ int main(int argc, char** argv) { xcb_disconnect(g_pWindowManager->DisplayConnection); + gdk_threads_leave(); + if (const auto err = xcb_connection_has_error(g_pWindowManager->DisplayConnection); err != 0) { Debug::log(CRIT, "Exiting because of error " + std::to_string(err)); return err; diff --git a/src/utilities/AnimationUtil.cpp b/src/utilities/AnimationUtil.cpp index d531aa7..d0981cc 100644 --- a/src/utilities/AnimationUtil.cpp +++ b/src/utilities/AnimationUtil.cpp @@ -6,14 +6,6 @@ void AnimationUtil::move() { static std::chrono::time_point lastFrame = std::chrono::high_resolution_clock::now(); const double DELTA = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastFrame).count(); - // wait for the main thread to be idle - while (g_pWindowManager->mainThreadBusy) { - ; - } - - // set state to let the main thread know to wait. - g_pWindowManager->animationUtilBusy = true; - const double ANIMATIONSPEED = ((double)1 / (double)ConfigManager::getFloat("anim.speed")) * DELTA; @@ -66,8 +58,5 @@ void AnimationUtil::move() { if (updateRequired) emptyEvent(); // send a fake request to update dirty windows - // restore anim state - g_pWindowManager->animationUtilBusy = false; - lastFrame = std::chrono::high_resolution_clock::now(); } \ No newline at end of file diff --git a/src/windowManager.cpp b/src/windowManager.cpp index 6ca9028..cc594c3 100644 --- a/src/windowManager.cpp +++ b/src/windowManager.cpp @@ -154,18 +154,20 @@ void CWindowManager::setupManager() { // ---- INIT THE BAR ---- // - for (auto& monitor : monitors) { - if (monitor.primary) { - statusBar.setup(ConfigManager::configValues["bar_monitor"].intValue); + if (ConfigManager::getInt("bar_enabled") == 1) { + for (auto& monitor : monitors) { + if (monitor.primary) { + statusBar.setup(ConfigManager::configValues["bar_monitor"].intValue); + } } + + // Update bar info + updateBarInfo(); + + // start its' update thread + Events::setThread(); } - - // Update bar info - updateBarInfo(); - - // start its' update thread - Events::setThread(); - + Debug::log(LOG, "Bar done."); ConfigManager::loadConfigLoadVars(); diff --git a/src/windowManager.hpp b/src/windowManager.hpp index b9d5c71..4452cb9 100644 --- a/src/windowManager.hpp +++ b/src/windowManager.hpp @@ -41,7 +41,7 @@ public: std::vector activeWorkspaces; CStatusBar statusBar; - std::thread* barThread; + GThread* barThread; std::atomic mainThreadBusy = false; std::atomic animationUtilBusy = false;