hyprctl: add getprop (#11394)

This commit is contained in:
Luke Barkess
2025-08-18 16:41:17 +01:00
committed by GitHub
parent d890178610
commit 21953ddf3d
4 changed files with 272 additions and 0 deletions

View File

@@ -49,6 +49,7 @@ commands:
the same format as in colors in config. Will reset the same format as in colors in config. Will reset
when Hyprland's config is reloaded when Hyprland's config is reloaded
setprop ... Sets a window property setprop ... Sets a window property
getprop ... Gets a window property
splash Get the current splash splash Get the current splash
switchxkblayout ... Sets the xkb layout index for a keyboard switchxkblayout ... Sets the xkb layout index for a keyboard
systeminfo Get system info systeminfo Get system info
@@ -159,6 +160,18 @@ lock:
flags: flags:
See 'hyprctl --help')#"; See 'hyprctl --help')#";
const std::string_view GETPROP_HELP = R"#(usage: hyprctl [flags] getprop <regex> <property>
regex:
Regular expression by which a window will be searched
property:
See https://wiki.hypr.land/Configuring/Using-hyprctl/#setprop for list
of properties
flags:
See 'hyprctl --help')#";
const std::string_view SWITCHXKBLAYOUT_HELP = R"#(usage: [flags] switchxkblayout <device> <cmd> const std::string_view SWITCHXKBLAYOUT_HELP = R"#(usage: [flags] switchxkblayout <device> <cmd>
device: device:

View File

@@ -427,6 +427,8 @@ int main(int argc, char** argv) {
std::println("{}", PLUGIN_HELP); std::println("{}", PLUGIN_HELP);
} else if (cmd == "setprop") { } else if (cmd == "setprop") {
std::println("{}", SETPROP_HELP); std::println("{}", SETPROP_HELP);
} else if (cmd == "getprop") {
std::println("{}", GETPROP_HELP);
} else if (cmd == "switchxkblayout") { } else if (cmd == "switchxkblayout") {
std::println("{}", SWITCHXKBLAYOUT_HELP); std::println("{}", SWITCHXKBLAYOUT_HELP);
} else { } else {

View File

@@ -18,6 +18,131 @@ using namespace Hyprutils::Memory;
#define UP CUniquePointer #define UP CUniquePointer
#define SP CSharedPointer #define SP CSharedPointer
static std::string getCommandStdOut(std::string command) {
CProcess process("bash", {"-c", command});
process.addEnv("HYPRLAND_INSTANCE_SIGNATURE", HIS);
process.runSync();
const std::string& stdOut = process.stdOut();
// Remove trailing new line
return stdOut.substr(0, stdOut.length() - 1);
}
static bool testGetprop() {
NLog::log("{}Testing hyprctl getprop", Colors::GREEN);
if (!Tests::spawnKitty()) {
NLog::log("{}Error: kitty did not spawn", Colors::RED);
return false;
}
// animationstyle
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "(unset)");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": ""})");
getFromSocket("/dispatch setprop class:kitty animationstyle teststyle");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "teststyle");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": "teststyle"})");
// maxsize
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "inf inf");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [null,null]})");
getFromSocket("/dispatch setprop class:kitty maxsize 200 150");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "200 150");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [200,150]})");
// minsize
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "20 20");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [20,20]})");
getFromSocket("/dispatch setprop class:kitty minsize 100 50");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "100 50");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [100,50]})");
// alpha
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "1");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 1})");
getFromSocket("/dispatch setprop class:kitty alpha 0.3");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "0.3");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 0.3})");
// alphainactive
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "1");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 1})");
getFromSocket("/dispatch setprop class:kitty alphainactive 0.5");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "0.5");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 0.5})");
// alphafullscreen
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "1");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 1})");
getFromSocket("/dispatch setprop class:kitty alphafullscreen 0.75");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "0.75");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 0.75})");
// alphaoverride
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": false})");
getFromSocket("/dispatch setprop class:kitty alphaoverride true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": true})");
// alphainactiveoverride
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": false})");
getFromSocket("/dispatch setprop class:kitty alphainactiveoverride true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": true})");
// alphafullscreenoverride
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": false})");
getFromSocket("/dispatch setprop class:kitty alphafullscreenoverride true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": true})");
// activebordercolor
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ee33ccff ee00ff99 45deg");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ee33ccff ee00ff99 45deg"})");
getFromSocket("/dispatch setprop class:kitty activebordercolor rgb(abcdef)");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ffabcdef 0deg");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ffabcdef 0deg"})");
// bool window properties
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": false})");
getFromSocket("/dispatch setprop class:kitty allowsinput true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": true})");
// int window properties
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "10");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 10})");
getFromSocket("/dispatch setprop class:kitty rounding 4");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "4");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})");
// float window properties
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "2");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 2})");
getFromSocket("/dispatch setprop class:kitty roundingpower 1.25");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "1.25");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 1.25})");
// errors
EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args");
EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animationstyle"), "window not found");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found");
// kill all
NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows();
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
EXPECT(Tests::windowCount(), 0);
return true;
}
static bool test() { static bool test() {
NLog::log("{}Testing hyprctl", Colors::GREEN); NLog::log("{}Testing hyprctl", Colors::GREEN);
@@ -29,6 +154,9 @@ static bool test() {
EXPECT(jqProc.exitCode(), 0); EXPECT(jqProc.exitCode(), 0);
} }
if (!testGetprop())
return false;
return !ret; return !ret;
} }

