diff --git a/CMakeLists.txt b/CMakeLists.txt index 86d4c73..419d4f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,11 +5,14 @@ project(Hypr ) add_compile_options(-std=c++17) +find_package(Threads REQUIRED) file(GLOB_RECURSE SRCFILES "src/*.cpp") add_executable(Hypr ${SRCFILES}) +target_link_libraries(Hypr rt) + set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) @@ -21,4 +24,5 @@ target_link_libraries(Hypr xcb-keysyms xcb-randr xcb-xinerama + ${CMAKE_THREAD_LIBS_INIT} ) \ No newline at end of file diff --git a/README.md b/README.md index c1d80cb..ae8881b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Hypr is a Linux tiling window manager for Xorg. It's written in XCB with modern ## Roadmap (not in order) - [ ] Multi-monitor support -- [ ] Status bar +- [x] Status bar ~ UNFINISHED, someone help please - [ ] Floating windows support - [ ] Config system - [x] Workspaces diff --git a/src/bar/Bar.cpp b/src/bar/Bar.cpp new file mode 100644 index 0000000..00d953c --- /dev/null +++ b/src/bar/Bar.cpp @@ -0,0 +1,133 @@ +#include "Bar.hpp" + +#include +#include + +#include "../windowManager.hpp" + +void CStatusBar::setup(Vector2D origin, Vector2D size) { + m_vecPosition = origin; + m_vecSize = size; + + // Create a pixmap for writing to. + m_iPixmap = xcb_generate_id(g_pWindowManager->DisplayConnection); + xcb_create_pixmap(g_pWindowManager->DisplayConnection, g_pWindowManager->Depth, m_iPixmap, m_iWindowID, m_vecSize.x, m_vecSize.y); + + // setup contexts.. ugh. + uint32_t values[4]; + + auto contextBG = &m_mContexts["BG"]; + contextBG->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); + + values[0] = 0x111111; + values[1] = 0x111111; + xcb_create_gc(g_pWindowManager->DisplayConnection, contextBG->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[2] = contextBASETEXT->Font; + + xcb_create_gc(g_pWindowManager->DisplayConnection, contextBASETEXT->GContext, m_iPixmap, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND | XCB_GC_FONT, values); + + // + // + + auto contextHITEXT = &m_mContexts["HITEXT"]; + contextHITEXT->GContext = xcb_generate_id(g_pWindowManager->DisplayConnection); + contextHITEXT->Font = contextBASETEXT->Font; + values[0] = 0x000000; + values[1] = 0xFF3333; + 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] = 0xFF3333; + values[1] = 0x111111; + 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); +} + +int getTextWidth(std::string text, xcb_font_t font) { + + // conv from utf8 to UCS-2 (what the fuck Xorg why) + std::wstring_convert> strCnv; + std::wstring wideString = strCnv.from_bytes(text); + + // 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; +} + +void CStatusBar::draw() { + + xcb_rectangle_t rectangles[] = {{m_vecPosition.x, m_vecPosition.y, m_vecSize.x + m_vecPosition.x, m_vecPosition.y + 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 <= g_pWindowManager->getHighestWorkspaceID(); ++i) { + + const auto WORKSPACE = g_pWindowManager->getWorkspaceByID(i); + + if (!WORKSPACE) + continue; + + std::string workspaceName = std::to_string(i); + + if (WORKSPACE->getID() == g_pWindowManager->activeWorkspace->getID()) { + 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_image_text_8(g_pWindowManager->DisplayConnection, workspaceName.length(), m_iPixmap, + WORKSPACE->getID() == g_pWindowManager->activeWorkspace->getID() ? m_mContexts["HITEXT"].GContext : m_mContexts["BASETEXT"].GContext, + m_vecSize.y * drawnWorkspaces + m_vecSize.y / 2.f - 2, m_vecSize.y - (m_vecSize.y - 10) / 2, workspaceName.c_str()); + + drawnWorkspaces++; + } + + // Draw time to the right + const auto TIMET = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + const std::string TIME = "Hello World!"; + xcb_image_text_8(g_pWindowManager->DisplayConnection, TIME.length(), m_iPixmap, + m_mContexts["BASETEXT"].GContext, m_vecSize.x - getTextWidth(TIME, m_mContexts["BASETEXT"].Font) - 2, (m_vecSize.y - (m_vecSize.y - 10) / 2), + TIME.c_str()); + + 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); +} \ No newline at end of file diff --git a/src/bar/Bar.hpp b/src/bar/Bar.hpp new file mode 100644 index 0000000..8434c2a --- /dev/null +++ b/src/bar/Bar.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "../defines.hpp" + +struct SDrawingContext { + xcb_gcontext_t GContext; + xcb_font_t Font; +}; + +class CStatusBar { +public: + + EXPOSED_MEMBER(WindowID, xcb_window_t, i); + + void draw(); + void setup(Vector2D, Vector2D); + +private: + Vector2D m_vecSize; + Vector2D m_vecPosition; + + xcb_pixmap_t m_iPixmap; + + std::unordered_map m_mContexts; +}; \ No newline at end of file diff --git a/src/events/events.cpp b/src/events/events.cpp index cc31ee2..bd43f46 100644 --- a/src/events/events.cpp +++ b/src/events/events.cpp @@ -1,5 +1,34 @@ #include "events.hpp" +void Events::redraw() { + xcb_expose_event_t exposeEvent; + exposeEvent.window = g_pWindowManager->statusBar.getWindowID(); + exposeEvent.response_type = XCB_EXPOSE; + exposeEvent.x = 0; + exposeEvent.y = 0; + exposeEvent.width = g_pWindowManager->Screen->width_in_pixels; + exposeEvent.height = g_pWindowManager->Screen->height_in_pixels; + xcb_send_event(g_pWindowManager->DisplayConnection, false, g_pWindowManager->statusBar.getWindowID(), XCB_EVENT_MASK_EXPOSURE, (char*)&exposeEvent); + xcb_flush(g_pWindowManager->DisplayConnection); +} + +void handle(sigval val) { + //Events::redraw(); + g_pWindowManager->statusBar.draw(); +} + +void Events::setThread() { + sigevent eventsig; + eventsig.sigev_notify = SIGEV_THREAD; + eventsig.sigev_notify_function = handle; + eventsig.sigev_notify_attributes = NULL; + eventsig.sigev_value.sival_ptr = &timerid; + timer_create(CLOCK_REALTIME, &eventsig, &timerid); + + itimerspec t = {{0, 1000 * (1000 / MAX_FPS)}, {1, 0}}; + timer_settime(timerid, 0, &t, 0); +} + void Events::eventEnter(xcb_generic_event_t* event) { const auto E = reinterpret_cast(event); @@ -33,6 +62,10 @@ void Events::eventDestroy(xcb_generic_event_t* event) { void Events::eventMapWindow(xcb_generic_event_t* event) { const auto E = reinterpret_cast(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); @@ -72,4 +105,11 @@ void Events::eventKeyPress(xcb_generic_event_t* event) { keybind.getDispatcher()(keybind.getCommand()); } } +} + +void Events::eventExpose(xcb_generic_event_t* event) { + const auto E = reinterpret_cast(event); + + // Draw the bar + g_pWindowManager->statusBar.draw(); } \ No newline at end of file diff --git a/src/events/events.hpp b/src/events/events.hpp index d8d1aed..d78922a 100644 --- a/src/events/events.hpp +++ b/src/events/events.hpp @@ -1,4 +1,7 @@ -#include "../defines.hpp" +#include + +#include + #include "../windowManager.hpp" namespace Events { @@ -7,4 +10,12 @@ namespace Events { EVENT(Destroy); EVENT(MapWindow); EVENT(KeyPress); + EVENT(Expose); + + + // A thread to notify xcb to redraw our shiz + void redraw(); + void setThread(); + + inline timer_t timerid; }; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 6aca642..f6e9670 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,5 +28,12 @@ int main(int argc, char** argv) { Debug::log(LOG, "Hypr reached the end! Exiting..."); + xcb_disconnect(g_pWindowManager->DisplayConnection); + + 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; + } + return 0; } \ No newline at end of file diff --git a/src/windowManager.cpp b/src/windowManager.cpp index 5b1aa2d..a1b49d8 100644 --- a/src/windowManager.cpp +++ b/src/windowManager.cpp @@ -1,6 +1,19 @@ #include "windowManager.hpp" #include "./events/events.hpp" +xcb_visualtype_t* CWindowManager::setupColors() { + auto depthIter = xcb_screen_allowed_depths_iterator(Screen); + xcb_visualtype_iterator_t visualIter; + for (; depthIter.rem; xcb_depth_next(&depthIter)) { + if (depthIter.data->depth == Depth) { + visualIter = xcb_depth_visuals_iterator(depthIter.data); + return visualIter.data; + } + } + + return nullptr; +} + void CWindowManager::setupManager() { KeybindManager::reloadAllKeybinds(); @@ -35,12 +48,42 @@ void CWindowManager::setupManager() { workspaces.push_back(protoWorkspace); activeWorkspace = &workspaces[0]; // + + // init visual type, default 32 bit depth + // TODO: fix this, ugh + Depth = 24; //32 + VisualType = setupColors(); + if (VisualType == NULL) { + Depth = 24; + VisualType = setupColors(); + } + + // ---- INIT THE BAR ---- // + + // window + statusBar.setWindowID(xcb_generate_id(DisplayConnection)); + + Values[0] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; + + xcb_create_window(DisplayConnection, Depth, statusBar.getWindowID(), + Screen->root, 0, 0, Screen->width_in_pixels, BAR_HEIGHT, + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, Screen->root_visual, + XCB_CW_EVENT_MASK, Values); + + // map + xcb_map_window(DisplayConnection, statusBar.getWindowID()); + + statusBar.setup(Vector2D(0, 0), Vector2D(Screen->width_in_pixels, BAR_HEIGHT)); + + // start its' update thread + Events::setThread(); } bool CWindowManager::handleEvent() { if (xcb_connection_has_error(DisplayConnection)) return false; + xcb_flush(DisplayConnection); const auto ev = xcb_wait_for_event(DisplayConnection); if (ev != NULL) { switch (ev->response_type & ~0x80) { @@ -65,6 +108,10 @@ bool CWindowManager::handleEvent() { Events::eventKeyPress(ev); Debug::log(LOG, "Event dispatched KEYPRESS"); break; + case XCB_EXPOSE: + Events::eventExpose(ev); + Debug::log(LOG, "Event dispatched EXPOSE"); + break; default: //Debug::log(WARN, "Unknown event: " + std::to_string(ev->response_type & ~0x80)); @@ -228,9 +275,9 @@ void CWindowManager::setEffectiveSizePosUsingConfig(CWindow* pWindow) { pWindow->setEffectiveSize(pWindow->getSize() - (Vector2D(BORDERSIZE, BORDERSIZE) * 2)); // do gaps, set top left - pWindow->setEffectivePosition(pWindow->getEffectivePosition() + Vector2D(DISPLAYLEFT ? GAPS_OUT : GAPS_IN, DISPLAYTOP ? GAPS_OUT : GAPS_IN)); + pWindow->setEffectivePosition(pWindow->getEffectivePosition() + Vector2D(DISPLAYLEFT ? GAPS_OUT : GAPS_IN, DISPLAYTOP ? GAPS_OUT + BAR_HEIGHT : GAPS_IN)); // fix to old size bottom right - pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYLEFT ? GAPS_OUT : GAPS_IN, DISPLAYTOP ? GAPS_OUT : GAPS_IN)); + pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYLEFT ? GAPS_OUT : GAPS_IN, DISPLAYTOP ? GAPS_OUT + BAR_HEIGHT : GAPS_IN)); // set bottom right pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYRIGHT ? GAPS_OUT : GAPS_IN, DISPLAYBOTTOM ? GAPS_OUT : GAPS_IN)); } @@ -499,4 +546,25 @@ void CWindowManager::setAllWorkspaceWindowsDirtyByID(int ID) { if (window.getWorkspaceID() == workspaceID) window.setDirty(true); } +} + +int CWindowManager::getHighestWorkspaceID() { + int max = -1; + for (auto& workspace : workspaces) { + if (workspace.getID() > max) { + max = workspace.getID(); + } + } + + return max; +} + +CWorkspace* CWindowManager::getWorkspaceByID(int ID) { + for (auto& workspace : workspaces) { + if (workspace.getID() == ID) { + return &workspace; + } + } + + return nullptr; } \ No newline at end of file diff --git a/src/windowManager.hpp b/src/windowManager.hpp index 398f327..8df74c3 100644 --- a/src/windowManager.hpp +++ b/src/windowManager.hpp @@ -4,15 +4,21 @@ #include "window.hpp" #include +#include +#include #include "KeybindManager.hpp" -#include "./utilities/Workspace.hpp" +#include "utilities/Workspace.hpp" +#include "bar/Bar.hpp" // temp config values #define BORDERSIZE 1 #define GAPS_IN 5 #define GAPS_OUT 20 +#define BAR_HEIGHT 15 + +#define MAX_FPS 60 class CWindowManager { public: @@ -21,12 +27,17 @@ public: xcb_drawable_t Drawable; uint32_t Values[3]; + uint8_t Depth = 32; + xcb_visualtype_t* VisualType; + std::vector windows; // windows never left. It has always been hiding amongst us. xcb_drawable_t LastWindow = -1; std::vector workspaces; CWorkspace* activeWorkspace = nullptr; + CStatusBar statusBar; + CWindow* getWindowFromDrawable(xcb_drawable_t); void addWindowToVectorSafe(CWindow); void removeWindowFromVectorSafe(xcb_drawable_t); @@ -45,6 +56,8 @@ public: void changeWorkspaceByID(int); void setAllWorkspaceWindowsDirtyByID(int); + int getHighestWorkspaceID(); + CWorkspace* getWorkspaceByID(int); private: @@ -57,6 +70,7 @@ private: void calculateNewTileSetOldTile(CWindow* pWindow); void setEffectiveSizePosUsingConfig(CWindow* pWindow); void cleanupUnusedWorkspaces(); + xcb_visualtype_t* setupColors(); };