mirror of
https://github.com/hyprwm/hyprland-plugins.git
synced 2026-05-07 11:18:01 +02:00
bugfix: workspace ids, 10 -> 0, >10 -> alpha
This commit is contained in:
parent
72cd83f8be
commit
faa6b41a73
3 changed files with 73 additions and 199 deletions
|
|
@ -1,197 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Link local hyprexpo.so into the hyprpm-installed plugin path for quick testing.
|
||||
#
|
||||
# Usage:
|
||||
# ./dev-link.sh # auto-detect installed hyprexpo.so and symlink to local build
|
||||
# ./dev-link.sh -b # build first, then link
|
||||
# ./dev-link.sh -t /path/to/hyprexpo.so # specify target path explicitly
|
||||
# ./dev-link.sh -r # restore original file from .bak and remove symlink
|
||||
#
|
||||
# Notes:
|
||||
# - This script only affects your local user install if hyprpm installed to XDG_DATA_HOME.
|
||||
# - If your hyprexpo is system-wide, you may need sudo to replace it.
|
||||
|
||||
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
local_so="$here/hyprexpo.so"
|
||||
target_so=""
|
||||
do_build=0
|
||||
do_restore=0
|
||||
do_list=0
|
||||
do_interactive=0
|
||||
|
||||
msg() { echo "[dev-link] $*"; }
|
||||
err() { echo "[dev-link] ERROR: $*" >&2; exit 1; }
|
||||
|
||||
usage() {
|
||||
sed -n '2,20p' "$BASH_SOURCE" | sed 's/^# \{0,1\}//'
|
||||
}
|
||||
|
||||
detect_target() {
|
||||
local data_home="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
local cache_home="${XDG_CACHE_HOME:-$HOME/.cache}"
|
||||
local candidates=(
|
||||
"$data_home/hyprpm"
|
||||
"$cache_home/hyprpm"
|
||||
"$data_home/hyprland"
|
||||
"$HOME/.local/lib/hyprland"
|
||||
"/usr/lib/hyprland"
|
||||
"/usr/lib64/hyprland"
|
||||
)
|
||||
|
||||
for base in "${candidates[@]}"; do
|
||||
if [[ -d "$base" ]]; then
|
||||
local found
|
||||
# Prefer plugin tree
|
||||
found=$(find "$base" -type f -name hyprexpo.so 2>/dev/null | head -n1 || true)
|
||||
if [[ -n "${found:-}" ]]; then
|
||||
echo "$found"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
detect_runtime_target() {
|
||||
# Extract hyprexpo.so from the running Hyprland process mappings
|
||||
local pid
|
||||
pid="$(pidof Hyprland 2>/dev/null || pgrep -x Hyprland 2>/dev/null || true)"
|
||||
[[ -n "$pid" ]] || return 1
|
||||
local path
|
||||
# print the last column (pathname) and stop at first match
|
||||
path="$(awk '/hyprexpo\.so/ {print $NF; exit}' "/proc/$pid/maps" 2>/dev/null || true)"
|
||||
if [[ -n "$path" && -f "$path" ]]; then
|
||||
echo "$path"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-b|--build) do_build=1; shift ;;
|
||||
-t|--target) target_so="${2:-}"; shift 2 ;;
|
||||
-r|--restore) do_restore=1; shift ;;
|
||||
-l|--list) do_list=1; shift ;;
|
||||
-i|--interactive) do_interactive=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) err "Unknown arg: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( do_restore )); then
|
||||
if [[ -z "$target_so" ]]; then
|
||||
target_so=$(detect_target || true)
|
||||
fi
|
||||
[[ -n "$target_so" ]] || err "Could not detect hyprexpo.so. Pass -t /path/to/hyprexpo.so"
|
||||
|
||||
if [[ -L "$target_so" ]]; then
|
||||
msg "Removing symlink: $target_so"
|
||||
rm -f -- "$target_so"
|
||||
fi
|
||||
if [[ -f "$target_so.bak" ]]; then
|
||||
msg "Restoring backup: $target_so.bak -> $target_so"
|
||||
mv -f -- "$target_so.bak" "$target_so"
|
||||
else
|
||||
msg "No backup found at $target_so.bak — nothing to restore"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if (( do_build )); then
|
||||
msg "Building hyprexpo.so"
|
||||
make -C "$here" all
|
||||
fi
|
||||
|
||||
[[ -f "$local_so" ]] || err "Local build not found: $local_so (run with -b to build)"
|
||||
|
||||
if [[ -z "$target_so" || $do_list -eq 1 || $do_interactive -eq 1 ]]; then
|
||||
# Prefer the path actually loaded by the running Hyprland instance
|
||||
target_so_runtime="$(detect_runtime_target || true)"
|
||||
# Fallback to filesystem-based detection
|
||||
target_so_fs="$(detect_target || true)"
|
||||
# try hyprpm output as an additional fallback
|
||||
target_so_hpm=""
|
||||
if command -v hyprpm >/dev/null 2>&1; then
|
||||
out="$(hyprpm list 2>/dev/null || true)"
|
||||
if [[ -n "$out" ]]; then
|
||||
target_so_hpm="$(printf '%s\n' "$out" | grep -i hyprexpo | grep -oE '/[^ ]*hyprexpo\.so' | head -n1 || true)"
|
||||
fi
|
||||
fi
|
||||
# last resort: shallow scan
|
||||
target_so_scan="$(find "$HOME/.local/share" "$HOME/.cache" -maxdepth 7 -type f -name hyprexpo.so 2>/dev/null | head -n1 || true)"
|
||||
|
||||
# collect candidates and filter out the local build if present
|
||||
local_abs="$(readlink -f "$local_so")"
|
||||
mapfile -t candidates < <(printf '%s\n' "$target_so_runtime" "$target_so_fs" "$target_so_hpm" "$target_so_scan" | awk 'NF' | awk '!seen[$0]++')
|
||||
filtered=()
|
||||
for c in "${candidates[@]}"; do
|
||||
ca="$(readlink -f "$c" 2>/dev/null || true)"
|
||||
[[ -n "$ca" && "$ca" != "$local_abs" ]] && filtered+=("$ca")
|
||||
done
|
||||
|
||||
if (( do_list )); then
|
||||
if ((${#filtered[@]} == 0)); then
|
||||
msg "No installed hyprexpo.so found (excluding local build)."
|
||||
else
|
||||
printf '%s\n' "${filtered[@]}"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if (( do_interactive )); then
|
||||
if ((${#filtered[@]} == 0)); then
|
||||
err "No installed hyprexpo.so found (excluding local build)."
|
||||
fi
|
||||
echo "Select target hyprexpo.so to link:" >&2
|
||||
i=1
|
||||
for c in "${filtered[@]}"; do
|
||||
echo " [$i] $c" >&2
|
||||
i=$((i+1))
|
||||
done
|
||||
read -rp "> " choice
|
||||
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#filtered[@]} )); then
|
||||
err "Invalid selection"
|
||||
fi
|
||||
target_so="${filtered[$((choice-1))]}"
|
||||
else
|
||||
# pick the first filtered candidate if no explicit target provided
|
||||
if [[ -z "$target_so" && ${#filtered[@]} -gt 0 ]]; then
|
||||
target_so="${filtered[0]}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
[[ -n "$target_so" ]] || err "Could not detect hyprexpo.so. Pass -t /path/to/hyprexpo.so"
|
||||
|
||||
msg "Target: $target_so"
|
||||
|
||||
local_abs="$(readlink -f "$local_so")"
|
||||
target_abs="$(readlink -f "$target_so" 2>/dev/null || true)"
|
||||
if [[ -n "$target_abs" && "$target_abs" == "$local_abs" ]]; then
|
||||
msg "Target is the local build; nothing to link."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -L "$target_so" ]]; then
|
||||
current_link="$(readlink -f "$target_so")"
|
||||
if [[ "$current_link" == "$local_abs" ]]; then
|
||||
msg "Already linked to local build. Done."
|
||||
exit 0
|
||||
else
|
||||
msg "Target is a symlink to $current_link — replacing"
|
||||
rm -f -- "$target_so"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$target_so" ]]; then
|
||||
msg "Backing up existing file to $target_so.bak"
|
||||
cp -f -- "$target_so" "$target_so.bak"
|
||||
fi
|
||||
|
||||
msg "Linking $target_so -> $local_so"
|
||||
ln -sf "$local_so" "$target_so"
|
||||
|
||||
msg "Done. Restart Hyprland to load the local build."
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
using namespace Hyprutils::String;
|
||||
|
||||
#include <cctype>
|
||||
#include <optional>
|
||||
|
||||
#include "globals.hpp"
|
||||
#include "overview.hpp"
|
||||
|
|
@ -42,6 +43,7 @@ static bool renderingOverview = false;
|
|||
static SDispatchResult onKbFocusDispatcher(std::string arg);
|
||||
static SDispatchResult onKbConfirmDispatcher(std::string arg);
|
||||
static SDispatchResult onKbSelectNumberDispatcher(std::string arg);
|
||||
static SDispatchResult onKbSelectTokenDispatcher(std::string arg);
|
||||
|
||||
//
|
||||
static void hkRenderWorkspace(void* thisptr, PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* now, const CBox& geometry) {
|
||||
|
|
@ -251,6 +253,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
HyprlandAPI::addDispatcherV2(PHANDLE, "hyprexpo:kb_focus", ::onKbFocusDispatcher);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "hyprexpo:kb_confirm", ::onKbConfirmDispatcher);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "hyprexpo:kb_selectn", ::onKbSelectNumberDispatcher);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "hyprexpo:kb_select", ::onKbSelectTokenDispatcher);
|
||||
|
||||
HyprlandAPI::addConfigKeyword(PHANDLE, "hyprexpo-gesture", ::expoGestureKeyword, {});
|
||||
|
||||
|
|
@ -273,6 +276,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_enable", Hyprlang::INT{1});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_color", Hyprlang::INT{0xFFFFFFFF});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_font_size", Hyprlang::INT{16});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_text_mode", Hyprlang::STRING{"id"});
|
||||
// defaults: center/middle within the label container
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_position", Hyprlang::STRING{"center"});
|
||||
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprexpo:label_offset_x", Hyprlang::INT{0});
|
||||
|
|
@ -368,3 +372,30 @@ static SDispatchResult onKbSelectNumberDispatcher(std::string arg) {
|
|||
g_pOverview->onKbSelectNumber(num);
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::optional<int> tokenToIndex(const std::string& s) {
|
||||
if (s.size() != 1)
|
||||
return std::nullopt;
|
||||
const char c = s[0];
|
||||
if (c >= '1' && c <= '9')
|
||||
return (c - '1');
|
||||
if (c == '0')
|
||||
return 9;
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return 10 + (c - 'a');
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return 10 + (c - 'A');
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static SDispatchResult onKbSelectTokenDispatcher(std::string arg) {
|
||||
if (!g_pOverview)
|
||||
return {};
|
||||
while (!arg.empty() && std::isspace(arg.front())) arg.erase(arg.begin());
|
||||
while (!arg.empty() && std::isspace(arg.back())) arg.pop_back();
|
||||
const auto idx = tokenToIndex(arg);
|
||||
if (!idx)
|
||||
return {.success = false, .error = "invalid token (expected 1..9, 0, a..z)"};
|
||||
g_pOverview->onKbSelectToken(*idx);
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -489,6 +489,20 @@ int COverview::tileForWorkspaceID(int wsid) const {
|
|||
return -1;
|
||||
}
|
||||
|
||||
int COverview::tileForVisibleIndex(int vIdx) const {
|
||||
if (vIdx < 0)
|
||||
return -1;
|
||||
int seen = 0;
|
||||
for (size_t i = 0; i < images.size(); ++i) {
|
||||
if (images[i].workspaceID == WORKSPACE_INVALID)
|
||||
continue;
|
||||
if (seen == vIdx)
|
||||
return (int)i;
|
||||
++seen;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void COverview::moveFocus(int dx, int dy) {
|
||||
ensureKbFocusInitialized();
|
||||
if (kbFocusID == -1)
|
||||
|
|
@ -599,6 +613,18 @@ void COverview::onKbSelectNumber(int num) {
|
|||
}
|
||||
}
|
||||
|
||||
void COverview::onKbSelectToken(int visibleIdx) {
|
||||
if (closing)
|
||||
return;
|
||||
if (visibleIdx < 0)
|
||||
return;
|
||||
const int tid = tileForVisibleIndex(visibleIdx);
|
||||
if (tid != -1) {
|
||||
closeOnID = tid;
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void COverview::redrawID(int id, bool forcelowres) {
|
||||
if (pMonitor->m_activeWorkspace != startedOn && !closing) {
|
||||
// likely user changed.
|
||||
|
|
@ -813,6 +839,7 @@ void COverview::fullRender() {
|
|||
static auto* const* PLABELCOL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_color")->getDataStaticPtr();
|
||||
static auto* const* PLABELSIZE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_font_size")->getDataStaticPtr();
|
||||
static auto const* PLABELPOS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_position")->getDataStaticPtr();
|
||||
static auto const* PLABELMODE = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_text_mode")->getDataStaticPtr();
|
||||
static auto* const* PLABELOX = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_offset_x")->getDataStaticPtr();
|
||||
static auto* const* PLABELOY = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_offset_y")->getDataStaticPtr();
|
||||
static auto const* PLABELSHOW = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_show")->getDataStaticPtr();
|
||||
|
|
@ -893,10 +920,11 @@ void COverview::fullRender() {
|
|||
return CBox{x, y, (double)size.x, (double)size.y};
|
||||
};
|
||||
|
||||
int tokenCounter = 0;
|
||||
for (size_t y = 0; y < (size_t)SIDE_LENGTH; ++y) {
|
||||
for (size_t x = 0; x < (size_t)SIDE_LENGTH; ++x) {
|
||||
const int id = x + y * SIDE_LENGTH;
|
||||
if (images[id].workspaceID == WORKSPACE_INVALID || !shouldShow(id))
|
||||
if (images[id].workspaceID == WORKSPACE_INVALID)
|
||||
continue;
|
||||
|
||||
// compute tile box again for label placement
|
||||
|
|
@ -904,7 +932,15 @@ void COverview::fullRender() {
|
|||
tile.scale(pMonitor->m_scale).translate(pos->value());
|
||||
tile.round();
|
||||
|
||||
const std::string label = std::to_string(images[id].workspaceID);
|
||||
std::string label;
|
||||
if (std::string{*PLABELMODE} == "token") {
|
||||
int k = tokenCounter;
|
||||
if (k <= 8) label = std::to_string(k + 1);
|
||||
else if (k == 9) label = "0";
|
||||
else label = std::string(1, char('a' + (k - 10)));
|
||||
} else {
|
||||
label = std::to_string(images[id].workspaceID);
|
||||
}
|
||||
const int baseF = std::max(8, (int)**PLABELSIZE);
|
||||
const int st = resolveState(id);
|
||||
|
||||
|
|
@ -919,6 +955,9 @@ void COverview::fullRender() {
|
|||
}
|
||||
};
|
||||
|
||||
// if label isn't shown per mode, still advance token index
|
||||
if (!shouldShow(id)) { tokenCounter++; continue; }
|
||||
|
||||
CHyprColor col = CHyprColor{(uint64_t)**PLCOLDEF};
|
||||
float scl = 1.0f;
|
||||
static auto* const* PLPIXELSNAP = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprexpo:label_pixel_snap")->getDataStaticPtr();
|
||||
|
|
@ -983,6 +1022,7 @@ void COverview::fullRender() {
|
|||
else
|
||||
drawNoBG(images[id].labelTexDefault, images[id].labelSizeDefault);
|
||||
}
|
||||
tokenCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue