2021-11-18 18:04:09 +01:00
|
|
|
#include "windowManager.hpp"
|
|
|
|
#include "./events/events.hpp"
|
|
|
|
|
2021-11-21 11:25:26 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::setupManager() {
|
2021-11-21 12:40:03 +01:00
|
|
|
ConfigManager::init();
|
2021-11-19 20:20:05 +01:00
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
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(DisplayConnection, Screen->root,
|
|
|
|
XCB_CW_EVENT_MASK, Values);
|
|
|
|
xcb_ungrab_key(DisplayConnection, XCB_GRAB_ANY, Screen->root, XCB_MOD_MASK_ANY);
|
2021-11-19 20:20:05 +01:00
|
|
|
|
|
|
|
for (auto& keybind : KeybindManager::keybinds) {
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_grab_key(DisplayConnection, 1, Screen->root,
|
2021-11-19 20:20:05 +01:00
|
|
|
KeybindManager::modToMask(keybind.getMod()), KeybindManager::getKeycodeFromKeysym(keybind.getKeysym()),
|
|
|
|
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
|
2021-11-18 18:04:09 +01:00
|
|
|
}
|
2021-11-19 20:20:05 +01:00
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_flush(DisplayConnection);
|
2021-11-19 20:20:05 +01:00
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_grab_button(DisplayConnection, 0,
|
|
|
|
Screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE,
|
|
|
|
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, Screen->root, XCB_NONE,
|
2021-11-19 20:20:05 +01:00
|
|
|
1, KeybindManager::modToMask(MOD_SUPER));
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_grab_button(DisplayConnection, 0,
|
|
|
|
Screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE,
|
|
|
|
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, Screen->root, XCB_NONE,
|
2021-11-19 20:20:05 +01:00
|
|
|
3, KeybindManager::modToMask(MOD_SUPER));
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_flush(DisplayConnection);
|
2021-11-20 10:04:14 +01:00
|
|
|
|
|
|
|
// Add a workspace to the monitor
|
|
|
|
CWorkspace protoWorkspace;
|
|
|
|
protoWorkspace.setID(1);
|
|
|
|
workspaces.push_back(protoWorkspace);
|
|
|
|
activeWorkspace = &workspaces[0];
|
|
|
|
//
|
2021-11-21 11:25:26 +01:00
|
|
|
|
|
|
|
// 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 ---- //
|
|
|
|
|
2021-11-21 12:40:03 +01:00
|
|
|
statusBar.setup(Vector2D(0, 0), Vector2D(Screen->width_in_pixels, ConfigManager::getInt("bar_height")));
|
2021-11-21 11:25:26 +01:00
|
|
|
|
|
|
|
// start its' update thread
|
|
|
|
Events::setThread();
|
2021-11-18 18:04:09 +01:00
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
bool CWindowManager::handleEvent() {
|
|
|
|
if (xcb_connection_has_error(DisplayConnection))
|
2021-11-18 18:04:09 +01:00
|
|
|
return false;
|
|
|
|
|
2021-11-21 11:25:26 +01:00
|
|
|
xcb_flush(DisplayConnection);
|
2021-11-20 09:25:21 +01:00
|
|
|
const auto ev = xcb_wait_for_event(DisplayConnection);
|
2021-11-18 18:04:09 +01:00
|
|
|
if (ev != NULL) {
|
|
|
|
switch (ev->response_type & ~0x80) {
|
|
|
|
case XCB_ENTER_NOTIFY:
|
|
|
|
Events::eventEnter(ev);
|
|
|
|
Debug::log(LOG, "Event dispatched ENTER");
|
|
|
|
break;
|
2021-11-18 22:08:28 +01:00
|
|
|
case XCB_LEAVE_NOTIFY:
|
|
|
|
Events::eventLeave(ev);
|
|
|
|
Debug::log(LOG, "Event dispatched LEAVE");
|
|
|
|
break;
|
2021-11-18 18:04:09 +01:00
|
|
|
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;
|
2021-11-19 20:20:05 +01:00
|
|
|
case XCB_BUTTON_PRESS:
|
2021-11-21 16:26:50 +01:00
|
|
|
Events::eventKeyPress(ev);
|
2021-11-21 15:25:57 +01:00
|
|
|
Debug::log(LOG, "Event dispatched BUTTON_PRESS");
|
2021-11-19 20:20:05 +01:00
|
|
|
break;
|
2021-11-21 11:25:26 +01:00
|
|
|
case XCB_EXPOSE:
|
|
|
|
Events::eventExpose(ev);
|
|
|
|
Debug::log(LOG, "Event dispatched EXPOSE");
|
|
|
|
break;
|
2021-11-21 15:25:57 +01:00
|
|
|
case XCB_KEY_PRESS:
|
2021-11-21 16:26:50 +01:00
|
|
|
Events::eventButtonPress(ev);
|
2021-11-21 15:25:57 +01:00
|
|
|
Debug::log(LOG, "Event dispatched KEY_PRESS");
|
|
|
|
break;
|
2021-11-18 18:04:09 +01:00
|
|
|
|
|
|
|
default:
|
2021-11-19 23:08:59 +01:00
|
|
|
//Debug::log(WARN, "Unknown event: " + std::to_string(ev->response_type & ~0x80));
|
2021-11-18 18:04:09 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
// refresh and apply the parameters of all dirty windows.
|
2021-11-20 09:25:21 +01:00
|
|
|
refreshDirtyWindows();
|
2021-11-18 18:04:09 +01:00
|
|
|
|
2021-11-20 10:04:14 +01:00
|
|
|
// remove unused workspaces
|
|
|
|
cleanupUnusedWorkspaces();
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_flush(DisplayConnection);
|
2021-11-18 18:04:09 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-20 10:04:14 +01:00
|
|
|
void CWindowManager::cleanupUnusedWorkspaces() {
|
|
|
|
std::vector<CWorkspace> temp = workspaces;
|
|
|
|
|
|
|
|
workspaces.clear();
|
|
|
|
|
|
|
|
for (auto& work : temp) {
|
|
|
|
if (work.getID() != activeWorkspace->getID()) {
|
|
|
|
// check if it has any children
|
|
|
|
bool hasChildren = false;
|
|
|
|
for (auto& window : windows) {
|
|
|
|
if (window.getWorkspaceID() == work.getID()) {
|
|
|
|
hasChildren = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasChildren) {
|
|
|
|
// Has windows opened on it.
|
|
|
|
workspaces.push_back(work);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Foreground workspace
|
|
|
|
workspaces.push_back(work);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::refreshDirtyWindows() {
|
2021-11-18 18:04:09 +01:00
|
|
|
for(auto& window : windows) {
|
|
|
|
if (window.getDirty()) {
|
2021-11-20 10:04:14 +01:00
|
|
|
|
2021-11-21 12:40:03 +01:00
|
|
|
setEffectiveSizePosUsingConfig(&window);
|
|
|
|
|
2021-11-21 11:42:44 +01:00
|
|
|
// Fullscreen flag
|
|
|
|
bool bHasFullscreenWindow = activeWorkspace->getHasFullscreenWindow();
|
|
|
|
|
2021-11-20 10:04:14 +01:00
|
|
|
// first and foremost, let's check if the window isn't on a different workspace
|
2021-11-21 11:42:44 +01:00
|
|
|
// or that it is not a non-fullscreen window in a fullscreen workspace
|
|
|
|
if ((window.getWorkspaceID() != activeWorkspace->getID()) || (bHasFullscreenWindow && !window.getFullscreen())) {
|
2021-11-20 10:04:14 +01:00
|
|
|
// Move it to hades
|
|
|
|
Values[0] = (int)1500000; // hmu when monitors actually have that many pixels
|
|
|
|
Values[1] = (int)1500000; // and we are still using xorg =)
|
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, Values);
|
|
|
|
|
|
|
|
// Set the size JIC.
|
|
|
|
Values[0] = (int)window.getEffectiveSize().x;
|
|
|
|
Values[1] = (int)window.getEffectiveSize().y;
|
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, Values);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2021-11-19 22:29:44 +01:00
|
|
|
|
2021-11-21 11:42:44 +01:00
|
|
|
// Fullscreen window. No border, all screen.
|
|
|
|
if (window.getFullscreen()) {
|
|
|
|
Values[0] = 0;
|
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_BORDER_WIDTH, Values);
|
|
|
|
|
|
|
|
Values[0] = 0x555555; // GRAY :)
|
|
|
|
xcb_change_window_attributes(DisplayConnection, window.getDrawable(), XCB_CW_BORDER_PIXEL, Values);
|
|
|
|
|
|
|
|
Values[0] = (int)Screen->width_in_pixels;
|
|
|
|
Values[1] = (int)Screen->height_in_pixels;
|
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, Values);
|
|
|
|
|
|
|
|
Values[0] = (int)0;
|
|
|
|
Values[1] = (int)0;
|
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, Values);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-11-18 22:08:28 +01:00
|
|
|
Values[0] = (int)window.getEffectiveSize().x;
|
|
|
|
Values[1] = (int)window.getEffectiveSize().y;
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, Values);
|
2021-11-18 18:04:09 +01:00
|
|
|
|
2021-11-18 22:08:28 +01:00
|
|
|
Values[0] = (int)window.getEffectivePosition().x;
|
|
|
|
Values[1] = (int)window.getEffectivePosition().y;
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, Values);
|
2021-11-18 18:04:09 +01:00
|
|
|
|
2021-11-18 22:08:28 +01:00
|
|
|
// Focused special border.
|
2021-11-20 09:25:21 +01:00
|
|
|
if (window.getDrawable() == LastWindow) {
|
2021-11-21 12:40:03 +01:00
|
|
|
Values[0] = (int)ConfigManager::getInt("border_size");
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_BORDER_WIDTH, Values);
|
2021-11-18 22:08:28 +01:00
|
|
|
|
|
|
|
// Update the position because the border makes the window jump
|
|
|
|
// I have added the bordersize vec2d before in the setEffectiveSizePosUsingConfig function.
|
2021-11-21 12:40:03 +01:00
|
|
|
Values[0] = (int)window.getEffectivePosition().x - ConfigManager::getInt("border_size");
|
|
|
|
Values[1] = (int)window.getEffectivePosition().y - ConfigManager::getInt("border_size");
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, Values);
|
2021-11-18 22:08:28 +01:00
|
|
|
|
|
|
|
Values[0] = 0xFF3333; // RED :)
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_change_window_attributes(DisplayConnection, window.getDrawable(), XCB_CW_BORDER_PIXEL, Values);
|
2021-11-18 22:08:28 +01:00
|
|
|
} else {
|
|
|
|
Values[0] = 0;
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_configure_window(DisplayConnection, window.getDrawable(), XCB_CONFIG_WINDOW_BORDER_WIDTH, Values);
|
2021-11-18 22:08:28 +01:00
|
|
|
|
|
|
|
Values[0] = 0x555555; // GRAY :)
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_change_window_attributes(DisplayConnection, window.getDrawable(), XCB_CW_BORDER_PIXEL, Values);
|
2021-11-18 22:08:28 +01:00
|
|
|
}
|
|
|
|
|
2021-11-18 18:04:09 +01:00
|
|
|
window.setDirty(false);
|
|
|
|
|
2021-11-19 22:29:44 +01:00
|
|
|
Debug::log(LOG, "Refreshed dirty window, with an ID of " + std::to_string(window.getDrawable()));
|
2021-11-18 18:04:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::setFocusedWindow(xcb_drawable_t window) {
|
|
|
|
if (window && window != Screen->root) {
|
|
|
|
xcb_set_input_focus(DisplayConnection, XCB_INPUT_FOCUS_POINTER_ROOT, window, XCB_CURRENT_TIME);
|
2021-11-18 22:08:28 +01:00
|
|
|
|
|
|
|
// Fix border from the old window that was in focus.
|
2021-11-20 09:25:21 +01:00
|
|
|
if (const auto PLASTWINDOW = getWindowFromDrawable(LastWindow); PLASTWINDOW)
|
2021-11-18 22:08:28 +01:00
|
|
|
PLASTWINDOW->setDirty(true);
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
LastWindow = window;
|
2021-11-18 18:04:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
CWindow* CWindowManager::getWindowFromDrawable(xcb_drawable_t window) {
|
|
|
|
for(auto& w : windows) {
|
2021-11-18 18:04:09 +01:00
|
|
|
if (w.getDrawable() == window) {
|
|
|
|
return &w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::addWindowToVectorSafe(CWindow window) {
|
|
|
|
for (auto& w : windows) {
|
2021-11-18 18:04:09 +01:00
|
|
|
if (w.getDrawable() == window.getDrawable())
|
|
|
|
return; // Do not add if already present.
|
|
|
|
}
|
2021-11-20 09:25:21 +01:00
|
|
|
windows.push_back(window);
|
2021-11-18 18:04:09 +01:00
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::removeWindowFromVectorSafe(xcb_drawable_t window) {
|
2021-11-18 18:04:09 +01:00
|
|
|
|
|
|
|
if (!window)
|
|
|
|
return;
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
std::vector<CWindow> temp = windows;
|
2021-11-18 18:04:09 +01:00
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
windows.clear();
|
2021-11-18 18:04:09 +01:00
|
|
|
|
|
|
|
for(auto p : temp) {
|
|
|
|
if (p.getDrawable() != window) {
|
2021-11-20 09:25:21 +01:00
|
|
|
windows.push_back(p);
|
2021-11-18 18:04:09 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::setEffectiveSizePosUsingConfig(CWindow* pWindow) {
|
2021-11-18 22:08:28 +01:00
|
|
|
|
|
|
|
if (!pWindow)
|
|
|
|
return;
|
|
|
|
|
2021-11-19 19:04:42 +01:00
|
|
|
// set some flags.
|
|
|
|
const bool DISPLAYLEFT = pWindow->getPosition().x == 0;
|
2021-11-20 09:25:21 +01:00
|
|
|
const bool DISPLAYRIGHT = pWindow->getPosition().x + pWindow->getSize().x == Screen->width_in_pixels;
|
2021-11-19 19:04:42 +01:00
|
|
|
const bool DISPLAYTOP = pWindow->getPosition().y == 0;
|
2021-11-20 09:25:21 +01:00
|
|
|
const bool DISPLAYBOTTOM = pWindow->getPosition().y + pWindow->getSize().y == Screen->height_in_pixels;
|
2021-11-19 19:04:42 +01:00
|
|
|
|
2021-11-21 12:40:03 +01:00
|
|
|
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));
|
2021-11-19 19:04:42 +01:00
|
|
|
|
|
|
|
// do gaps, set top left
|
2021-11-21 12:40:03 +01:00
|
|
|
pWindow->setEffectivePosition(pWindow->getEffectivePosition() + Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + ConfigManager::getInt("bar_height") : ConfigManager::getInt("gaps_in")));
|
2021-11-19 19:04:42 +01:00
|
|
|
// fix to old size bottom right
|
2021-11-21 12:40:03 +01:00
|
|
|
pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYLEFT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYTOP ? ConfigManager::getInt("gaps_out") + ConfigManager::getInt("bar_height") : ConfigManager::getInt("gaps_in")));
|
2021-11-19 19:04:42 +01:00
|
|
|
// set bottom right
|
2021-11-21 12:40:03 +01:00
|
|
|
pWindow->setEffectiveSize(pWindow->getEffectiveSize() - Vector2D(DISPLAYRIGHT ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in"), DISPLAYBOTTOM ? ConfigManager::getInt("gaps_out") : ConfigManager::getInt("gaps_in")));
|
2021-11-18 22:08:28 +01:00
|
|
|
}
|
|
|
|
|
2021-11-21 15:15:33 +01:00
|
|
|
CWindow* CWindowManager::findWindowAtCursor() {
|
|
|
|
const auto POINTERCOOKIE = xcb_query_pointer(DisplayConnection, Screen->root);
|
|
|
|
|
|
|
|
xcb_query_pointer_reply_t* pointerreply = xcb_query_pointer_reply(DisplayConnection, POINTERCOOKIE, NULL);
|
|
|
|
if (!pointerreply) {
|
|
|
|
Debug::log(ERR, "Couldn't query pointer.");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector2D cursorPos = Vector2D(pointerreply->root_x, pointerreply->root_y);
|
|
|
|
|
|
|
|
free(pointerreply);
|
|
|
|
|
|
|
|
for (auto& window : windows) {
|
|
|
|
if (window.getWorkspaceID() == activeWorkspace->getID() && !window.getIsFloating()) {
|
|
|
|
|
|
|
|
if (cursorPos.x >= window.getPosition().x
|
|
|
|
&& cursorPos.x <= window.getPosition().x + window.getSize().x
|
|
|
|
&& cursorPos.y >= window.getPosition().y
|
|
|
|
&& cursorPos.y <= window.getPosition().y + window.getSize().y) {
|
|
|
|
|
|
|
|
return &window;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::calculateNewTileSetOldTile(CWindow* pWindow) {
|
2021-11-21 15:15:33 +01:00
|
|
|
auto PLASTWINDOW = getWindowFromDrawable(LastWindow);
|
|
|
|
|
|
|
|
if (PLASTWINDOW && PLASTWINDOW->getIsFloating()) {
|
|
|
|
// find a window manually
|
|
|
|
PLASTWINDOW = findWindowAtCursor();
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:04:09 +01:00
|
|
|
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
|
2021-11-20 09:25:21 +01:00
|
|
|
pWindow->setSize(Vector2D(Screen->width_in_pixels, Screen->height_in_pixels));
|
2021-11-18 18:04:09 +01:00
|
|
|
pWindow->setPosition(Vector2D(0, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-21 15:15:33 +01:00
|
|
|
void CWindowManager::calculateNewFloatingWindow(CWindow* pWindow) {
|
|
|
|
if (!pWindow)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pWindow->setPosition(pWindow->getDefaultPosition());
|
|
|
|
pWindow->setSize(pWindow->getDefaultSize());
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::calculateNewWindowParams(CWindow* pWindow) {
|
2021-11-18 18:04:09 +01:00
|
|
|
// And set old one's if needed.
|
|
|
|
if (!pWindow)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!pWindow->getIsFloating()) {
|
|
|
|
calculateNewTileSetOldTile(pWindow);
|
2021-11-21 15:15:33 +01:00
|
|
|
} else {
|
|
|
|
calculateNewFloatingWindow(pWindow);
|
2021-11-18 18:04:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pWindow->setDirty(true);
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
bool CWindowManager::isNeighbor(CWindow* a, CWindow* b) {
|
2021-11-20 10:04:14 +01:00
|
|
|
|
|
|
|
if (a->getWorkspaceID() != b->getWorkspaceID())
|
|
|
|
return false; // Different workspaces
|
|
|
|
|
2021-11-18 18:04:09 +01:00
|
|
|
const auto POSA = a->getPosition();
|
|
|
|
const auto POSB = b->getPosition();
|
|
|
|
const auto SIZEA = a->getSize();
|
2021-11-18 22:26:29 +01:00
|
|
|
const auto SIZEB = b->getSize();
|
2021-11-18 18:04:09 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
bool CWindowManager::canEatWindow(CWindow* a, CWindow* toEat) {
|
2021-11-19 19:04:42 +01:00
|
|
|
// Pos is min of both.
|
|
|
|
const auto POSAFTEREAT = 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 = Vector2D(POSAFTEREAT) + a->getSize();
|
|
|
|
const auto OPPCORNERB = toEat->getPosition() + toEat->getSize();
|
|
|
|
|
|
|
|
const auto SIZEAFTEREAT = Vector2D(std::max(OPPCORNERA.x, OPPCORNERB.x), std::max(OPPCORNERA.y, OPPCORNERB.y)) - POSAFTEREAT;
|
|
|
|
|
|
|
|
const auto doOverlap = [&](CWindow* b) {
|
|
|
|
const auto RIGHT1 = Vector2D(POSAFTEREAT.x + SIZEAFTEREAT.x, POSAFTEREAT.y + SIZEAFTEREAT.y);
|
|
|
|
const auto RIGHT2 = b->getPosition() + b->getSize();
|
|
|
|
const auto LEFT1 = POSAFTEREAT;
|
|
|
|
const auto LEFT2 = b->getPosition();
|
|
|
|
|
|
|
|
return !(LEFT1.x >= RIGHT2.x || LEFT2.x >= RIGHT1.x || LEFT1.y >= RIGHT2.y || LEFT2.y >= RIGHT1.y);
|
|
|
|
};
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
for (auto& w : windows) {
|
2021-11-21 15:15:33 +01:00
|
|
|
if (w.getDrawable() == a->getDrawable() || w.getDrawable() == toEat->getDrawable() || w.getWorkspaceID() != toEat->getWorkspaceID() || w.getIsFloating())
|
2021-11-19 19:04:42 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (doOverlap(&w))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::eatWindow(CWindow* a, CWindow* toEat) {
|
2021-11-19 22:29:44 +01:00
|
|
|
|
2021-11-18 18:04:09 +01:00
|
|
|
// Size is pos + size max - pos
|
|
|
|
const auto OPPCORNERA = a->getPosition() + a->getSize();
|
|
|
|
const auto OPPCORNERB = toEat->getPosition() + toEat->getSize();
|
|
|
|
|
2021-11-19 20:39:43 +01:00
|
|
|
// Pos is min of both.
|
|
|
|
a->setPosition(Vector2D(std::min(a->getPosition().x, toEat->getPosition().x), std::min(a->getPosition().y, toEat->getPosition().y)));
|
|
|
|
|
2021-11-18 18:04:09 +01:00
|
|
|
a->setSize(Vector2D(std::max(OPPCORNERA.x, OPPCORNERB.x), std::max(OPPCORNERA.y, OPPCORNERB.y)) - a->getPosition());
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::fixWindowOnClose(CWindow* pClosedWindow) {
|
2021-11-18 18:04:09 +01:00
|
|
|
if (!pClosedWindow)
|
|
|
|
return;
|
|
|
|
|
2021-11-21 11:42:44 +01:00
|
|
|
// Fix if was fullscreen
|
|
|
|
if (pClosedWindow->getFullscreen())
|
|
|
|
activeWorkspace->setHasFullscreenWindow(false);
|
|
|
|
|
2021-11-18 18:04:09 +01:00
|
|
|
// get the first neighboring window
|
|
|
|
CWindow* neighbor = nullptr;
|
2021-11-20 09:25:21 +01:00
|
|
|
for(auto& w : windows) {
|
2021-11-18 18:04:09 +01:00
|
|
|
if (w.getDrawable() == pClosedWindow->getDrawable())
|
|
|
|
continue;
|
|
|
|
|
2021-11-19 19:04:42 +01:00
|
|
|
if (isNeighbor(&w, pClosedWindow) && canEatWindow(&w, pClosedWindow)) {
|
2021-11-18 18:04:09 +01:00
|
|
|
neighbor = &w;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!neighbor)
|
|
|
|
return; // No neighbor. Don't update, easy.
|
|
|
|
|
|
|
|
// update neighbor to "eat" closed.
|
|
|
|
eatWindow(neighbor, pClosedWindow);
|
|
|
|
|
|
|
|
neighbor->setDirty(true);
|
2021-11-20 09:25:21 +01:00
|
|
|
setFocusedWindow(neighbor->getDrawable()); // Set focus. :)
|
2021-11-19 22:29:44 +01:00
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
CWindow* CWindowManager::getNeighborInDir(char dir) {
|
2021-11-19 22:29:44 +01:00
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
const auto CURRENTWINDOW = getWindowFromDrawable(LastWindow);
|
2021-11-19 22:29:44 +01:00
|
|
|
|
|
|
|
if (!CURRENTWINDOW)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
const auto POSA = CURRENTWINDOW->getPosition();
|
|
|
|
const auto SIZEA = CURRENTWINDOW->getSize();
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
for (auto& w : windows) {
|
2021-11-20 10:04:14 +01:00
|
|
|
if (w.getDrawable() == CURRENTWINDOW->getDrawable() || w.getWorkspaceID() != CURRENTWINDOW->getWorkspaceID())
|
2021-11-19 22:29:44 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
const auto POSB = w.getPosition();
|
|
|
|
const auto SIZEB = w.getSize();
|
|
|
|
|
|
|
|
switch (dir) {
|
|
|
|
case 'l':
|
|
|
|
if (STICKS(POSA.x, POSB.x + SIZEB.x))
|
|
|
|
return &w;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
if (STICKS(POSA.x + SIZEA.x, POSB.x))
|
|
|
|
return &w;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
if (STICKS(POSA.y, POSB.y + SIZEB.y))
|
|
|
|
return &w;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
if (STICKS(POSA.y + SIZEA.y, POSB.y))
|
|
|
|
return &w;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-11-19 23:08:59 +01:00
|
|
|
// I don't know if this works, it might be an issue with my nested Xorg session I am using rn to test this.
|
|
|
|
// Will check later.
|
|
|
|
// TODO:
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::warpCursorTo(Vector2D to) {
|
|
|
|
const auto POINTERCOOKIE = xcb_query_pointer(DisplayConnection, Screen->root);
|
2021-11-19 23:08:59 +01:00
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_query_pointer_reply_t* pointerreply = xcb_query_pointer_reply(DisplayConnection, POINTERCOOKIE, NULL);
|
2021-11-19 23:08:59 +01:00
|
|
|
if (!pointerreply) {
|
|
|
|
Debug::log(ERR, "Couldn't query pointer.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
xcb_warp_pointer(DisplayConnection, XCB_NONE, Screen->root, 0, 0, 0, 0, (int)to.x, (int)to.y);
|
2021-11-19 23:08:59 +01:00
|
|
|
free(pointerreply);
|
|
|
|
}
|
|
|
|
|
2021-11-20 09:25:21 +01:00
|
|
|
void CWindowManager::moveActiveWindowTo(char dir) {
|
|
|
|
const auto CURRENTWINDOW = getWindowFromDrawable(LastWindow);
|
2021-11-19 22:29:44 +01:00
|
|
|
|
|
|
|
if (!CURRENTWINDOW)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const auto neighbor = getNeighborInDir(dir);
|
|
|
|
|
|
|
|
if (!neighbor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// swap their stuff and mark dirty
|
|
|
|
const auto TEMP_SIZEA = CURRENTWINDOW->getSize();
|
|
|
|
const auto TEMP_POSA = CURRENTWINDOW->getPosition();
|
|
|
|
|
|
|
|
CURRENTWINDOW->setSize(neighbor->getSize());
|
|
|
|
CURRENTWINDOW->setPosition(neighbor->getPosition());
|
|
|
|
|
|
|
|
neighbor->setSize(TEMP_SIZEA);
|
|
|
|
neighbor->setPosition(TEMP_POSA);
|
|
|
|
|
|
|
|
CURRENTWINDOW->setDirty(true);
|
|
|
|
neighbor->setDirty(true);
|
|
|
|
|
2021-11-19 23:08:59 +01:00
|
|
|
// finish by moving the cursor to the current window
|
2021-11-20 09:25:21 +01:00
|
|
|
warpCursorTo(CURRENTWINDOW->getPosition() + CURRENTWINDOW->getSize() / 2.f);
|
2021-11-20 10:04:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CWindowManager::changeWorkspaceByID(int ID) {
|
|
|
|
for (auto& workspace : workspaces) {
|
|
|
|
if (workspace.getID() == ID) {
|
|
|
|
activeWorkspace = &workspace;
|
|
|
|
LastWindow = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we are here it means the workspace is new. Let's create it.
|
|
|
|
CWorkspace newWorkspace;
|
|
|
|
newWorkspace.setID(ID);
|
|
|
|
workspaces.push_back(newWorkspace);
|
|
|
|
activeWorkspace = &workspaces[workspaces.size() - 1];
|
|
|
|
LastWindow = -1;
|
|
|
|
}
|
|
|
|
|
2021-11-21 12:40:03 +01:00
|
|
|
void CWindowManager::setAllWindowsDirty() {
|
|
|
|
for (auto& window : windows) {
|
|
|
|
window.setDirty(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 10:04:14 +01:00
|
|
|
void CWindowManager::setAllWorkspaceWindowsDirtyByID(int ID) {
|
|
|
|
int workspaceID = -1;
|
|
|
|
for (auto& workspace : workspaces) {
|
|
|
|
if (workspace.getID() == ID) {
|
|
|
|
workspaceID = workspace.getID();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (workspaceID == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (auto& window : windows) {
|
|
|
|
if (window.getWorkspaceID() == workspaceID)
|
|
|
|
window.setDirty(true);
|
|
|
|
}
|
2021-11-21 11:25:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-11-18 18:04:09 +01:00
|
|
|
}
|