From 53a64d9574861d09468d431c2d720472b749d3ed Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Thu, 18 Nov 2021 18:04:09 +0100 Subject: [PATCH] initial commit, very simple and buggy prolly :) --- .gitignore | 3 + CMakeLists.txt | 24 ++++ README.md | 2 + src/KeybindManager.cpp | 10 ++ src/KeybindManager.hpp | 11 ++ src/defines.hpp | 24 ++++ src/events/events.cpp | 56 ++++++++++ src/events/events.hpp | 8 ++ src/helpers/Vector.cpp | 19 ++++ src/helpers/Vector.hpp | 29 +++++ src/main.cpp | 32 ++++++ src/utilities/Debug.cpp | 26 +++++ src/utilities/Debug.hpp | 14 +++ src/utilities/Keybind.hpp | 9 ++ src/window.cpp | 4 + src/window.hpp | 29 +++++ src/windowManager.cpp | 226 ++++++++++++++++++++++++++++++++++++++ src/windowManager.hpp | 29 +++++ 18 files changed, 555 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 src/KeybindManager.cpp create mode 100644 src/KeybindManager.hpp create mode 100644 src/defines.hpp create mode 100644 src/events/events.cpp create mode 100644 src/events/events.hpp create mode 100644 src/helpers/Vector.cpp create mode 100644 src/helpers/Vector.hpp create mode 100644 src/main.cpp create mode 100644 src/utilities/Debug.cpp create mode 100644 src/utilities/Debug.hpp create mode 100644 src/utilities/Keybind.hpp create mode 100644 src/window.cpp create mode 100644 src/window.hpp create mode 100644 src/windowManager.cpp create mode 100644 src/windowManager.hpp diff --git a/.gitignore b/.gitignore index 46f42f8..4e00cfc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ install_manifest.txt compile_commands.json CTestTestfile.cmake _deps + +build/ +.vscode/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..86d4c73 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.4) +project(Hypr + VERSION 0.1 + DESCRIPTION "A Modern OOP C++ Window Manager" +) + +add_compile_options(-std=c++17) + +file(GLOB_RECURSE SRCFILES "src/*.cpp") + +add_executable(Hypr ${SRCFILES}) + +set(CPACK_PROJECT_NAME ${PROJECT_NAME}) +set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) +include(CPack) + +target_link_libraries(Hypr + xcb + xcb-ewmh + xcb-icccm + xcb-keysyms + xcb-randr + xcb-xinerama +) \ No newline at end of file diff --git a/README.md b/README.md index 39c7f65..54650d5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # Hypr Hypr is a tiling window manager written in modern OOP C++ + +I will make this readme better in the future cant be arsed to do it rn \ No newline at end of file diff --git a/src/KeybindManager.cpp b/src/KeybindManager.cpp new file mode 100644 index 0000000..39e90c1 --- /dev/null +++ b/src/KeybindManager.cpp @@ -0,0 +1,10 @@ +#include "KeybindManager.hpp" + +Keybind* KeybindManager::findKeybindByKey(int mod, xcb_keysym_t keysym) { + for(auto& key : KeybindManager::keybinds) { + if (keysym == key.getKeysym() && mod == key.getMod()) { + return &key; + } + } + return nullptr; +} \ No newline at end of file diff --git a/src/KeybindManager.hpp b/src/KeybindManager.hpp new file mode 100644 index 0000000..1ee73eb --- /dev/null +++ b/src/KeybindManager.hpp @@ -0,0 +1,11 @@ +#include "utilities/Keybind.hpp" +#include + +namespace KeybindManager { + std::vector keybinds; + + + // -- Methods -- // + + Keybind* findKeybindByKey(int mod, xcb_keysym_t keysym); +}; \ No newline at end of file diff --git a/src/defines.hpp b/src/defines.hpp new file mode 100644 index 0000000..4598c84 --- /dev/null +++ b/src/defines.hpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +#include + +#include "./helpers/Vector.hpp" + +#include "./utilities/Debug.hpp" + +#define EXPOSED_MEMBER(var, type, prefix) \ + private: \ + type m_##prefix##var; \ + public: \ + type get##var() { return this->m_##prefix##var; } \ + void set##var(type value) { this->m_##prefix##var = value; } + + +#define EVENT(name) \ + void event##name(xcb_generic_event_t* event); + +#define STICKS(a, b) abs(a - b) < 2 \ No newline at end of file diff --git a/src/events/events.cpp b/src/events/events.cpp new file mode 100644 index 0000000..9d47841 --- /dev/null +++ b/src/events/events.cpp @@ -0,0 +1,56 @@ +#include "events.hpp" + +void Events::eventEnter(xcb_generic_event_t* event) { + const auto E = reinterpret_cast(event); + + // Just focus it and update. + WindowManager::setFocusedWindow(E->event); + + // vvv insallah no segfaults + WindowManager::getWindowFromDrawable(E->event)->setDirty(true); +} + +void Events::eventDestroy(xcb_generic_event_t* event) { + const xcb_destroy_notify_event_t* E = reinterpret_cast(event); + xcb_kill_client(WindowManager::DisplayConnection, E->window); + + // fix last window + const auto CLOSEDWINDOW = WindowManager::getWindowFromDrawable(E->window); + if (CLOSEDWINDOW) { + WindowManager::fixWindowOnClose(CLOSEDWINDOW); + + // delete off of the arr + WindowManager::removeWindowFromVectorSafe(E->window); + } +} + +void Events::eventMapWindow(xcb_generic_event_t* event) { + const xcb_map_request_event_t* E = reinterpret_cast(event); + + // Map the window + xcb_map_window(WindowManager::DisplayConnection, E->window); + + // Do the setup of the window's params and stuf + CWindow window; + window.setDrawable(E->window); + window.setIsFloating(false); + window.setDirty(true); + + // Also sets the old one + WindowManager::calculateNewWindowParams(&window); + + // Focus + WindowManager::setFocusedWindow(E->window); + + // Add to arr + WindowManager::addWindowToVectorSafe(window); + + Debug::log(LOG, "Created a new window! X: " + std::to_string(window.getPosition().x) + ", Y: " + std::to_string(window.getPosition().y) + ", W: " + + std::to_string(window.getSize().x) + ", H:" + std::to_string(window.getSize().y) + " ID: " + std::to_string(E->window)); + + // Set map values + WindowManager::Values[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE; + xcb_change_window_attributes_checked(WindowManager::DisplayConnection, E->window, XCB_CW_EVENT_MASK, WindowManager::Values); + + WindowManager::setFocusedWindow(E->window); +} \ No newline at end of file diff --git a/src/events/events.hpp b/src/events/events.hpp new file mode 100644 index 0000000..a7547f5 --- /dev/null +++ b/src/events/events.hpp @@ -0,0 +1,8 @@ +#include "../defines.hpp" +#include "../windowManager.hpp" + +namespace Events { + EVENT(Enter); + EVENT(Destroy); + EVENT(MapWindow); +}; \ No newline at end of file diff --git a/src/helpers/Vector.cpp b/src/helpers/Vector.cpp new file mode 100644 index 0000000..0c2ff76 --- /dev/null +++ b/src/helpers/Vector.cpp @@ -0,0 +1,19 @@ +#include "Vector.hpp" + +Vector2D::Vector2D(double xx, double yy) { + x = xx; + y = yy; +} + +Vector2D::Vector2D() { x = 0; y = 0; } +Vector2D::~Vector2D() {} + +double Vector2D::normalize() { + // get max abs + const auto max = abs(x) > abs(y) ? abs(x) : abs(y); + + x /= max; + y /= max; + + return max; +} \ No newline at end of file diff --git a/src/helpers/Vector.hpp b/src/helpers/Vector.hpp new file mode 100644 index 0000000..9466582 --- /dev/null +++ b/src/helpers/Vector.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +class Vector2D { + public: + Vector2D(double, double); + Vector2D(); + ~Vector2D(); + + double x = 0; + double y = 0; + + // returns the scale + double normalize(); + + Vector2D operator+(Vector2D a) { + return Vector2D(this->x + a.x, this->y + a.y); + } + Vector2D operator-(Vector2D a) { + return Vector2D(this->x - a.x, this->y - a.y); + } + Vector2D operator*(float a) { + return Vector2D(this->x * a, this->y * a); + } + Vector2D operator/(float a) { + return Vector2D(this->x / a, this->y / a); + } +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..8e8912b --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,32 @@ +/* + +Hypr Window Manager for X. +Started by Vaxry on 2021 / 11 / 17 + +*/ + +#include "windowManager.hpp" + +int main(int argc, char** argv) { + Debug::log(LOG, "Hypr debug log. Built on " + std::string(__DATE__) + " at " + std::string(__TIME__)); + + WindowManager::DisplayConnection = xcb_connect(NULL, NULL); + if (const auto RET = xcb_connection_has_error(WindowManager::DisplayConnection); RET != 0) { + Debug::log(CRIT, "Connection Failed! Return: " + std::to_string(RET)); + return RET; + } + + WindowManager::Screen = xcb_setup_roots_iterator(xcb_get_setup(WindowManager::DisplayConnection)).data; + + WindowManager::setupManager(); + + Debug::log(LOG, "Hypr Started!"); + + while (WindowManager::handleEvent()) { + ; + } + + Debug::log(LOG, "Hypr reached the end! Exiting..."); + + return 0; +} \ No newline at end of file diff --git a/src/utilities/Debug.cpp b/src/utilities/Debug.cpp new file mode 100644 index 0000000..dfb7b23 --- /dev/null +++ b/src/utilities/Debug.cpp @@ -0,0 +1,26 @@ +#include "Debug.hpp" + +void Debug::log(LogLevel level, std::string msg) { + switch (level) + { + case LOG: + msg = "[LOG] " + msg; + break; + + case WARN: + msg = "[WARN] " + msg; + break; + + case ERR: + msg = "[ERR] " + msg; + break; + + case CRIT: + msg = "[CRITICAL] " + msg; + break; + + default: + break; + } + printf((msg + "\n").c_str()); +} \ No newline at end of file diff --git a/src/utilities/Debug.hpp b/src/utilities/Debug.hpp new file mode 100644 index 0000000..4c6b890 --- /dev/null +++ b/src/utilities/Debug.hpp @@ -0,0 +1,14 @@ +#pragma once +#include + +enum LogLevel { + NONE = -1, + LOG = 0, + WARN, + ERR, + CRIT +}; + +namespace Debug { + void log(LogLevel, std::string msg); +}; \ No newline at end of file diff --git a/src/utilities/Keybind.hpp b/src/utilities/Keybind.hpp new file mode 100644 index 0000000..d3bcd7b --- /dev/null +++ b/src/utilities/Keybind.hpp @@ -0,0 +1,9 @@ +#include "../defines.hpp" + +class Keybind { + EXPOSED_MEMBER(Mod, int, i); + EXPOSED_MEMBER(Keysym, xcb_keysym_t,); + EXPOSED_MEMBER(Command, std::string, sz); + EXPOSED_MEMBER(Dispatcher, void*, p); +}; + diff --git a/src/window.cpp b/src/window.cpp new file mode 100644 index 0000000..6462e01 --- /dev/null +++ b/src/window.cpp @@ -0,0 +1,4 @@ +#include "window.hpp" + +CWindow::CWindow() { this->setDirty(true); } +CWindow::~CWindow() { } \ No newline at end of file diff --git a/src/window.hpp b/src/window.hpp new file mode 100644 index 0000000..f881a73 --- /dev/null +++ b/src/window.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "defines.hpp" + +class CWindow { +public: + CWindow(); + ~CWindow(); + + void move(Vector2D dest); + void moveByDelta(Vector2D delta); + + void resize(Vector2D size); + void resize(float percx, float percy); + + std::string getName(); + + // Tells the window manager to reload the window's params + EXPOSED_MEMBER(Dirty, bool, b); + + EXPOSED_MEMBER(Size, Vector2D, vec); + EXPOSED_MEMBER(Position, Vector2D, vec); + EXPOSED_MEMBER(IsFloating, bool, b); + EXPOSED_MEMBER(Drawable, xcb_drawable_t, i); + + +private: + +}; \ No newline at end of file diff --git a/src/windowManager.cpp b/src/windowManager.cpp new file mode 100644 index 0000000..a557697 --- /dev/null +++ b/src/windowManager.cpp @@ -0,0 +1,226 @@ +#include "windowManager.hpp" +#include "./events/events.hpp" + +void WindowManager::setupManager() { + WindowManager::Values[0] = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_change_window_attributes_checked(WindowManager::DisplayConnection, WindowManager::Screen->root, + XCB_CW_EVENT_MASK, WindowManager::Values); + xcb_ungrab_key(WindowManager::DisplayConnection, XCB_GRAB_ANY, WindowManager::Screen->root, XCB_MOD_MASK_ANY); + /*int key_table_size = sizeof(keys) / sizeof(*keys); + for (int i = 0; i < key_table_size; ++i) { + xcb_keycode_t *keycode = xcb_get_keycodes(keys[i].keysym); + if (keycode != NULL) { + xcb_grab_key(WindowManager::DisplayConnection, 1, WindowManager::Screen->root, keys[i].mod, *keycode, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + } + } + xcb_flush(WindowManager::DisplayConnection); + xcb_grab_button(WindowManager::DisplayConnection, 0, WindowManager::Screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC, WindowManager::Screen->root, XCB_NONE, 1, MOD1); + xcb_grab_button(WindowManager::DisplayConnection, 0, WindowManager::Screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC, WindowManager::Screen->root, XCB_NONE, 3, MOD1);*/ + xcb_flush(WindowManager::DisplayConnection); +} + +bool WindowManager::handleEvent() { + if (xcb_connection_has_error(WindowManager::DisplayConnection)) + return false; + + xcb_generic_event_t* ev = xcb_wait_for_event(WindowManager::DisplayConnection); + if (ev != NULL) { + switch (ev->response_type & ~0x80) { + case XCB_ENTER_NOTIFY: + Events::eventEnter(ev); + Debug::log(LOG, "Event dispatched ENTER"); + break; + case XCB_DESTROY_NOTIFY: + Events::eventDestroy(ev); + Debug::log(LOG, "Event dispatched DESTROY"); + break; + case XCB_MAP_REQUEST: + Events::eventMapWindow(ev); + Debug::log(LOG, "Event dispatched MAP"); + break; + + default: + Debug::log(WARN, "Unknown event: " + std::to_string(ev->response_type & ~0x80)); + break; + } + + free(ev); + } + + // refresh and apply the parameters of all dirty windows. + WindowManager::refreshDirtyWindows(); + + xcb_flush(WindowManager::DisplayConnection); + + return true; +} + +void WindowManager::refreshDirtyWindows() { + for(auto& window : windows) { + if (window.getDirty()) { + Values[0] = (int)window.getSize().x; + Values[1] = (int)window.getSize().y; + xcb_configure_window(WindowManager::DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, Values); + + Values[0] = (int)window.getPosition().x; + Values[1] = (int)window.getPosition().y; + xcb_configure_window(WindowManager::DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, Values); + + window.setDirty(false); + + Debug::log(LOG, "Refreshed dirty window."); + } + } +} + +void WindowManager::setFocusedWindow(xcb_drawable_t window) { + if (window && window != WindowManager::Screen->root) { + xcb_set_input_focus(WindowManager::DisplayConnection, XCB_INPUT_FOCUS_POINTER_ROOT, window, XCB_CURRENT_TIME); + WindowManager::LastWindow = window; + } +} + +CWindow* WindowManager::getWindowFromDrawable(xcb_drawable_t window) { + for(auto& w : WindowManager::windows) { + if (w.getDrawable() == window) { + return &w; + } + + Debug::log(LOG, "Not " + std::to_string(w.getDrawable())); + } + return nullptr; +} + +void WindowManager::addWindowToVectorSafe(CWindow window) { + for (auto& w : WindowManager::windows) { + if (w.getDrawable() == window.getDrawable()) + return; // Do not add if already present. + } + WindowManager::windows.push_back(window); +} + +void WindowManager::removeWindowFromVectorSafe(xcb_drawable_t window) { + + if (!window) + return; + + std::vector temp = WindowManager::windows; + + WindowManager::windows.clear(); + + for(auto p : temp) { + if (p.getDrawable() != window) { + WindowManager::windows.push_back(p); + continue; + } + Debug::log(LOG, "Is, removing: " + std::to_string(p.getDrawable())); + } +} + +void calculateNewTileSetOldTile(CWindow* pWindow) { + const auto PLASTWINDOW = WindowManager::getWindowFromDrawable(WindowManager::LastWindow); + if (PLASTWINDOW) { + const auto PLASTSIZE = PLASTWINDOW->getSize(); + const auto PLASTPOS = PLASTWINDOW->getPosition(); + + if (PLASTSIZE.x > PLASTSIZE.y) { + PLASTWINDOW->setSize(Vector2D(PLASTSIZE.x / 2.f, PLASTSIZE.y)); + pWindow->setSize(Vector2D(PLASTSIZE.x / 2.f, PLASTSIZE.y)); + pWindow->setPosition(Vector2D(PLASTPOS.x + PLASTSIZE.x / 2.f, PLASTPOS.y)); + } else { + PLASTWINDOW->setSize(Vector2D(PLASTSIZE.x, PLASTSIZE.y / 2.f)); + pWindow->setSize(Vector2D(PLASTSIZE.x, PLASTSIZE.y / 2.f)); + pWindow->setPosition(Vector2D(PLASTPOS.x, PLASTPOS.y + PLASTSIZE.y / 2.f)); + } + + PLASTWINDOW->setDirty(true); + } else { + // Open a fullscreen window + pWindow->setSize(Vector2D(WindowManager::Screen->width_in_pixels, WindowManager::Screen->height_in_pixels)); + pWindow->setPosition(Vector2D(0, 0)); + } +} + +void WindowManager::calculateNewWindowParams(CWindow* pWindow) { + // And set old one's if needed. + if (!pWindow) + return; + + if (!pWindow->getIsFloating()) { + calculateNewTileSetOldTile(pWindow); + } + + pWindow->setDirty(true); +} + +bool isNeighbor(CWindow* a, CWindow* b) { + const auto POSA = a->getPosition(); + const auto POSB = b->getPosition(); + const auto SIZEA = a->getSize(); + const auto SIZEB = a->getSize(); + + if (POSA.x != 0) { + if (STICKS(POSA.x, (POSB.x + SIZEB.x))) { + return true; + } + } + if (POSA.y != 0) { + if (STICKS(POSA.y, (POSB.y + SIZEB.y))) { + return true; + } + } + + if (POSB.x != 0) { + if (STICKS(POSB.x, (POSA.x + SIZEA.x))) { + return true; + } + } + if (POSB.y != 0) { + if (STICKS(POSB.y, (POSA.y + SIZEA.y))) { + return true; + } + } + + return false; +} + +void eatWindow(CWindow* a, CWindow* toEat) { + + // Pos is min of both. + a->setPosition(Vector2D(std::min(a->getPosition().x, toEat->getPosition().x), std::min(a->getPosition().y, toEat->getPosition().y))); + + // Size is pos + size max - pos + const auto OPPCORNERA = a->getPosition() + a->getSize(); + const auto OPPCORNERB = toEat->getPosition() + toEat->getSize(); + + a->setSize(Vector2D(std::max(OPPCORNERA.x, OPPCORNERB.x), std::max(OPPCORNERA.y, OPPCORNERB.y)) - a->getPosition()); +} + +void WindowManager::fixWindowOnClose(CWindow* pClosedWindow) { + if (!pClosedWindow) + return; + + // get the first neighboring window + CWindow* neighbor = nullptr; + for(auto& w : WindowManager::windows) { + if (w.getDrawable() == pClosedWindow->getDrawable()) + continue; + + if (isNeighbor(&w, pClosedWindow)) { + neighbor = &w; + break; + } + } + + if (!neighbor) + return; // No neighbor. Don't update, easy. + + + // update neighbor to "eat" closed. + eatWindow(neighbor, pClosedWindow); + + neighbor->setDirty(true); +} \ No newline at end of file diff --git a/src/windowManager.hpp b/src/windowManager.hpp new file mode 100644 index 0000000..042ed27 --- /dev/null +++ b/src/windowManager.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "defines.hpp" +#include "window.hpp" + +#include + +namespace WindowManager { + inline xcb_connection_t* DisplayConnection; + inline xcb_screen_t* Screen; + inline xcb_drawable_t Drawable; + inline uint32_t Values[3]; + + inline std::vector windows; // windows never left. It has always been hiding amongst us. + inline xcb_drawable_t LastWindow = -1; + + CWindow* getWindowFromDrawable(xcb_drawable_t); + void addWindowToVectorSafe(CWindow); + void removeWindowFromVectorSafe(xcb_drawable_t); + + void setupManager(); + bool handleEvent(); + void refreshDirtyWindows(); + + void setFocusedWindow(xcb_drawable_t); + + void calculateNewWindowParams(CWindow*); + void fixWindowOnClose(CWindow*); +}; \ No newline at end of file