Skip to content

Commit

Permalink
Merge pull request #4 from levnikmyskin/fix/vdesk_recalc_on_monitor_u…
Browse files Browse the repository at this point in the history
…pdate

Fix Vdesk recomputation on monitor layout change. Fixes #3
  • Loading branch information
levnikmyskin authored Dec 1, 2023
2 parents 5448857 + f40b96f commit c7d7a6f
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 116 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PLUGIN_NAME=virtual-desktops

SOURCE_FILES=$(wildcard src/*.cpp)

COMPILE_FLAGS=-g -fPIC --no-gnu-unique -std=c++23
COMPILE_FLAGS=-g -fPIC --no-gnu-unique -std=c++23 -Wall
COMPILE_FLAGS+=-I "/usr/include/pixman-1"
COMPILE_FLAGS+=-I "/usr/include/libdrm"
COMPILE_FLAGS+=-I "${HYPRLAND_HEADERS}"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
- [Install](#install)
- [Help, Hyprland is being weird!](#help-hyprland-is-being-weird)
- [It's actually the plugin 😱](#its-actually-the-plugin-)
- [Thanks to](#thanks-to)
- [Thanks to](#thanks-to)


## What is this exactly?
Expand Down Expand Up @@ -186,6 +186,6 @@ I've noticed that, sometimes, when disconnecting or reconnecting monitors, there
If instead you're seeing weird behaviour with the plugin itself, remember you can always run:
`hyprtl dispatch vdeskreset`

### Thanks to
## Thanks to
[split-workspaces](https://github.com/Duckonaut/split-monitor-workspaces/), from which I borrowed the Makefile,
and the general idea of how to write Hyprland plugins.
46 changes: 25 additions & 21 deletions include/VirtualDesk.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#ifndef VDESK_H
#define VDESK_H

#include <string>
Expand All @@ -9,10 +10,11 @@
#include <src/helpers/Monitor.hpp>
#include "globals.hpp"
#include "utils.hpp"
#include <src/Compositor.hpp>

typedef std::unordered_map<int, int> WorkspaceMap;
typedef std::unordered_map<std::string, int> Layout;
typedef std::string MonitorName;
typedef std::unordered_map<int, int> WorkspaceMap;
typedef std::unordered_map<const CMonitor*, int> Layout;
typedef std::string MonitorName;

/*
* Each virtual desk holds a list of layouts. Layouts remember which workspace was on which monitor
Expand All @@ -23,24 +25,26 @@ typedef std::string MonitorName;
class VirtualDesk {
public:
VirtualDesk(int id = 1, std::string name = "1");
int id;
std::string name;
std::vector<Layout> layouts;

const Layout& activeLayout(const RememberLayoutConf&);
Layout& searchActiveLayout(const RememberLayoutConf&);
std::unordered_set<std::string> setFromMonitors(const std::vector<std::shared_ptr<CMonitor>>&);
void changeWorkspaceOnMonitor(int, CMonitor*);
void invalidateActiveLayout();
void resetLayout();
void deleteInvalidMonitor(CMonitor*);
void deleteInvalidMonitorOnAllLayouts(CMonitor*);
int id;
std::string name;
std::vector<Layout> layouts;

const Layout& activeLayout(const RememberLayoutConf&, const CMonitor* exclude = nullptr);
Layout& searchActiveLayout(const RememberLayoutConf&, const CMonitor* exclude = nullptr);
std::unordered_set<std::string> setFromMonitors(const std::vector<std::shared_ptr<CMonitor>>&);
void changeWorkspaceOnMonitor(int, CMonitor*);
void invalidateActiveLayout();
void resetLayout();
CMonitor* deleteInvalidMonitor(const CMonitor*);
void deleteInvalidMonitorsOnActiveLayout();
void deleteInvalidMonitorOnAllLayouts(const CMonitor*);
static std::shared_ptr<CMonitor> firstAvailableMonitor(const std::vector<std::shared_ptr<CMonitor>>&);

private:
int m_activeLayout_idx;
bool activeIsValid = false;
Layout generateCurrentMonitorLayout();
std::vector<std::shared_ptr<CMonitor>> currentlyEnabledMonitors();
static std::string monitorDesc(const CMonitor&);
void checkAndAdaptLayout(Layout*);
int m_activeLayout_idx;
bool activeIsValid = false;
Layout generateCurrentMonitorLayout();
static std::string monitorDesc(const CMonitor*);
void checkAndAdaptLayout(Layout*, const CMonitor* exclude = nullptr);
};
#endif
5 changes: 4 additions & 1 deletion include/VirtualDeskManager.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#pragma once

#ifndef VDESK_MANAGER_H
#define VDESK_MANAGER_H

#include "VirtualDesk.hpp"
Expand All @@ -24,7 +25,8 @@ class VirtualDeskManager {
void invalidateAllLayouts();
void resetAllVdesks();
void resetVdesk(const std::string& arg);
void deleteInvalidMonitorsOnAllVdesks(CMonitor*);
void deleteInvalidMonitorsOnAllVdesks(const CMonitor*);
void deleteInvalidMonitorsOnAllVdesks();

private:
int m_activeDeskKey = 1;
Expand All @@ -33,3 +35,4 @@ class VirtualDeskManager {
int getDeskIdFromName(const std::string& name, bool createIfNotFound = true);
CMonitor* getCurrentMonitor();
};
#endif
14 changes: 9 additions & 5 deletions include/utils.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#pragma once

#ifndef UTILS_H
#define UTILS_H

#include "src/debug/Log.hpp"
#include "globals.hpp"
#include "src/config/ConfigManager.hpp"
#include <string>
#include "src/Compositor.hpp"

const std::string VIRTUALDESK_NAMES_CONF = "plugin:virtual-desktops:names";
const std::string CYCLEWORKSPACES_CONF = "plugin:virtual-desktops:cycleworkspaces";
Expand All @@ -32,10 +34,12 @@ enum RememberLayoutConf {
monitors = 2
};

RememberLayoutConf layoutConfFromInt(const int64_t);
RememberLayoutConf layoutConfFromString(const std::string& conf);
void printLog(std::string s, LogLevel level = INFO);
RememberLayoutConf layoutConfFromInt(const int64_t);
RememberLayoutConf layoutConfFromString(const std::string& conf);
void printLog(std::string s, LogLevel level = INFO);

std::string parseMoveDispatch(std::string& arg);
std::string parseMoveDispatch(std::string& arg);
std::vector<std::shared_ptr<CMonitor>> currentlyEnabledMonitors(const CMonitor* exclude = nullptr);

bool isVerbose();
bool isVerbose();
#endif
113 changes: 67 additions & 46 deletions src/VirtualDesk.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "VirtualDesk.hpp"
#include <src/Compositor.hpp>
#include <numeric>
#include <algorithm>
#include <unordered_set>
#include <format>

VirtualDesk::VirtualDesk(int id, std::string name) {
this->id = id;
Expand All @@ -10,17 +11,17 @@ VirtualDesk::VirtualDesk(int id, std::string name) {
m_activeLayout_idx = 0;
}

const Layout& VirtualDesk::activeLayout(const RememberLayoutConf& conf) {
const Layout& VirtualDesk::activeLayout(const RememberLayoutConf& conf, const CMonitor* exclude) {
if (!activeIsValid) {
activeIsValid = true;
searchActiveLayout(conf);
searchActiveLayout(conf, exclude);
}
return layouts[m_activeLayout_idx];
}

Layout& VirtualDesk::searchActiveLayout(const RememberLayoutConf& conf) {
Layout& VirtualDesk::searchActiveLayout(const RememberLayoutConf& conf, const CMonitor* exclude) {

auto monitors = currentlyEnabledMonitors();
auto monitors = currentlyEnabledMonitors(exclude);
switch (conf) {
case RememberLayoutConf::monitors: {
// Compute hash set of descriptions
Expand All @@ -29,7 +30,7 @@ Layout& VirtualDesk::searchActiveLayout(const RememberLayoutConf& conf) {
for (auto& layout : layouts) {
std::unordered_set<std::string> set;
for (const auto& [k, v] : layout) {
set.insert(k);
set.insert(monitorDesc(k));
}

std::unordered_set<std::string> intersection;
Expand All @@ -50,8 +51,9 @@ Layout& VirtualDesk::searchActiveLayout(const RememberLayoutConf& conf) {
if (layout.size() == monitors.size()) {
if (isVerbose())
printLog("Found layout with size " + std::to_string(layout.size()));

// check layout is valid and substitute invalid monitors
checkAndAdaptLayout(&layout);
checkAndAdaptLayout(&layout, exclude);

m_activeLayout_idx = idx;
return layouts[idx];
Expand All @@ -68,7 +70,7 @@ Layout& VirtualDesk::searchActiveLayout(const RememberLayoutConf& conf) {
}

void VirtualDesk::changeWorkspaceOnMonitor(int workspaceId, CMonitor* monitor) {
layouts[m_activeLayout_idx][monitorDesc(*monitor)] = workspaceId;
layouts[m_activeLayout_idx][monitor] = workspaceId;
}

void VirtualDesk::invalidateActiveLayout() {
Expand All @@ -79,57 +81,80 @@ void VirtualDesk::resetLayout() {
layouts[m_activeLayout_idx] = generateCurrentMonitorLayout();
}

void VirtualDesk::deleteInvalidMonitorOnAllLayouts(CMonitor* monitor) {
void VirtualDesk::deleteInvalidMonitorOnAllLayouts(const CMonitor* monitor) {
for (auto layout : layouts) {
deleteInvalidMonitor(monitor);
}
}

void VirtualDesk::deleteInvalidMonitor(CMonitor* monitor) {
CMonitor* VirtualDesk::deleteInvalidMonitor(const CMonitor* monitor) {
Layout layout_copy(layouts[m_activeLayout_idx]);
for (auto const& [desc, workspaceId] : layout_copy) {
if (monitorDesc(*monitor) == desc) {
int n = INT_MAX;
std::shared_ptr<CMonitor> newMonitor;

// Just place it on the less busy monitor
for (auto mon : currentlyEnabledMonitors()) {
if (mon->ID == monitor->ID)
continue;
auto n_on_mon = g_pCompositor->getWindowsOnWorkspace(mon->activeWorkspace);
if (n_on_mon < n) {
n = n_on_mon;
newMonitor = mon;
}
}
layouts[m_activeLayout_idx][monitorDesc(*newMonitor)] = workspaceId;
layouts[m_activeLayout_idx].erase(desc);
for (auto const& [mon, workspaceId] : layout_copy) {
if (mon == monitor) {
auto newMonitor = firstAvailableMonitor(currentlyEnabledMonitors(monitor));
if (newMonitor)
layouts[m_activeLayout_idx][newMonitor.get()] = workspaceId;
layouts[m_activeLayout_idx].erase(monitor);
return newMonitor.get();
}
}
return nullptr;
}

void VirtualDesk::deleteInvalidMonitorsOnActiveLayout() {
Layout layout_copy(layouts[m_activeLayout_idx]);
auto enabledMonitors = currentlyEnabledMonitors();
std::unordered_set<const CMonitor*> enabledMonitors_set;
for (auto mon : enabledMonitors) {
enabledMonitors_set.insert(mon.get());
}
for (auto [mon, workspaceId] : layout_copy) {
if (enabledMonitors_set.count(mon) <= 0) {
auto newMonitor = firstAvailableMonitor(enabledMonitors);
layouts[m_activeLayout_idx][newMonitor.get()] = workspaceId;
layouts[m_activeLayout_idx].erase(newMonitor.get());
}
}
}

void VirtualDesk::checkAndAdaptLayout(Layout* layout) {
for (auto [desc, wid] : Layout(*layout)) {
auto fromDesc = g_pCompositor->getMonitorFromDesc(desc);
if (!fromDesc || !fromDesc->m_bEnabled) {
std::shared_ptr<CMonitor> VirtualDesk::firstAvailableMonitor(const std::vector<std::shared_ptr<CMonitor>>& enabledMonitors) {
int n = INT_MAX;
std::shared_ptr<CMonitor> newMonitor;
for (auto mon : currentlyEnabledMonitors()) {
auto n_on_mon = g_pCompositor->getWindowsOnWorkspace(mon->activeWorkspace);
if (n_on_mon < n) {
n = n_on_mon;
newMonitor = mon;
}
}
return newMonitor;
}

void VirtualDesk::checkAndAdaptLayout(Layout* layout, const CMonitor* exclude) {
auto enabledMons = currentlyEnabledMonitors(exclude);
if (enabledMons.size() == 0)
return;
for (auto [mon, wid] : Layout(*layout)) {
if (!mon || !mon->m_bEnabled || mon == exclude) {
// Let's try to find a "new" monitor which wasn't in
// the layout before. If we don't find it, not much we can
// do except for removing this monitor
for (const auto& mon : currentlyEnabledMonitors()) {
if (!layout->contains(monitorDesc(*mon))) {
(*layout)[monitorDesc(*mon)] = wid;
(*layout).erase(desc);
printLog("adapting layout");
for (const auto& enabledMon : enabledMons) {
if (!layout->contains(enabledMon.get())) {
(*layout)[enabledMon.get()] = wid;
(*layout).erase(mon);
return;
}
}
(*layout).erase(desc);
(*layout).erase(mon);
}
}
}

std::unordered_set<std::string> VirtualDesk::setFromMonitors(const std::vector<std::shared_ptr<CMonitor>>& monitors) {
std::unordered_set<std::string> set;
std::transform(monitors.begin(), monitors.end(), std::inserter(set, set.begin()), [](auto mon) { return monitorDesc(*mon); });
std::transform(monitors.begin(), monitors.end(), std::inserter(set, set.begin()), [](auto mon) { return monitorDesc(mon.get()); });
return set;
}

Expand All @@ -142,18 +167,14 @@ Layout VirtualDesk::generateCurrentMonitorLayout() {
auto vdeskFirstWorkspace = (this->id - 1) * monitors.size() + 1;
int j = 0;
for (int i = vdeskFirstWorkspace; i < vdeskFirstWorkspace + monitors.size(); i++) {
layout[monitorDesc(*monitors[j])] = i;
layout[monitors[j].get()] = i;
j++;
}
return layout;
}

std::vector<std::shared_ptr<CMonitor>> VirtualDesk::currentlyEnabledMonitors() {
std::vector<std::shared_ptr<CMonitor>> monitors;
std::copy_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), std::back_inserter(monitors), [](auto mon) { return mon->m_bEnabled; });
return monitors;
}

std::string VirtualDesk::monitorDesc(const CMonitor& monitor) {
return monitor.output->description ? monitor.output->description : "";
std::string VirtualDesk::monitorDesc(const CMonitor* monitor) {
if (!monitor->output)
return monitor->szName;
return monitor->output->description ? monitor->output->description : monitor->szName;
}
Loading

0 comments on commit c7d7a6f

Please sign in to comment.