Initial commit

This commit is contained in:
Stanisław Zagórowski 2023-02-28 00:58:50 +01:00
commit e3f4e050ce
6 changed files with 228 additions and 0 deletions

14
.clang-format Normal file
View file

@ -0,0 +1,14 @@
BasedOnStyle: LLVM
BreakBeforeBraces: Stroustrup
TabWidth: 4
IndentWidth: 4
UseTab: Never
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: true
ColumnLimit: 200
PointerAlignment: Left
AllowShortBlocksOnASingleLine: Empty
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.so
compile_flags.txt

28
Makefile Normal file
View file

@ -0,0 +1,28 @@
# compile with HYPRLAND_HEADERS=<path_to_hl> make all
# make sure that the path above is to the root hl repo directory, NOT src/
# and that you have ran `make protocols` in the hl dir.
PLUGIN_NAME=split-monitor-workspaces
SOURCE_FILES=$(wildcard src/*.cpp)
.PHONY: clean clangd
all: check_env $(PLUGIN_NAME).so
install: all
cp $(PLUGIN_NAME).so ${HOME}/.local/share/hyprload/plugins/bin
check_env:
ifndef HYPRLAND_HEADERS
$(error HYPRLAND_HEADERS is undefined! Please set it to the path to the root of the configured Hyprland repo)
endif
$(PLUGIN_NAME).so: $(SOURCE_FILES) $(INCLUDE_FILES)
g++ -shared -fPIC --no-gnu-unique $(SOURCE_FILES) -o $(PLUGIN_NAME).so -g -I "/usr/include/pixman-1" -I "/usr/include/libdrm" -I "${HYPRLAND_HEADERS}" -Iinclude -std=c++23
clean:
rm ./examplePlugin.so
clangd:
printf "%b" "-I/usr/include/pixman-1\n-I/usr/include/libdrm\n-I${HYPRLAND_HEADERS}\n-Iinclude\n-std=c++2b" > compile_flags.txt

43
README.md Normal file
View file

@ -0,0 +1,43 @@
# split-monitor-workspaces
A small plugin to provide `awesome`/`dwm`-like behavior with workspaces: split them between monitors and provide independent numbering
# Installing
Since Hyprland plugins don't have ABI guarantees, you *should* download the Hyprland source and compile it if you plan to use plugins.
This ensures the compiler version is the same between the Hyprland build you're running, and the plugins you are using.
The guide on compiling and installing Hyprland manually is on the [wiki](http://wiki.hyprland.org/Getting-Started/Installation/#manual-manual-build)
## Using [hyprload](https://github.com/Duckonaut/hyprload)
1. Export the `HYPRLAND_HEADERS` variable to point to the root directory of the Hyprland repo
- `export HYPRLAND_HEADERS="$HOME/repos/Hyprland"`
2. Install
- `make install`
## Manual installation
1. Export the `HYPRLAND_HEADERS` variable to point to the root directory of the Hyprland repo
- `export HYPRLAND_HEADERS="$HOME/repos/Hyprland"`
2. Compile
- `make all`
3. Add this line to the bottom of your hyprland config
- `exec-once=hyprctl plugin load <ABSOLUTE PATH TO split-monitor-workspaces.so>`
# Usage
The plugin provides drop-in replacements for workspace-related commands
| Normal | Replacement |
|-----------------------|-------------------------------|
| workspace | split-workspace |
| movetoworkspace | split-movetoworkspace |
| movetoworkspacesilent | split-movetoworkspacesilent |
It also provides the following config values
| Name | Type | Default | Description |
|-------------------------------------------|-----------|-----------|-----------------------------------------------|
| `plugin:split-monitor-workspaces:count` | int | 10 | How many workspaces to bind to the monitor |
Keep in mind that if you're using, for example, the `wlr/workspaces` widgets in [waybar](https://github.com/Alexays/Waybar), this will require a change to your config. You should set `all-outputs` to `false`, and adjust the icon mapping.
If your workspace-per-monitor count is 10, the first monitor will have workspaces 1-10, the second 11-20 and so on. They will be accessed via numbers 1-10 while your mouse is on a given monitor.
# Special thanks
- [hyprsome](https://github.com/sopa0/hyprsome): An earlier project of similar nature

5
include/globals.hpp Normal file
View file

@ -0,0 +1,5 @@
#pragma once
#include <src/plugins/PluginAPI.hpp>
inline HANDLE PHANDLE = nullptr;

136
src/main.cpp Normal file
View file

@ -0,0 +1,136 @@
#define WLR_USE_UNSTABLE
#include "src/helpers/Color.hpp"
#include "src/managers/KeybindManager.hpp"
#include "globals.hpp"
#include <src/Compositor.hpp>
#include <src/config/ConfigManager.hpp>
#include <src/helpers/Workspace.hpp>
#include <algorithm>
#include <map>
#include <thread>
#include <unistd.h>
#include <vector>
const std::string s_workspaceCount = "plugin:split-monitor-workspaces:count";
const CColor s_pluginColor = {0x61 / 255.0f, 0xAF / 255.0f, 0xEF / 255.0f, 1.0f};
std::map<uint64_t, std::vector<std::string>> g_vMonitorWorkspaceMap;
static HOOK_CALLBACK_FN* e_monitorAddedHandle = nullptr;
static HOOK_CALLBACK_FN* e_monitorRemovedHandle = nullptr;
const std::string& getWorkspaceFromMonitor(CMonitor* monitor, const std::string& workspace)
{
int workspaceIndex = std::stoi(workspace);
if (workspaceIndex - 1 < 0) {
return workspace;
}
if (workspaceIndex - 1 >= g_vMonitorWorkspaceMap[monitor->ID].size()) {
return workspace;
}
return g_vMonitorWorkspaceMap[monitor->ID][workspaceIndex - 1];
}
void monitorWorkspace(std::string workspace)
{
CMonitor* monitor = g_pCompositor->getMonitorFromCursor();
int workspaceCount = HyprlandAPI::getConfigValue(PHANDLE, s_workspaceCount)->intValue;
HyprlandAPI::invokeHyprctlCommand("dispatch", "workspace " + getWorkspaceFromMonitor(monitor, workspace));
}
void monitorMoveToWorkspace(std::string workspace)
{
CMonitor* monitor = g_pCompositor->getMonitorFromCursor();
int workspaceCount = HyprlandAPI::getConfigValue(PHANDLE, s_workspaceCount)->intValue;
HyprlandAPI::invokeHyprctlCommand("dispatch", "movetoworkspace " + getWorkspaceFromMonitor(monitor, workspace));
}
void monitorMoveToWorkspaceSilent(std::string workspace)
{
CMonitor* monitor = g_pCompositor->getMonitorFromCursor();
int workspaceCount = HyprlandAPI::getConfigValue(PHANDLE, s_workspaceCount)->intValue;
HyprlandAPI::invokeHyprctlCommand("dispatch", "movetoworkspacesilent " + getWorkspaceFromMonitor(monitor, workspace));
}
void mapWorkspacesToMonitors()
{
g_vMonitorWorkspaceMap.clear();
int workspaceIndex = 1;
for (auto& monitor : g_pCompositor->m_vMonitors) {
int workspaceCount = g_pConfigManager->getConfigValuePtrSafe(s_workspaceCount)->intValue;
std::string logMessage =
"[split-monitor-workspaces] Mapping workspaces " + std::to_string(workspaceIndex) + "-" + std::to_string(workspaceIndex + workspaceCount - 1) + " to monitor " + monitor->szName;
HyprlandAPI::addNotification(PHANDLE, logMessage, s_pluginColor, 5000);
for (int i = workspaceIndex; i < workspaceIndex + workspaceCount; i++) {
std::string workspaceName = std::to_string(i);
g_vMonitorWorkspaceMap[monitor->ID].push_back(workspaceName);
HyprlandAPI::invokeHyprctlCommand("keyword", "wsbind " + workspaceName + "," + monitor->szName);
CWorkspace* workspace = g_pCompositor->getWorkspaceByName(workspaceName);
if (workspace != nullptr) {
g_pCompositor->moveWorkspaceToMonitor(workspace, monitor.get());
}
}
workspaceIndex += workspaceCount;
}
}
void refreshMapping(void*, std::any value)
{
mapWorkspacesToMonitors();
}
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION()
{
return HYPRLAND_API_VERSION;
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle)
{
PHANDLE = handle;
HyprlandAPI::addConfigValue(PHANDLE, s_workspaceCount, SConfigValue{.intValue = 10});
HyprlandAPI::addDispatcher(PHANDLE, "split-workspace", monitorWorkspace);
HyprlandAPI::addDispatcher(PHANDLE, "split-movetoworkspace", monitorMoveToWorkspace);
HyprlandAPI::addDispatcher(PHANDLE, "split-movetoworkspacesilent", monitorMoveToWorkspaceSilent);
HyprlandAPI::reloadConfig();
mapWorkspacesToMonitors();
HyprlandAPI::addNotification(PHANDLE, "[split-monitor-workspaces] Initialized successfully!", s_pluginColor, 5000);
e_monitorAddedHandle = HyprlandAPI::registerCallbackDynamic(PHANDLE, "monitorAdded", refreshMapping);
e_monitorRemovedHandle = HyprlandAPI::registerCallbackDynamic(PHANDLE, "monitorRemoved", refreshMapping);
return {"split-monitor-workspaces", "Split monitor workspace namespaces", "Duckonaut", "1.0"};
}
APICALL EXPORT void PLUGIN_EXIT()
{
HyprlandAPI::removeDispatcher(PHANDLE, "split-workspace");
HyprlandAPI::removeDispatcher(PHANDLE, "split-movetoworkspace");
HyprlandAPI::removeDispatcher(PHANDLE, "split-movetoworkspacesilent");
HyprlandAPI::unregisterCallback(PHANDLE, e_monitorAddedHandle);
HyprlandAPI::unregisterCallback(PHANDLE, e_monitorRemovedHandle);
HyprlandAPI::addNotification(PHANDLE, "[split-monitor-workspaces] Unloaded successfully!", s_pluginColor, 5000);
g_vMonitorWorkspaceMap.clear();
}