diff --git a/README.md b/README.md index 631f436..0786f2f 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,11 @@ Hypr is a Linux tiling window manager for Xorg. It's written in XCB with modern
# Features +- True parabolic animations +- A built-in status bar - Keybinds - Tiling windows +- Floating windows - Workspaces - Config reloaded instantly upon saving - Moving / Fullscreening windows @@ -18,12 +21,14 @@ Hypr is a Linux tiling window manager for Xorg. It's written in XCB with modern - [x] Multi-monitor support - [x] Status bar ~ Needs expanding - [x] Floating windows support -- [x] Config system ~ Basic done +- [x] Config system - [x] Workspaces - [x] Moving windows / user input (e.g. fullscreening) ~ More to be done probably -- [ ] True lerp animations +- [x] True lerp animations ~ BETA - [x] Rewriting the window system to be a tree +Roadmap complete! Wait for a redone readme and a new one! 🎉 +
# Contributions diff --git a/src/bar/Bar.cpp b/src/bar/Bar.cpp index 9f011e4..acb1993 100644 --- a/src/bar/Bar.cpp +++ b/src/bar/Bar.cpp @@ -132,6 +132,9 @@ int getTextWidth(std::string text, xcb_font_t font) { 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 diff --git a/src/bar/Bar.hpp b/src/bar/Bar.hpp index 85d3a33..569839f 100644 --- a/src/bar/Bar.hpp +++ b/src/bar/Bar.hpp @@ -3,6 +3,7 @@ #include #include "../defines.hpp" +#include "../utilities/AnimationUtil.hpp" struct SDrawingContext { xcb_gcontext_t GContext; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 05e0461..949b939 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -24,6 +24,11 @@ void ConfigManager::init() { configValues["col.active_border"].intValue = 0x77FF3333; configValues["col.inactive_border"].intValue = 0x77222222; + + // animations + configValues["anim.speed"].floatValue = 1; + configValues["anim.enabled"].intValue = 1; + loadConfigLoadVars(); applyKeybindsToX(); } @@ -166,18 +171,6 @@ void ConfigManager::loadConfigLoadVars() { loadBar = true; } -void emptyEvent() { - xcb_expose_event_t exposeEvent; - exposeEvent.window = g_pWindowManager->statusBar.getWindowID(); - exposeEvent.response_type = 0; - 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->Screen->root, XCB_EVENT_MASK_EXPOSURE, (char*)&exposeEvent); - xcb_flush(g_pWindowManager->DisplayConnection); -} - void ConfigManager::applyKeybindsToX() { xcb_ungrab_key(g_pWindowManager->DisplayConnection, XCB_GRAB_ANY, g_pWindowManager->Screen->root, XCB_MOD_MASK_ANY); diff --git a/src/defines.hpp b/src/defines.hpp index a014ba5..b9ffd5d 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -40,3 +40,6 @@ return; \ } \ free(error##name); + + +#define VECTORDELTANONZERO(veca, vecb) ((int)abs(veca.x - vecb.x) > 0 || (int)abs(veca.y - vecb.y) > 0) diff --git a/src/events/events.cpp b/src/events/events.cpp index b1e6499..883474f 100644 --- a/src/events/events.cpp +++ b/src/events/events.cpp @@ -114,8 +114,9 @@ CWindow* Events::remapWindow(int windowID, bool wasfloating) { // Also sets the old one g_pWindowManager->calculateNewWindowParams(&window); - // Focus - g_pWindowManager->setFocusedWindow(windowID); + // Set real size. No animations in the beginning. Maybe later. TODO? + window.setRealPosition(window.getEffectivePosition()); + window.setRealSize(window.getEffectiveSize()); // Add to arr g_pWindowManager->addWindowToVectorSafe(window); @@ -211,8 +212,6 @@ void Events::eventMotionNotify(xcb_generic_event_t* event) { return; } - float Values[2]; - if (abs(POINTERDELTA.x) < 1 && abs(POINTERDELTA.y) < 1) return; // micromovements @@ -221,6 +220,7 @@ void Events::eventMotionNotify(xcb_generic_event_t* event) { PACTINGWINDOW->setPosition(PACTINGWINDOW->getPosition() + POINTERDELTA); PACTINGWINDOW->setEffectivePosition(PACTINGWINDOW->getPosition()); PACTINGWINDOW->setDefaultPosition(PACTINGWINDOW->getPosition()); + PACTINGWINDOW->setRealPosition(PACTINGWINDOW->getPosition()); // update workspace if needed if (g_pWindowManager->getMonitorFromCursor()) { @@ -240,6 +240,7 @@ void Events::eventMotionNotify(xcb_generic_event_t* event) { // apply to other PACTINGWINDOW->setDefaultSize(PACTINGWINDOW->getSize()); PACTINGWINDOW->setEffectiveSize(PACTINGWINDOW->getSize()); + PACTINGWINDOW->setRealSize(PACTINGWINDOW->getSize()); PACTINGWINDOW->setDirty(true); } @@ -250,6 +251,8 @@ void Events::eventMotionNotify(xcb_generic_event_t* event) { void Events::eventExpose(xcb_generic_event_t* event) { const auto E = reinterpret_cast(event); - // Draw the bar + // Draw the bar, disable thread warn + g_pWindowManager->mainThreadBusy = false; g_pWindowManager->statusBar.draw(); + g_pWindowManager->mainThreadBusy = true; } \ No newline at end of file diff --git a/src/utilities/AnimationUtil.cpp b/src/utilities/AnimationUtil.cpp new file mode 100644 index 0000000..f23dd72 --- /dev/null +++ b/src/utilities/AnimationUtil.cpp @@ -0,0 +1,73 @@ +#include "AnimationUtil.hpp" +#include "../windowManager.hpp" + +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; + + + bool updateRequired = false; + // Now we are (or should be, lul) thread-safe. + for (auto& window : g_pWindowManager->windows) { + // check if window needs an animation. + + if (ConfigManager::getInt("anim.enabled") == 0) { + // Disabled animations. instant warps. + window.setRealPosition(window.getEffectivePosition()); + window.setRealSize(window.getEffectiveSize()); + + if (VECTORDELTANONZERO(window.getRealPosition(), window.getEffectivePosition()) + || VECTORDELTANONZERO(window.getRealSize(), window.getEffectiveSize())) { + window.setDirty(true); + updateRequired = true; + } + + continue; + } + + if (VECTORDELTANONZERO(window.getRealPosition(), window.getEffectivePosition())) { + Debug::log(LOG, "Updating position animations for " + std::to_string(window.getDrawable()) + " delta: " + std::to_string(ANIMATIONSPEED)); + + // we need to update it. + window.setDirty(true); + updateRequired = true; + + const auto EFFPOS = window.getEffectivePosition(); + const auto REALPOS = window.getRealPosition(); + + window.setRealPosition(Vector2D(parabolic(REALPOS.x, EFFPOS.x, ANIMATIONSPEED), parabolic(REALPOS.y, EFFPOS.y, ANIMATIONSPEED))); + } + + if (VECTORDELTANONZERO(window.getRealSize(), window.getEffectiveSize())) { + Debug::log(LOG, "Updating size animations for " + std::to_string(window.getDrawable()) + " delta: " + std::to_string(ANIMATIONSPEED)); + + // we need to update it. + window.setDirty(true); + updateRequired = true; + + const auto REALSIZ = window.getRealSize(); + const auto EFFSIZ = window.getEffectiveSize(); + + window.setRealSize(Vector2D(parabolic(REALSIZ.x, EFFSIZ.x, ANIMATIONSPEED), parabolic(REALSIZ.y, EFFSIZ.y, ANIMATIONSPEED))); + } + } + + 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/utilities/AnimationUtil.hpp b/src/utilities/AnimationUtil.hpp new file mode 100644 index 0000000..45d5082 --- /dev/null +++ b/src/utilities/AnimationUtil.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +#include "Util.hpp" + +namespace AnimationUtil { + + void move(); +}; \ No newline at end of file diff --git a/src/utilities/Util.cpp b/src/utilities/Util.cpp index e251c74..f999b77 100644 --- a/src/utilities/Util.cpp +++ b/src/utilities/Util.cpp @@ -1,4 +1,5 @@ #include "Util.hpp" +#include "../windowManager.hpp" // Execute a shell command and get the output std::string exec(const char* cmd) { @@ -22,4 +23,20 @@ void clearLogs() { logs.open(DEBUGPATH, std::ios::out | std::ios::trunc); logs << " "; logs.close(); +} + +double parabolic(double from, double to, double incline) { + return from + ((to - from) / incline); +} + +void emptyEvent() { + xcb_expose_event_t exposeEvent; + exposeEvent.window = g_pWindowManager->statusBar.getWindowID(); + exposeEvent.response_type = 0; + 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->Screen->root, XCB_EVENT_MASK_EXPOSURE, (char*)&exposeEvent); + xcb_flush(g_pWindowManager->DisplayConnection); } \ No newline at end of file diff --git a/src/utilities/Util.hpp b/src/utilities/Util.hpp index 7df0d7a..f8373f0 100644 --- a/src/utilities/Util.hpp +++ b/src/utilities/Util.hpp @@ -5,3 +5,6 @@ std::string exec(const char* cmd); void clearLogs(); +void emptyEvent(); + +double parabolic(double from, double to, double incline); \ No newline at end of file diff --git a/src/window.hpp b/src/window.hpp index d808b6d..fc14844 100644 --- a/src/window.hpp +++ b/src/window.hpp @@ -44,6 +44,8 @@ public: EXPOSED_MEMBER(EffectiveSize, Vector2D, vec); EXPOSED_MEMBER(EffectivePosition, Vector2D, vec); EXPOSED_MEMBER(Position, Vector2D, vec); + EXPOSED_MEMBER(RealSize, Vector2D, vec); + EXPOSED_MEMBER(RealPosition, Vector2D, vec); EXPOSED_MEMBER(IsFloating, bool, b); EXPOSED_MEMBER(Drawable, int64_t, i); // int64_t because it's my internal ID system too. diff --git a/src/windowManager.cpp b/src/windowManager.cpp index 8a10ea7..1059bca 100644 --- a/src/windowManager.cpp +++ b/src/windowManager.cpp @@ -188,7 +188,7 @@ void CWindowManager::setupManager() { updateBarInfo(); // start its' update thread - //Events::setThread(); + Events::setThread(); Debug::log(LOG, "Bar done."); @@ -204,6 +204,13 @@ bool CWindowManager::handleEvent() { xcb_flush(DisplayConnection); const auto ev = xcb_wait_for_event(DisplayConnection); if (ev != NULL) { + while (animationUtilBusy) { + ; // wait for it to finish + } + + // Set thread state, halt animations until done. + mainThreadBusy = true; + switch (ev->response_type & ~0x80) { case XCB_ENTER_NOTIFY: Events::eventEnter(ev); @@ -258,6 +265,9 @@ bool CWindowManager::handleEvent() { xcb_flush(DisplayConnection); + // Restore thread state + mainThreadBusy = false; + return true; } @@ -343,14 +353,14 @@ void CWindowManager::refreshDirtyWindows() { continue; } - Values[0] = (int)window.getEffectiveSize().x; - Values[1] = (int)window.getEffectiveSize().y; + Values[0] = (int)window.getRealSize().x; + Values[1] = (int)window.getRealSize().y; xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, Values); // Update the position because the border makes the window jump // I have added the bordersize vec2d before in the setEffectiveSizePosUsingConfig function. - Values[0] = (int)window.getEffectivePosition().x - ConfigManager::getInt("border_size"); - Values[1] = (int)window.getEffectivePosition().y - ConfigManager::getInt("border_size"); + Values[0] = (int)window.getRealPosition().x - ConfigManager::getInt("border_size"); + Values[1] = (int)window.getRealPosition().y - ConfigManager::getInt("border_size"); xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, Values); // Focused special border. @@ -441,8 +451,6 @@ void CWindowManager::setEffectiveSizePosUsingConfig(CWindow* pWindow) { pWindow->setEffectivePosition(pWindow->getPosition() + Vector2D(ConfigManager::getInt("border_size"), ConfigManager::getInt("border_size"))); pWindow->setEffectiveSize(pWindow->getSize() - (Vector2D(ConfigManager::getInt("border_size"), ConfigManager::getInt("border_size")) * 2)); - //TODO: make windows with no bar taller, this aint working chief - // 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"))); // fix to old size bottom right @@ -547,6 +555,8 @@ void CWindowManager::calculateNewWindowParams(CWindow* pWindow) { calculateNewFloatingWindow(pWindow); } + setEffectiveSizePosUsingConfig(pWindow); + pWindow->setDirty(true); } diff --git a/src/windowManager.hpp b/src/windowManager.hpp index 794e3b2..fdf947e 100644 --- a/src/windowManager.hpp +++ b/src/windowManager.hpp @@ -13,6 +13,7 @@ #include "config/ConfigManager.hpp" #include "utilities/Monitor.hpp" #include "utilities/Util.hpp" +#include "utilities/AnimationUtil.hpp" class CWindowManager { public: @@ -40,6 +41,9 @@ public: CStatusBar statusBar; std::thread* barThread; + std::atomic mainThreadBusy = false; + std::atomic animationUtilBusy = false; + CWindow* getWindowFromDrawable(int64_t); void addWindowToVectorSafe(CWindow); void removeWindowFromVectorSafe(int64_t);