View File

@@ -1399,6 +1399,134 @@ static std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string requ
return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error); return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error);
} }
static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 3)
return "not enough args";
const auto WINREGEX = vars[1];
const auto PROP = vars[2];
const auto PWINDOW = g_pCompositor->getWindowByRegex(WINREGEX);
if (!PWINDOW)
return "window not found";
const bool FORMNORM = format == FORMAT_NORMAL;
auto sizeToString = [&](bool max) -> std::string {
auto sizeValue = PWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
if (max)
sizeValue = PWINDOW->m_windowData.maxSize.valueOr(Vector2D(INFINITY, INFINITY));
if (FORMNORM)
return std::format("{} {}", sizeValue.x, sizeValue.y);
else {
std::string xSizeString = (sizeValue.x != INFINITY) ? std::to_string(sizeValue.x) : "null";
std::string ySizeString = (sizeValue.y != INFINITY) ? std::to_string(sizeValue.y) : "null";
return std::format(R"({{"{}": [{},{}]}})", PROP, xSizeString, ySizeString);
}
};
auto alphaToString = [&](CWindowOverridableVar<SAlphaValue>& alpha, bool getAlpha) -> std::string {
if (FORMNORM) {
if (getAlpha)
return std::format("{}", alpha.valueOrDefault().alpha);
else
return std::format("{}", alpha.valueOrDefault().overridden);
} else {
if (getAlpha)
return std::format(R"({{"{}": {}}})", PROP, alpha.valueOrDefault().alpha);
else
return std::format(R"({{"{}": {}}})", PROP, alpha.valueOrDefault().overridden);
}
};
auto borderColorToString = [&](bool active) -> std::string {
static auto PACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.active_border");
static auto PINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.inactive_border");
static auto PNOGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border_active");
static auto PNOGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border");
static auto PGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_active");
static auto PGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_inactive");
static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
const bool GROUPLOCKED = PWINDOW->m_groupData.pNextWindow.lock() ? PWINDOW->getGroupHead()->m_groupData.locked : false;
if (active) {
auto* const ACTIVECOL = (CGradientValueData*)(PACTIVECOL.ptr())->getData();
auto* const NOGROUPACTIVECOL = (CGradientValueData*)(PNOGROUPACTIVECOL.ptr())->getData();
auto* const GROUPACTIVECOL = (CGradientValueData*)(PGROUPACTIVECOL.ptr())->getData();
auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPACTIVELOCKEDCOL.ptr())->getData();
const auto* const ACTIVECOLOR =
!PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
std::string borderColorString = PWINDOW->m_windowData.activeBorderColor.valueOr(*ACTIVECOLOR).toString();
if (FORMNORM)
return borderColorString;
else
return std::format(R"({{"{}": "{}"}})", PROP, borderColorString);
} else {
auto* const INACTIVECOL = (CGradientValueData*)(PINACTIVECOL.ptr())->getData();
auto* const NOGROUPINACTIVECOL = (CGradientValueData*)(PNOGROUPINACTIVECOL.ptr())->getData();
auto* const GROUPINACTIVECOL = (CGradientValueData*)(PGROUPINACTIVECOL.ptr())->getData();
auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPINACTIVELOCKEDCOL.ptr())->getData();
const auto* const INACTIVECOLOR = !PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) :
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
std::string borderColorString = PWINDOW->m_windowData.inactiveBorderColor.valueOr(*INACTIVECOLOR).toString();
if (FORMNORM)
return borderColorString;
else
return std::format(R"({{"{}": "{}"}})", PROP, borderColorString);
}
};
auto windowPropToString = [&](auto& prop) -> std::string {
if (FORMNORM)
return std::format("{}", prop.valueOrDefault());
else
return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
};
if (PROP == "animationstyle") {
auto& animationStyle = PWINDOW->m_windowData.animationStyle;
if (FORMNORM)
return animationStyle.valueOr("(unset)");
else
return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
} else if (PROP == "maxsize")
return sizeToString(true);
else if (PROP == "minsize")
return sizeToString(false);
else if (PROP == "alpha")
return alphaToString(PWINDOW->m_windowData.alpha, true);
else if (PROP == "alphainactive")
return alphaToString(PWINDOW->m_windowData.alphaInactive, true);
else if (PROP == "alphafullscreen")
return alphaToString(PWINDOW->m_windowData.alphaFullscreen, true);
else if (PROP == "alphaoverride")
return alphaToString(PWINDOW->m_windowData.alpha, false);
else if (PROP == "alphainactiveoverride")
return alphaToString(PWINDOW->m_windowData.alphaInactive, false);
else if (PROP == "alphafullscreenoverride")
return alphaToString(PWINDOW->m_windowData.alphaFullscreen, false);
else if (PROP == "activebordercolor")
return borderColorToString(true);
else if (PROP == "inactivebordercolor")
return borderColorToString(false);
else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end())
return windowPropToString(*search->second(PWINDOW));
else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end())
return windowPropToString(*search->second(PWINDOW));
else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end())
return windowPropToString(*search->second(PWINDOW));
return "prop not found";
}
static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) { static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
std::string curitem = ""; std::string curitem = "";
@@ -1760,6 +1888,7 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify}); registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify}); registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp}); registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp});
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror}); registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest}); registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
registerCommand(SHyprCtlCommand{"output", false, dispatchOutput}); registerCommand(SHyprCtlCommand{"output", false, dispatchOutput});