Hyprland/src/debug/HyprCtl.cpp

2009 lines
73 KiB
C++
Raw Normal View History

2022-03-20 16:51:14 +01:00
#include "HyprCtl.hpp"
#include <algorithm>
#include <format>
#include <fstream>
#include <iterator>
2022-03-21 18:29:41 +01:00
#include <netinet/in.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
2022-03-21 18:29:41 +01:00
#include <sys/socket.h>
2022-03-20 16:51:14 +01:00
#include <sys/stat.h>
2022-03-21 18:29:41 +01:00
#include <sys/types.h>
2024-01-01 17:53:00 +01:00
#include <sys/utsname.h>
#include <sys/un.h>
2022-03-20 16:51:14 +01:00
#include <unistd.h>
#include <sys/poll.h>
#include <filesystem>
#include <ranges>
#include <sys/eventfd.h>
2022-03-20 16:51:14 +01:00
2023-01-31 00:03:23 +00:00
#include <sstream>
2022-03-20 16:51:14 +01:00
#include <string>
#include <typeindex>
#include <numeric>
2022-03-20 16:51:14 +01:00
#include <hyprutils/string/String.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
using namespace Hyprutils::String;
core: begin using CFileDescriptor from hyprutils (#9122) * config: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * hyprctl: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * ikeyboard: make fd use CFileDescriptor make use of the new CFileDescriptor instead of manual FD handling, also in sendKeymap remove dead code, it already early returns if keyboard isnt valid, and dont try to close the FD that ikeyboard owns. * core: make SHMFile functions use CFileDescriptor make SHMFile misc functions use CFileDescriptor and its associated usage in dmabuf and keyboard. * core: make explicit sync use CFileDescriptor begin using CFileDescriptor in explicit sync and its timelines and eglsync usage in opengl, there is still a bit left with manual handling that requires future aquamarine change aswell. * eventmgr: make fd and sockets use CFileDescriptor make use of the hyprutils CFileDescriptor instead of manual FD and socket handling and closing. * eventloopmgr: make timerfd use CFileDescriptor make the timerfd use CFileDescriptor instead of manual fd handling * opengl: make gbm fd use CFileDescriptor make the gbm rendernode fd use CFileDescriptor instead of manual fd handling * core: make selection source/offer use CFileDescriptor make data selection source and offers use CFileDescriptor and its associated use in xwm and protocols * protocols: convert protocols fd to CFileDescriptor make most fd handling use CFileDescriptor in protocols * shm: make SHMPool use CfileDescriptor make SHMPool use CFileDescriptor instead of manual fd handling. * opengl: duplicate fd with CFileDescriptor duplicate fenceFD with CFileDescriptor duplicate instead. * xwayland: make sockets and fds use CFileDescriptor instead of manual opening/closing make sockets and fds use CFileDescriptor * keybindmgr: make sockets and fds use CFileDescriptor make sockets and fds use CFileDescriptor instead of manual handling.
2025-01-30 12:30:12 +01:00
using namespace Hyprutils::OS;
#include <aquamarine/input/Input.hpp>
#include "../config/ConfigDataValues.hpp"
#include "../config/ConfigValue.hpp"
#include "../managers/CursorManager.hpp"
#include "../hyprerror/HyprError.hpp"
#include "../devices/IPointer.hpp"
#include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "debug/RollingLogFollow.hpp"
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
#include "../desktop/LayerSurface.hpp"
2024-12-05 19:35:50 +00:00
#include "../version.h"
#include "../Compositor.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../plugins/PluginSystem.hpp"
#include "../managers/AnimationManager.hpp"
#include "../debug/HyprNotificationOverlay.hpp"
#include "../render/Renderer.hpp"
#include "../render/OpenGL.hpp"
#if defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/ucred.h>
#define CRED_T xucred
#define CRED_LVL SOL_LOCAL
#define CRED_OPT LOCAL_PEERCRED
#define CRED_PID cr_pid
#elif defined(__NetBSD__)
#define CRED_T unpcbid
#define CRED_LVL SOL_LOCAL
#define CRED_OPT LOCAL_PEEREID
#define CRED_PID unp_pid
#else
#if defined(__OpenBSD__)
#define CRED_T sockpeercred
#else
#define CRED_T ucred
#endif
#define CRED_LVL SOL_SOCKET
#define CRED_OPT SO_PEERCRED
#define CRED_PID pid
#endif
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
str.pop_back();
}
static std::string formatToString(uint32_t drmFormat) {
switch (drmFormat) {
case DRM_FORMAT_XRGB2101010: return "XRGB2101010";
case DRM_FORMAT_XBGR2101010: return "XBGR2101010";
case DRM_FORMAT_XRGB8888: return "XRGB8888";
case DRM_FORMAT_XBGR8888: return "XBGR8888";
default: break;
}
return "Invalid";
}
static std::string availableModesForOutput(PHLMONITOR pMonitor, eHyprCtlOutputFormat format) {
std::string result;
for (auto const& m : pMonitor->m_output->modes) {
if (format == FORMAT_NORMAL)
result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
else
result += std::format("\"{}x{}@{:.2f}Hz\",", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
}
trimTrailingComma(result);
return result;
}
std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) {
std::string result;
if (!m->m_output || m->m_id == -1)
return "";
2023-03-16 00:18:39 +00:00
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += std::format(
R"#({{
2023-09-06 12:51:36 +02:00
"id": {},
"name": "{}",
"description": "{}",
"make": "{}",
"model": "{}",
"serial": "{}",
"width": {},
"height": {},
"refreshRate": {:.5f},
"x": {},
"y": {},
"activeWorkspace": {{
"id": {},
"name": "{}"
}},
"specialWorkspace": {{
"id": {},
"name": "{}"
}},
"reserved": [{}, {}, {}, {}],
"scale": {:.2f},
"transform": {},
"focused": {},
"dpmsStatus": {},
"vrr": {},
"solitary": "{:x}",
"activelyTearing": {},
"directScanoutTo": "{:x}",
"disabled": {},
"currentFormat": "{}",
"mirrorOf": "{}",
"availableModes": [{}]
2023-09-06 12:51:36 +02:00
}},)#",
m->m_id, escapeJSONStrings(m->m_name), escapeJSONStrings(m->m_shortDescription), escapeJSONStrings(m->m_output->make), escapeJSONStrings(m->m_output->model),
escapeJSONStrings(m->m_output->serial), (int)m->m_pixelSize.x, (int)m->m_pixelSize.y, m->m_refreshRate, (int)m->m_position.x, (int)m->m_position.y,
m->activeWorkspaceID(), (!m->m_activeWorkspace ? "" : escapeJSONStrings(m->m_activeWorkspace->m_name)), m->activeSpecialWorkspaceID(),
escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), (int)m->m_reservedTopLeft.x, (int)m->m_reservedTopLeft.y,
(int)m->m_reservedBottomRight.x, (int)m->m_reservedBottomRight.y, m->m_scale, (int)m->m_transform, (m == g_pCompositor->m_lastMonitor ? "true" : "false"),
(m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"), (uint64_t)m->m_solitaryClient.get(),
(m->m_tearingState.activelyTearing ? "true" : "false"), (uint64_t)m->m_lastScanout.get(), (m->m_enabled ? "false" : "true"),
formatToString(m->m_output->state->state().drmFormat), m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format));
} else {
result +=
std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
"special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
"dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tactivelyTearing: {}\n\tdirectScanoutTo: {:x}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tmirrorOf: "
"{}\n\tavailableModes: {}\n\n",
m->m_name, m->m_id, (int)m->m_pixelSize.x, (int)m->m_pixelSize.y, m->m_refreshRate, (int)m->m_position.x, (int)m->m_position.y, m->m_shortDescription,
m->m_output->make, m->m_output->model, m->m_output->serial, m->activeWorkspaceID(), (!m->m_activeWorkspace ? "" : m->m_activeWorkspace->m_name),
m->activeSpecialWorkspaceID(), (m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), (int)m->m_reservedTopLeft.x,
(int)m->m_reservedTopLeft.y, (int)m->m_reservedBottomRight.x, (int)m->m_reservedBottomRight.y, m->m_scale, (int)m->m_transform,
(m == g_pCompositor->m_lastMonitor ? "yes" : "no"), (int)m->m_dpmsStatus, m->m_output->state->state().adaptiveSync, (uint64_t)m->m_solitaryClient.get(),
m->m_tearingState.activelyTearing, (uint64_t)m->m_lastScanout.get(), !m->m_enabled, formatToString(m->m_output->state->state().drmFormat),
m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format));
}
return result;
}
static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
auto allMonitors = false;
if (vars.size() > 2)
return "too many args";
if (vars.size() == 2 && vars[1] == "all")
allMonitors = true;
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto const& m : allMonitors ? g_pCompositor->m_realMonitors : g_pCompositor->m_monitors) {
result += CHyprCtl::getMonitorData(m, format);
}
trimTrailingComma(result);
result += "]";
} else {
for (auto const& m : allMonitors ? g_pCompositor->m_realMonitors : g_pCompositor->m_monitors) {
if (!m->m_output || m->m_id == -1)
2023-03-16 00:18:39 +00:00
continue;
result += CHyprCtl::getMonitorData(m, format);
}
2022-03-20 16:51:14 +01:00
}
return result;
}
static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
const auto tags = w->m_tags.getTags();
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
return std::accumulate(tags.begin(), tags.end(), std::string(),
[](const std::string& a, const std::string& b) { return a.empty() ? std::format("\"{}\"", b) : std::format("{}, \"{}\"", a, b); });
else
return std::accumulate(tags.begin(), tags.end(), std::string(), [](const std::string& a, const std::string& b) { return a.empty() ? b : a + ", " + b; });
}
static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
if (w->m_groupData.pNextWindow.expired())
return isJson ? "" : "0";
std::ostringstream result;
PHLWINDOW head = w->getGroupHead();
PHLWINDOW curr = head;
while (true) {
if (isJson)
result << std::format("\"0x{:x}\"", (uintptr_t)curr.get());
else
result << std::format("{:x}", (uintptr_t)curr.get());
curr = curr->m_groupData.pNextWindow.lock();
// We've wrapped around to the start, break out without trailing comma
if (curr == head)
break;
result << (isJson ? ", " : ",");
}
return result.str();
}
std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
auto getFocusHistoryID = [](PHLWINDOW wnd) -> int {
for (size_t i = 0; i < g_pCompositor->m_windowFocusHistory.size(); ++i) {
if (g_pCompositor->m_windowFocusHistory[i].lock() == wnd)
return i;
}
return -1;
};
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(
2023-09-06 12:51:36 +02:00
R"#({{
"address": "0x{:x}",
"mapped": {},
"hidden": {},
"at": [{}, {}],
"size": [{}, {}],
"workspace": {{
"id": {},
"name": "{}"
}},
"floating": {},
"pseudo": {},
2023-09-06 12:51:36 +02:00
"monitor": {},
"class": "{}",
"title": "{}",
"initialClass": "{}",
"initialTitle": "{}",
"pid": {},
"xwayland": {},
"pinned": {},
"fullscreen": {},
internal: refactor fullscreen states (#7104) * refactor fullscreen modified: src/Compositor.cpp modified: src/Compositor.hpp modified: src/config/ConfigManager.cpp modified: src/config/ConfigManager.hpp modified: src/debug/HyprCtl.cpp modified: src/desktop/LayerSurface.cpp modified: src/desktop/Window.cpp modified: src/desktop/Window.hpp modified: src/desktop/Workspace.cpp modified: src/desktop/Workspace.hpp modified: src/events/Windows.cpp modified: src/helpers/Monitor.cpp modified: src/layout/DwindleLayout.cpp modified: src/layout/DwindleLayout.hpp modified: src/layout/IHyprLayout.cpp modified: src/layout/IHyprLayout.hpp modified: src/layout/MasterLayout.cpp modified: src/layout/MasterLayout.hpp modified: src/managers/KeybindManager.cpp modified: src/managers/KeybindManager.hpp modified: src/managers/input/IdleInhibitor.cpp modified: src/managers/input/InputManager.cpp modified: src/managers/input/Swipe.cpp modified: src/protocols/ForeignToplevelWlr.cpp modified: src/render/Renderer.cpp modified: src/render/decorations/CHyprGroupBarDecoration.cpp * clean up modified: src/config/ConfigManager.cpp modified: src/debug/HyprCtl.cpp modified: src/desktop/Window.hpp modified: src/desktop/Workspace.cpp modified: src/events/Windows.cpp modified: src/managers/KeybindManager.cpp modified: src/managers/input/Swipe.cpp * fix mapWindow fullscreen modified: src/events/Windows.cpp * fix typo modified: src/desktop/Workspace.cpp * add fullscreenstate modified: src/config/ConfigManager.cpp modified: src/events/Windows.cpp * change syncFullscreen to lower modified: src/config/ConfigManager.hpp * initialize fs state modified: src/desktop/Window.hpp
2024-07-31 17:55:52 +00:00
"fullscreenClient": {},
2023-09-06 12:51:36 +02:00
"grouped": [{}],
"tags": [{}],
"swallowing": "0x{:x}",
"focusHistoryID": {},
"inhibitingIdle": {},
"xdgTag": "{}",
"xdgDescription": "{}"
2023-09-06 12:51:36 +02:00
}},)#",
(uintptr_t)w.get(), (w->m_isMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_realPosition->goal().x, (int)w->m_realPosition->goal().y,
(int)w->m_realSize->goal().x, (int)w->m_realSize->goal().y, w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
escapeJSONStrings(!w->m_workspace ? "" : w->m_workspace->m_name), ((int)w->m_isFloating == 1 ? "true" : "false"), (w->m_isPseudotiled ? "true" : "false"),
(int64_t)w->monitorID(), escapeJSONStrings(w->m_class), escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle),
w->getPID(), ((int)w->m_isX11 == 1 ? "true" : "false"), (w->m_pinned ? "true" : "false"), (uint8_t)w->m_fullscreenState.internal, (uint8_t)w->m_fullscreenState.client,
getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_swallowed.get(), getFocusHistoryID(w),
(g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")));
} else {
return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
"{}\n\txwayland: {}\n\tpinned: "
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: {}\n\txdgTag: "
"{}\n\txdgDescription: {}\n\n",
(uintptr_t)w.get(), w->m_title, (int)w->m_isMapped, (int)w->isHidden(), (int)w->m_realPosition->goal().x, (int)w->m_realPosition->goal().y,
(int)w->m_realSize->goal().x, (int)w->m_realSize->goal().y, w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID, (!w->m_workspace ? "" : w->m_workspace->m_name),
(int)w->m_isFloating, (int)w->m_isPseudotiled, (int64_t)w->monitorID(), w->m_class, w->m_title, w->m_initialClass, w->m_initialTitle, w->getPID(), (int)w->m_isX11,
(int)w->m_pinned, (uint8_t)w->m_fullscreenState.internal, (uint8_t)w->m_fullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
(uintptr_t)w->m_swallowed.get(), getFocusHistoryID(w), (int)g_pInputManager->isWindowInhibiting(w, false), w->xdgTag().value_or(""), w->xdgDescription().value_or(""));
}
}
static std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto const& w : g_pCompositor->m_windows) {
if (!w->m_isMapped && !g_pHyprCtl->m_currentRequestParams.all)
continue;
result += CHyprCtl::getWindowData(w, format);
}
trimTrailingComma(result);
result += "]";
} else {
for (auto const& w : g_pCompositor->m_windows) {
if (!w->m_isMapped && !g_pHyprCtl->m_currentRequestParams.all)
continue;
result += CHyprCtl::getWindowData(w, format);
2022-06-27 13:17:11 +02:00
}
2022-03-20 16:51:14 +01:00
}
return result;
}
std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) {
const auto PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = w->m_monitor.lock();
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(R"#({{
2023-09-06 12:51:36 +02:00
"id": {},
"name": "{}",
"monitor": "{}",
"monitorID": {},
2023-09-06 12:51:36 +02:00
"windows": {},
"hasfullscreen": {},
"lastwindow": "0x{:x}",
"lastwindowtitle": "{}",
"ispersistent": {}
2023-09-06 12:51:36 +02:00
}})#",
w->m_id, escapeJSONStrings(w->m_name), escapeJSONStrings(PMONITOR ? PMONITOR->m_name : "?"),
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->m_id) : "null"), w->getWindows(), w->m_hasFullscreenWindow ? "true" : "false",
(uintptr_t)PLASTW.get(), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->m_persistent ? "true" : "false");
} else {
return std::format(
"workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: {}\n\n",
w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindows(), (int)w->m_hasFullscreenWindow,
(uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_title : "", (int)w->m_persistent);
}
}
static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputFormat format) {
const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; };
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
const std::string monitor = r.monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.monitor));
const std::string default_ = (bool)(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : "";
const std::string persistent = (bool)(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : "";
const std::string gapsIn = (bool)(r.gapsIn) ?
std::format(",\n \"gapsIn\": [{}, {}, {}, {}]", r.gapsIn.value().m_top, r.gapsIn.value().m_right, r.gapsIn.value().m_bottom, r.gapsIn.value().m_left) :
"";
const std::string gapsOut = (bool)(r.gapsOut) ?
std::format(",\n \"gapsOut\": [{}, {}, {}, {}]", r.gapsOut.value().m_top, r.gapsOut.value().m_right, r.gapsOut.value().m_bottom, r.gapsOut.value().m_left) :
2024-02-23 21:25:04 +00:00
"";
const std::string borderSize = (bool)(r.borderSize) ? std::format(",\n \"borderSize\": {}", r.borderSize.value()) : "";
const std::string border = (bool)(r.noBorder) ? std::format(",\n \"border\": {}", boolToString(!r.noBorder.value())) : "";
const std::string rounding = (bool)(r.noRounding) ? std::format(",\n \"rounding\": {}", boolToString(!r.noRounding.value())) : "";
const std::string decorate = (bool)(r.decorate) ? std::format(",\n \"decorate\": {}", boolToString(r.decorate.value())) : "";
const std::string shadow = (bool)(r.noShadow) ? std::format(",\n \"shadow\": {}", boolToString(!r.noShadow.value())) : "";
const std::string defaultName = r.defaultName.has_value() ? std::format(",\n \"defaultName\": \"{}\"", escapeJSONStrings(r.defaultName.value())) : "";
std::string result =
std::format(R"#({{
"workspaceString": "{}"{}{}{}{}{}{}{}{}{}{}{}
}})#",
escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut, borderSize, border, rounding, decorate, shadow, defaultName);
return result;
} else {
const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "<unset>" : escapeJSONStrings(r.monitor));
const std::string default_ = std::format("\tdefault: {}\n", (bool)(r.isDefault) ? boolToString(r.isDefault) : "<unset>");
const std::string persistent = std::format("\tpersistent: {}\n", (bool)(r.isPersistent) ? boolToString(r.isPersistent) : "<unset>");
const std::string gapsIn = (bool)(r.gapsIn) ? std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.gapsIn.value().m_top), std::to_string(r.gapsIn.value().m_right),
std::to_string(r.gapsIn.value().m_bottom), std::to_string(r.gapsIn.value().m_left)) :
std::format("\tgapsIn: <unset>\n");
const std::string gapsOut = (bool)(r.gapsOut) ? std::format("\tgapsOut: {} {} {} {}\n", std::to_string(r.gapsOut.value().m_top), std::to_string(r.gapsOut.value().m_right),
std::to_string(r.gapsOut.value().m_bottom), std::to_string(r.gapsOut.value().m_left)) :
std::format("\tgapsOut: <unset>\n");
const std::string borderSize = std::format("\tborderSize: {}\n", (bool)(r.borderSize) ? std::to_string(r.borderSize.value()) : "<unset>");
const std::string border = std::format("\tborder: {}\n", (bool)(r.noBorder) ? boolToString(!r.noBorder.value()) : "<unset>");
const std::string rounding = std::format("\trounding: {}\n", (bool)(r.noRounding) ? boolToString(!r.noRounding.value()) : "<unset>");
const std::string decorate = std::format("\tdecorate: {}\n", (bool)(r.decorate) ? boolToString(r.decorate.value()) : "<unset>");
const std::string shadow = std::format("\tshadow: {}\n", (bool)(r.noShadow) ? boolToString(!r.noShadow.value()) : "<unset>");
const std::string defaultName = std::format("\tdefaultName: {}\n", r.defaultName.value_or("<unset>"));
std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut,
borderSize, border, rounding, decorate, shadow, defaultName);
return result;
}
}
static std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
if (!g_pCompositor->m_lastMonitor)
return "unsafe state";
std::string result = "";
auto w = g_pCompositor->m_lastMonitor->m_activeWorkspace;
if (!valid(w))
return "internal error";
return CHyprCtl::getWorkspaceData(w, format);
}
static std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto const& w : g_pCompositor->m_workspaces) {
result += CHyprCtl::getWorkspaceData(w, format);
result += ",";
}
trimTrailingComma(result);
result += "]";
} else {
for (auto const& w : g_pCompositor->m_workspaces) {
result += CHyprCtl::getWorkspaceData(w, format);
}
2022-03-20 16:51:14 +01:00
}
2022-03-20 16:51:14 +01:00
return result;
}
static std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto const& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
result += ",";
}
trimTrailingComma(result);
result += "]";
} else {
for (auto const& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
}
}
return result;
}
static std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
const auto PWINDOW = g_pCompositor->m_lastWindow.lock();
2022-03-22 16:54:45 +01:00
if (!validMapped(PWINDOW))
return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
auto result = CHyprCtl::getWindowData(PWINDOW, format);
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
result.pop_back();
return result;
2022-03-22 16:54:45 +01:00
}
static std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
2022-03-22 16:54:45 +01:00
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
for (auto const& mon : g_pCompositor->m_monitors) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#("{}": {{
"levels": {{
)#",
escapeJSONStrings(mon->m_name));
int layerLevel = 0;
for (auto const& level : mon->m_layerSurfaceLayers) {
result += std::format(
R"#(
2023-09-06 12:51:36 +02:00
"{}": [
)#",
layerLevel);
for (auto const& layer : level) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"x": {},
"y": {},
"w": {},
"h": {},
"namespace": "{}",
"pid": {}
2023-09-06 12:51:36 +02:00
}},)#",
(uintptr_t)layer.get(), layer->m_geometry.x, layer->m_geometry.y, layer->m_geometry.width, layer->m_geometry.height, escapeJSONStrings(layer->m_namespace),
layer->getPID());
}
trimTrailingComma(result);
if (level.size() > 0)
result += "\n ";
result += "],";
layerLevel++;
2022-03-22 16:54:45 +01:00
}
trimTrailingComma(result);
result += "\n }\n},";
}
trimTrailingComma(result);
result += "\n}\n";
2022-09-25 20:07:48 +02:00
} else {
for (auto const& mon : g_pCompositor->m_monitors) {
result += std::format("Monitor {}:\n", mon->m_name);
2023-01-09 21:26:07 +01:00
int layerLevel = 0;
static const std::array<std::string, 4> levelNames = {"background", "bottom", "top", "overlay"};
for (auto const& level : mon->m_layerSurfaceLayers) {
result += std::format("\tLayer level {} ({}):\n", layerLevel, levelNames[layerLevel]);
for (auto const& layer : level) {
result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, namespace: {}, pid: {}\n", (uintptr_t)layer.get(), layer->m_geometry.x, layer->m_geometry.y,
layer->m_geometry.width, layer->m_geometry.height, layer->m_namespace, layer->getPID());
}
layerLevel++;
}
result += "\n\n";
2022-03-22 16:54:45 +01:00
}
}
return result;
}
static std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format(
R"#(
"{}",)#",
m);
}
trimTrailingComma(result);
result += "\n]\n";
} else {
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format("{}\n", m);
}
}
return result;
}
static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
std::string currErrors = g_pConfigManager->getErrors();
CVarList errLines(currErrors, 0, '\n');
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (const auto& line : errLines) {
result += std::format(
R"#(
"{}",)#",
escapeJSONStrings(line));
}
trimTrailingComma(result);
result += "\n]\n";
} else {
for (const auto& line : errLines) {
result += std::format("{}\n", line);
}
}
return result;
}
static std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
2022-06-02 13:59:33 +02:00
std::string result = "";
auto getModState = [](SP<IKeyboard> keyboard, const char* xkbModName) -> bool {
auto IDX = xkb_keymap_mod_get_index(keyboard->m_xkbKeymap, xkbModName);
if (IDX == XKB_MOD_INVALID)
return false;
return (keyboard->m_modifiersState.locked & (1 << IDX)) > 0;
};
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
result += "\"mice\": [\n";
for (auto const& m : g_pInputManager->m_pointers) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"name": "{}",
"defaultSpeed": {:.5f}
}},)#",
(uintptr_t)m.get(), escapeJSONStrings(m->m_hlName),
m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f);
}
2022-06-02 13:59:33 +02:00
trimTrailingComma(result);
result += "\n],\n";
result += "\"keyboards\": [\n";
for (auto const& k : g_pInputManager->m_keyboards) {
const auto KM = k->getActiveLayout();
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"name": "{}",
"rules": "{}",
"model": "{}",
"layout": "{}",
"variant": "{}",
"options": "{}",
"active_keymap": "{}",
"capsLock": {},
"numLock": {},
2023-09-06 12:51:36 +02:00
"main": {}
}},)#",
(uintptr_t)k.get(), escapeJSONStrings(k->m_hlName), escapeJSONStrings(k->m_currentRules.rules), escapeJSONStrings(k->m_currentRules.model),
escapeJSONStrings(k->m_currentRules.layout), escapeJSONStrings(k->m_currentRules.variant), escapeJSONStrings(k->m_currentRules.options), escapeJSONStrings(KM),
(getModState(k, XKB_MOD_NAME_CAPS) ? "true" : "false"), (getModState(k, XKB_MOD_NAME_NUM) ? "true" : "false"), (k->m_active ? "true" : "false"));
}
2022-06-02 13:59:33 +02:00
trimTrailingComma(result);
result += "\n],\n";
2022-06-02 13:59:33 +02:00
result += "\"tablets\": [\n";
2022-06-02 13:59:33 +02:00
for (auto const& d : g_pInputManager->m_tabletPads) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"type": "tabletPad",
2023-09-06 12:51:36 +02:00
"belongsTo": {{
"address": "0x{:x}",
"name": "{}"
}}
}},)#",
(uintptr_t)d.get(), (uintptr_t)d->m_parent.get(), escapeJSONStrings(d->m_parent ? d->m_parent->m_hlName : ""));
}
2022-06-09 19:25:26 +02:00
for (auto const& d : g_pInputManager->m_tablets) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)d.get(), escapeJSONStrings(d->m_hlName));
}
2022-06-09 19:25:26 +02:00
for (auto const& d : g_pInputManager->m_tabletTools) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"type": "tabletTool",
2023-09-06 12:51:36 +02:00
}},)#",
(uintptr_t)d.get());
}
trimTrailingComma(result);
result += "\n],\n";
result += "\"touch\": [\n";
for (auto const& d : g_pInputManager->m_touches) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)d.get(), escapeJSONStrings(d->m_hlName));
}
trimTrailingComma(result);
2022-10-04 20:07:21 +01:00
result += "\n],\n";
result += "\"switches\": [\n";
for (auto const& d : g_pInputManager->m_switches) {
result += std::format(
2023-09-06 12:51:36 +02:00
R"#( {{
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, escapeJSONStrings(d.pDevice ? d.pDevice->getName() : ""));
2022-10-04 20:07:21 +01:00
}
trimTrailingComma(result);
result += "\n]\n";
2022-06-09 19:25:26 +02:00
result += "}\n";
} else {
result += "mice:\n";
for (auto const& m : g_pInputManager->m_pointers) {
result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)m.get(), m->m_hlName,
(m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f));
}
result += "\n\nKeyboards:\n";
for (auto const& k : g_pInputManager->m_keyboards) {
const auto KM = k->getActiveLayout();
result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\tcapsLock: "
"{}\n\t\t\tnumLock: {}\n\t\t\tmain: {}\n",
(uintptr_t)k.get(), k->m_hlName, k->m_currentRules.rules, k->m_currentRules.model, k->m_currentRules.layout, k->m_currentRules.variant,
k->m_currentRules.options, KM, (getModState(k, XKB_MOD_NAME_CAPS) ? "yes" : "no"), (getModState(k, XKB_MOD_NAME_NUM) ? "yes" : "no"),
(k->m_active ? "yes" : "no"));
}
result += "\n\nTablets:\n";
for (auto const& d : g_pInputManager->m_tabletPads) {
result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)d.get(), (uintptr_t)d->m_parent.get(), d->m_parent ? d->m_parent->m_hlName : "");
}
for (auto const& d : g_pInputManager->m_tablets) {
result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->m_hlName, d->aq()->physicalSize.x, d->aq()->physicalSize.y);
}
for (auto const& d : g_pInputManager->m_tabletTools) {
result += std::format("\tTablet Tool at {:x}\n", (uintptr_t)d.get());
}
result += "\n\nTouch:\n";
for (auto const& d : g_pInputManager->m_touches) {
result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)d.get(), d->m_hlName);
}
2022-10-04 20:07:21 +01:00
result += "\n\nSwitches:\n";
for (auto const& d : g_pInputManager->m_switches) {
result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.pDevice ? d.pDevice->getName() : "");
2022-10-04 20:07:21 +01:00
}
2022-06-09 19:25:26 +02:00
}
2022-06-02 13:59:33 +02:00
return result;
}
static std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) {
2023-01-25 15:16:28 +00:00
std::string ret = "";
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
2023-01-25 15:16:28 +00:00
ret += "animations:\n";
for (auto const& ac : g_pConfigManager->getAnimationConfig()) {
ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, (int)ac.second->overridden,
ac.second->internalBezier, ac.second->internalEnabled, ac.second->internalSpeed, ac.second->internalStyle);
2023-01-25 15:16:28 +00:00
}
ret += "beziers:\n";
for (auto const& bz : g_pAnimationManager->getAllBeziers()) {
ret += std::format("\n\tname: {}\n", bz.first);
2023-01-25 15:16:28 +00:00
}
} else {
// json
ret += "[[";
for (auto const& ac : g_pConfigManager->getAnimationConfig()) {
ret += std::format(R"#(
2023-09-06 12:51:36 +02:00
{{
"name": "{}",
"overridden": {},
"bezier": "{}",
"enabled": {},
"speed": {:.2f},
"style": "{}"
}},)#",
ac.first, ac.second->overridden ? "true" : "false", escapeJSONStrings(ac.second->internalBezier), ac.second->internalEnabled ? "true" : "false",
ac.second->internalSpeed, escapeJSONStrings(ac.second->internalStyle));
2023-01-25 15:16:28 +00:00
}
ret[ret.length() - 1] = ']';
ret += ",\n[";
for (auto const& bz : g_pAnimationManager->getAllBeziers()) {
ret += std::format(R"#(
2023-09-06 12:51:36 +02:00
{{
"name": "{}"
}},)#",
escapeJSONStrings(bz.first));
2023-01-25 15:16:28 +00:00
}
trimTrailingComma(ret);
2023-01-25 15:16:28 +00:00
2023-02-11 20:54:32 +00:00
ret += "]]";
2023-01-25 15:16:28 +00:00
}
return ret;
}
static std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[\n\"log\":\"";
result += escapeJSONStrings(Debug::m_rollingLog);
result += "\"]";
} else {
result = Debug::m_rollingLog;
}
return result;
}
static std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto const& sh : SHORTCUTS) {
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
}
if (ret.empty())
ret = "none";
} else {
ret += "[";
for (auto const& sh : SHORTCUTS) {
ret += std::format(R"#(
2023-09-06 12:51:36 +02:00
{{
"name": "{}",
"description": "{}"
}},)#",
escapeJSONStrings(sh.appid + ":" + sh.id), escapeJSONStrings(sh.description));
}
trimTrailingComma(ret);
ret += "]\n";
}
return ret;
}
static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
2023-01-06 14:32:25 +01:00
std::string ret = "";
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto const& kb : g_pKeybindManager->m_keybinds) {
2023-01-06 14:32:25 +01:00
ret += "bind";
2024-11-18 19:56:26 +00:00
if (kb->locked)
2023-01-06 14:32:25 +01:00
ret += "l";
2024-11-18 19:56:26 +00:00
if (kb->mouse)
2023-01-06 14:32:25 +01:00
ret += "m";
2024-11-18 19:56:26 +00:00
if (kb->release)
2023-01-06 14:32:25 +01:00
ret += "r";
2024-11-18 19:56:26 +00:00
if (kb->repeat)
2023-01-06 14:32:25 +01:00
ret += "e";
2024-11-18 19:56:26 +00:00
if (kb->nonConsuming)
ret += "n";
2024-11-18 19:56:26 +00:00
if (kb->hasDescription)
ret += "d";
2023-01-06 14:32:25 +01:00
2024-11-18 19:56:26 +00:00
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdescription: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb->modmask,
kb->submap, kb->key, kb->keycode, kb->catchAll, kb->description, kb->handler, kb->arg);
2023-01-06 14:32:25 +01:00
}
} else {
// json
ret += "[";
for (auto const& kb : g_pKeybindManager->m_keybinds) {
ret += std::format(
2023-01-06 14:32:25 +01:00
R"#(
2023-09-06 12:51:36 +02:00
{{
"locked": {},
"mouse": {},
"release": {},
"repeat": {},
"longPress": {},
2023-09-06 12:51:36 +02:00
"non_consuming": {},
"has_description": {},
2023-09-06 12:51:36 +02:00
"modmask": {},
"submap": "{}",
"key": "{}",
"keycode": {},
"catch_all": {},
"description": "{}",
2023-09-06 12:51:36 +02:00
"dispatcher": "{}",
"arg": "{}"
}},)#",
2024-11-18 19:56:26 +00:00
kb->locked ? "true" : "false", kb->mouse ? "true" : "false", kb->release ? "true" : "false", kb->repeat ? "true" : "false", kb->longPress ? "true" : "false",
kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap), escapeJSONStrings(kb->key), kb->keycode,
kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler), escapeJSONStrings(kb->arg));
2023-01-06 14:32:25 +01:00
}
trimTrailingComma(ret);
2023-01-06 14:32:25 +01:00
ret += "]";
}
return ret;
}
std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
2022-08-11 21:28:37 +02:00
auto commitMsg = trim(GIT_COMMIT_MESSAGE);
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n"
"Date: {}\n"
"Tag: {}, commits: {}\n"
"built against:\n aquamarine {}\n hyprlang {}\n hyprutils {}\n hyprcursor {}\n hyprgraphics {}\n\n\n",
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS, AQUAMARINE_VERSION,
HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION);
2024-12-25 04:32:18 +05:00
#if (!defined(LEGACY_RENDERER) && !ISDEBUG && !defined(NO_XWAYLAND))
result += "no flags were set\n";
#else
result += "flags set:\n";
2022-04-22 18:14:25 +02:00
#ifdef LEGACY_RENDERER
2022-08-11 21:28:37 +02:00
result += "legacyrenderer\n";
2022-04-22 18:14:25 +02:00
#endif
2024-12-25 04:32:18 +05:00
#if ISDEBUG
result += "debug\n";
#endif
2022-04-22 18:14:25 +02:00
#ifdef NO_XWAYLAND
2022-08-11 21:28:37 +02:00
result += "no xwayland\n";
2022-04-22 18:14:25 +02:00
#endif
#endif
2022-08-11 21:28:37 +02:00
return result;
} else {
std::string result = std::format(
2023-09-06 12:51:36 +02:00
R"#({{
"branch": "{}",
"commit": "{}",
"version": "{}",
2023-09-06 12:51:36 +02:00
"dirty": {},
"commit_message": "{}",
"commit_date": "{}",
2023-09-06 12:51:36 +02:00
"tag": "{}",
"commits": "{}",
"buildAquamarine": "{}",
"buildHyprlang": "{}",
"buildHyprutils": "{}",
"buildHyprcursor": "{}",
"buildHyprgraphics": "{}",
"flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION);
2022-08-11 21:28:37 +02:00
#ifdef LEGACY_RENDERER
result += "\"legacyrenderer\",";
#endif
2024-12-25 04:32:18 +05:00
#if ISDEBUG
result += "\"debug\",";
2022-08-11 21:28:37 +02:00
#endif
#ifdef NO_XWAYLAND
result += "\"no xwayland\",";
#endif
trimTrailingComma(result);
2022-08-11 21:28:37 +02:00
result += "]\n}";
return result;
}
return ""; // make the compiler happy
2022-04-22 18:14:25 +02:00
}
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "");
2024-01-01 17:53:00 +01:00
result += "\n\nSystem Information:\n";
struct utsname unameInfo;
uname(&unameInfo);
result += "System name: " + std::string{unameInfo.sysname} + "\n";
result += "Node name: " + std::string{unameInfo.nodename} + "\n";
result += "Release: " + std::string{unameInfo.release} + "\n";
result += "Version: " + std::string{unameInfo.version} + "\n";
result += "\n\n";
#if defined(__DragonFly__) || defined(__FreeBSD__)
2024-11-17 21:57:00 +00:00
const std::string GPUINFO = execAndGet("pciconf -lv | grep -F -A4 vga");
#elif defined(__arm__) || defined(__aarch64__)
std::string GPUINFO;
const std::filesystem::path dev_tree = "/proc/device-tree";
try {
if (std::filesystem::exists(dev_tree) && std::filesystem::is_directory(dev_tree)) {
std::for_each(std::filesystem::directory_iterator(dev_tree), std::filesystem::directory_iterator{}, [&](const std::filesystem::directory_entry& entry) {
if (std::filesystem::is_directory(entry) && entry.path().filename().string().starts_with("soc")) {
std::for_each(std::filesystem::directory_iterator(entry.path()), std::filesystem::directory_iterator{}, [&](const std::filesystem::directory_entry& sub_entry) {
if (std::filesystem::is_directory(sub_entry) && sub_entry.path().filename().string().starts_with("gpu")) {
std::filesystem::path file_path = sub_entry.path() / "compatible";
std::ifstream file(file_path);
if (file)
GPUINFO.append(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
}
});
}
});
}
} catch (...) { GPUINFO = "error"; }
2024-01-01 17:53:00 +01:00
#else
2024-12-19 04:08:57 +05:00
const std::string GPUINFO = execAndGet("lspci -vnn | grep -E '(VGA|Display|3D)'");
2024-01-01 17:53:00 +01:00
#endif
result += "GPU information: \n" + GPUINFO;
if (GPUINFO.contains("NVIDIA") && std::filesystem::exists("/proc/driver/nvidia/version")) {
std::ifstream file("/proc/driver/nvidia/version");
std::string line;
if (file.is_open()) {
while (std::getline(file, line)) {
if (!line.contains("NVRM"))
continue;
result += line;
result += "\n";
}
} else
result += "error";
}
result += "\n\n";
2024-01-01 17:53:00 +01:00
if (std::ifstream file("/etc/os-release"); file.is_open()) {
std::stringstream buffer;
buffer << file.rdbuf();
result += "os-release: " + buffer.str() + "\n\n";
} else
result += "os-release: error\n\n";
2024-01-01 17:53:00 +01:00
result += "plugins:\n";
if (g_pPluginSystem) {
for (auto const& pl : g_pPluginSystem->getAllPlugins()) {
result += std::format(" {} by {} ver {}\n", pl->m_name, pl->m_author, pl->m_version);
}
} else
result += "\tunknown: not runtime\n";
2024-01-01 17:53:00 +01:00
if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.sysInfoConfig) {
result += "\n======Config-Start======\n";
result += g_pConfigManager->getConfigString();
result += "\n======Config-End========\n";
}
2024-01-01 17:53:00 +01:00
return result;
}
static std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
2022-04-21 16:11:29 +02:00
// get rid of the dispatch keyword
in = in.substr(in.find_first_of(' ') + 1);
const auto DISPATCHSTR = in.substr(0, in.find_first_of(' '));
2023-07-21 17:20:23 +02:00
auto DISPATCHARG = std::string();
if ((int)in.find_first_of(' ') != -1)
DISPATCHARG = in.substr(in.find_first_of(' ') + 1);
2022-04-21 16:11:29 +02:00
const auto DISPATCHER = g_pKeybindManager->m_dispatchers.find(DISPATCHSTR);
if (DISPATCHER == g_pKeybindManager->m_dispatchers.end())
2022-04-21 16:11:29 +02:00
return "Invalid dispatcher";
SDispatchResult res = DISPATCHER->second(DISPATCHARG);
2022-04-21 16:11:29 +02:00
Debug::log(LOG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error);
2022-04-21 21:48:37 +02:00
return res.success ? "ok" : res.error;
2022-04-21 16:11:29 +02:00
}
static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// Find the first space to strip the keyword keyword
auto const firstSpacePos = in.find_first_of(' ');
if (firstSpacePos == std::string::npos) // Handle the case where there's no space found (invalid input)
return "Invalid input: no space found";
// Strip the keyword
in = in.substr(firstSpacePos + 1);
// Find the next space for the COMMAND and VALUE
auto const secondSpacePos = in.find_first_of(' ');
if (secondSpacePos == std::string::npos) // Handle the case where there's no second space (invalid input)
return "Invalid input: command and value not properly formatted";
// Extract COMMAND and VALUE
const auto COMMAND = in.substr(0, secondSpacePos);
const auto VALUE = in.substr(secondSpacePos + 1);
// If COMMAND is empty, handle accordingly
if (COMMAND.empty())
return "Invalid input: command is empty";
2022-04-21 16:56:27 +02:00
std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE);
2022-04-21 16:56:27 +02:00
// if we are executing a dynamic source we have to reload everything, so every if will have a check for source.
if (COMMAND == "monitor" || COMMAND == "source")
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
if (COMMAND.contains("input") || COMMAND.contains("device") || COMMAND == "source") {
g_pInputManager->setKeyboardLayout(); // update kb layout
g_pInputManager->setPointerConfigs(); // update mouse cfgs
g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs
2022-12-21 15:11:39 +00:00
g_pInputManager->setTabletConfigs(); // update tablets
2022-08-20 18:57:30 +02:00
}
static auto PLAYOUT = CConfigValue<std::string>("general:layout");
2022-07-16 22:44:29 +02:00
if (COMMAND.contains("general:layout"))
g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout
if (COMMAND.contains("decoration:screen_shader") || COMMAND == "source")
g_pHyprOpenGL->m_bReloadScreenShader = true;
2022-07-16 22:44:29 +02:00
if (COMMAND.contains("blur") || COMMAND == "source") {
for (auto& [m, rd] : g_pHyprOpenGL->m_mMonitorRenderResources) {
rd.blurFBDirty = true;
}
}
if (COMMAND.contains("misc:disable_autoreload"))
g_pConfigManager->updateWatcher();
2023-01-12 12:14:57 +01:00
// decorations will probably need a repaint
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
COMMAND.starts_with("windowrule")) {
for (auto const& m : g_pCompositor->m_monitors) {
g_pHyprRenderer->damageMonitor(m);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
}
2023-01-12 12:14:57 +01:00
}
Debug::log(LOG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);
2022-04-21 21:48:37 +02:00
2022-09-25 20:07:48 +02:00
if (retval == "")
2022-04-21 16:56:27 +02:00
return "ok";
return retval;
}
static std::string reloadRequest(eHyprCtlOutputFormat format, std::string request) {
2022-09-25 20:07:48 +02:00
2022-08-14 23:26:18 +02:00
const auto REQMODE = request.substr(request.find_last_of(' ') + 1);
if (REQMODE == "config-only")
g_pConfigManager->m_noMonitorReload = true;
2022-08-14 23:26:18 +02:00
g_pConfigManager->reload();
2022-09-13 15:36:49 +02:00
2022-05-08 15:28:45 +02:00
return "ok";
}
static std::string killRequest(eHyprCtlOutputFormat format, std::string request) {
2022-06-27 13:42:20 +02:00
g_pInputManager->setClickMode(CLICKMODE_KILL);
return "ok";
}
static std::string splashRequest(eHyprCtlOutputFormat format, std::string request) {
return g_pCompositor->m_currentSplash;
2022-07-10 15:41:26 +02:00
}
static std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) {
2022-10-26 13:19:37 +01:00
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
return std::format("{}, {}", (int)CURSORPOS.x, (int)CURSORPOS.y);
2022-10-26 13:19:37 +01:00
} else {
return std::format(R"#(
2023-09-06 12:51:36 +02:00
{{
"x": {},
"y": {}
}}
)#",
(int)CURSORPOS.x, (int)CURSORPOS.y);
2022-10-26 13:19:37 +01:00
}
return "error";
}
static std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) {
// split by ; ignores ; inside [] and adds ; on last command
2022-04-29 19:44:09 +02:00
request = request.substr(9);
std::string reply = "";
const std::string DELIMITER = "\n\n\n";
int bracket = 0;
size_t idx = 0;
for (size_t i = 0; i <= request.size(); ++i) {
char ch = (i < request.size()) ? request[i] : ';';
if (ch == '[')
++bracket;
else if (ch == ']')
--bracket;
else if (ch == ';' && bracket == 0) {
if (idx < i)
reply += g_pHyprCtl->getReply(trim(request.substr(idx, i - idx))).append(DELIMITER);
idx = i + 1;
continue;
}
2022-04-29 19:44:09 +02:00
}
return reply.substr(0, std::max(static_cast<int>(reply.size() - DELIMITER.size()), 0));
2022-04-29 19:44:09 +02:00
}
static std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
2023-04-20 23:59:31 +01:00
const auto SIZESTR = vars[vars.size() - 1];
std::string theme = "";
2023-04-20 23:59:31 +01:00
for (size_t i = 1; i < vars.size() - 1; ++i)
theme += vars[i] + " ";
if (!theme.empty())
theme.pop_back();
2023-04-20 23:59:31 +01:00
int size = 0;
try {
size = std::stoi(SIZESTR);
} catch (...) { return "size not int"; }
2022-08-10 21:22:11 +02:00
2023-04-20 23:59:31 +01:00
if (size <= 0)
return "size not positive";
2022-08-10 21:22:11 +02:00
if (!g_pCursorManager->changeTheme(theme, size))
return "failed to set cursor";
2022-08-10 21:22:11 +02:00
return "ok";
}
static std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
2022-12-03 15:56:07 +00:00
const auto KB = vars[1];
const auto CMD = vars[2];
2022-12-03 15:56:07 +00:00
SP<IKeyboard> pKeyboard;
2022-12-03 15:56:07 +00:00
auto updateKeyboard = [](const SP<IKeyboard> KEEB, const std::string& CMD) -> std::optional<std::string> {
const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->m_xkbKeymap);
xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) {
if (xkb_state_layout_index_is_active(KEEB->m_xkbState, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1)
break;
2022-12-03 15:56:07 +00:00
activeLayout++;
}
if (CMD == "next")
KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1);
else if (CMD == "prev")
KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked,
activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
else {
int requestedLayout = 0;
try {
requestedLayout = std::stoi(CMD);
} catch (std::exception& e) { return "invalid arg 2"; }
if (requestedLayout < 0 || (uint64_t)requestedLayout > LAYOUTS - 1) {
return "layout idx out of range of " + std::to_string(LAYOUTS);
}
2022-12-03 15:56:07 +00:00
KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked, requestedLayout);
}
return std::nullopt;
};
if (KB == "main" || KB == "active" || KB == "current") {
for (auto const& k : g_pInputManager->m_keyboards) {
if (!k->m_active)
continue;
2022-12-03 15:56:07 +00:00
pKeyboard = k;
break;
2022-12-03 15:56:07 +00:00
}
} else if (KB == "all") {
std::string result = "";
for (auto const& k : g_pInputManager->m_keyboards) {
auto res = updateKeyboard(k, CMD);
if (res.has_value())
result += *res + "\n";
}
return result.empty() ? "ok" : result;
} else {
auto k = std::find_if(g_pInputManager->m_keyboards.begin(), g_pInputManager->m_keyboards.end(),
[&](const auto& other) { return other->m_hlName == g_pInputManager->deviceNameToInternalString(KB); });
2022-12-03 15:56:07 +00:00
if (k == g_pInputManager->m_keyboards.end())
return "device not found";
pKeyboard = *k;
2022-12-03 15:56:07 +00:00
}
if (!pKeyboard)
return "no device";
auto result = updateKeyboard(pKeyboard, CMD);
if (result.has_value())
return *result;
2022-12-03 15:56:07 +00:00
return "ok";
}
static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) {
2023-01-22 16:38:17 +01:00
CVarList vars(request, 0, ' ');
std::string errorMessage = "";
if (vars.size() < 3) {
g_pHyprError->destroy();
if (vars.size() == 2 && !vars[1].find("dis"))
return "var 1 not color or disable";
2023-01-22 16:38:17 +01:00
return "ok";
}
const CHyprColor COLOR = configStringToInt(vars[1]).value_or(0);
2023-01-22 16:38:17 +01:00
for (size_t i = 2; i < vars.size(); ++i)
errorMessage += vars[i] + ' ';
if (errorMessage.empty()) {
g_pHyprError->destroy();
} else {
errorMessage.pop_back(); // pop last space
g_pHyprError->queueCreate(errorMessage, COLOR);
}
return "ok";
}
static std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
auto result = g_pKeybindManager->m_dispatchers["setprop"](request.substr(request.find_first_of(' ') + 1));
return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error);
2023-01-24 19:05:34 +00:00
}
static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) {
2022-08-11 21:16:38 +02:00
std::string curitem = "";
auto nextItem = [&]() {
2022-08-11 21:16:38 +02:00
auto idx = request.find_first_of(' ');
if (idx != std::string::npos) {
curitem = request.substr(0, idx);
request = request.substr(idx + 1);
} else {
curitem = request;
request = "";
}
curitem = trim(curitem);
2022-08-11 21:16:38 +02:00
};
nextItem();
nextItem();
const auto VAR = g_pConfigManager->getHyprlangConfigValuePtr(curitem);
2022-08-11 21:16:38 +02:00
if (!VAR)
2022-08-11 21:16:38 +02:00
return "no such option";
const auto VAL = VAR->getValue();
const auto TYPE = std::type_index(VAL.type());
if (format == FORMAT_NORMAL) {
if (TYPE == typeid(Hyprlang::INT))
return std::format("int: {}\nset: {}", std::any_cast<Hyprlang::INT>(VAL), VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::FLOAT))
return std::format("float: {:2f}\nset: {}", std::any_cast<Hyprlang::FLOAT>(VAL), VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::VEC2))
return std::format("vec2: [{}, {}]\nset: {}", std::any_cast<Hyprlang::VEC2>(VAL).x, std::any_cast<Hyprlang::VEC2>(VAL).y, VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::STRING))
return std::format("str: {}\nset: {}", std::any_cast<Hyprlang::STRING>(VAL), VAR->m_bSetByUser);
else if (TYPE == typeid(void*))
return std::format("custom type: {}\nset: {}", ((ICustomConfigValueData*)std::any_cast<void*>(VAL))->toString(), VAR->m_bSetByUser);
} else {
if (TYPE == typeid(Hyprlang::INT))
return std::format(R"({{"option": "{}", "int": {}, "set": {} }})", curitem, std::any_cast<Hyprlang::INT>(VAL), VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::FLOAT))
return std::format(R"({{"option": "{}", "float": {:2f}, "set": {} }})", curitem, std::any_cast<Hyprlang::FLOAT>(VAL), VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::VEC2))
return std::format(R"({{"option": "{}", "vec2": [{},{}], "set": {} }})", curitem, std::any_cast<Hyprlang::VEC2>(VAL).x, std::any_cast<Hyprlang::VEC2>(VAL).y,
VAR->m_bSetByUser);
else if (TYPE == typeid(Hyprlang::STRING))
return std::format(R"({{"option": "{}", "str": "{}", "set": {} }})", curitem, escapeJSONStrings(std::any_cast<Hyprlang::STRING>(VAL)), VAR->m_bSetByUser);
else if (TYPE == typeid(void*))
return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem, ((ICustomConfigValueData*)std::any_cast<void*>(VAL))->toString(), VAR->m_bSetByUser);
2022-08-11 21:16:38 +02:00
}
return "invalid type (internal error)";
2022-08-11 21:16:38 +02:00
}
static std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
if (!PWINDOW)
return "none";
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto const& wd : PWINDOW->m_windowDecorations) {
result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},";
}
trimTrailingComma(result);
result += "]";
} else {
result = +"Decoration\tPriority\n";
for (auto const& wd : PWINDOW->m_windowDecorations) {
result += wd->getDisplayName() + "\t" + std::to_string(wd->getPositioningInfo().priority) + "\n";
}
}
return result;
}
static std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 2)
return "not enough args";
const auto MODE = vars[1];
bool added = false;
if (!vars[3].empty()) {
for (auto const& m : g_pCompositor->m_realMonitors) {
if (m->m_name == vars[3])
return "Name already taken";
}
}
if (MODE == "create" || MODE == "add") {
if (g_pCompositor->getMonitorFromName(vars[3]))
return "A real monitor already uses that name.";
for (auto const& impl : g_pCompositor->m_aqBackend->getImplementations() | std::views::reverse) {
auto type = impl->type();
if (type == Aquamarine::AQ_BACKEND_HEADLESS && (vars[2] == "headless" || vars[2] == "auto")) {
added = true;
impl->createOutput(vars[3]);
break;
}
if (type == Aquamarine::AQ_BACKEND_WAYLAND && (vars[2] == "wayland" || vars[2] == "auto")) {
added = true;
impl->createOutput(vars[3]);
break;
}
}
if (!added)
return "no backend replied to the request";
} else if (MODE == "destroy" || MODE == "remove") {
const auto PMONITOR = g_pCompositor->getMonitorFromName(vars[2]);
if (!PMONITOR)
return "output not found";
if (!PMONITOR->m_createdByUser)
return "cannot remove a real display. Use the monitor keyword.";
PMONITOR->m_output->destroy();
}
return "ok";
}
static std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 2)
return "not enough args";
const auto OPERATION = vars[1];
const auto PATH = vars[2];
if (OPERATION == "load") {
if (vars.size() < 3)
return "not enough args";
g_pHyprCtl->m_currentRequestParams.pendingPromise = CPromise<std::string>::make([PATH](SP<CPromiseResolver<std::string>> resolver) {
g_pPluginSystem->loadPlugin(PATH)->then([resolver, PATH](SP<CPromiseResult<CPlugin*>> result) {
if (result->hasError()) {
resolver->reject(result->error());
return;
}
resolver->resolve("ok");
});
});
return "ok";
} else if (OPERATION == "unload") {
if (vars.size() < 3)
return "not enough args";
const auto PLUGIN = g_pPluginSystem->getPluginByPath(PATH);
if (!PLUGIN)
return "plugin not loaded";
g_pPluginSystem->unloadPlugin(PLUGIN);
} else if (OPERATION == "list") {
const auto PLUGINS = g_pPluginSystem->getAllPlugins();
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
if (PLUGINS.size() == 0)
return "[]";
for (auto const& p : PLUGINS) {
result += std::format(
R"#(
{{
"name": "{}",
"author": "{}",
"handle": "{:x}",
"version": "{}",
"description": "{}"
}},)#",
escapeJSONStrings(p->m_name), escapeJSONStrings(p->m_author), (uintptr_t)p->m_handle, escapeJSONStrings(p->m_version), escapeJSONStrings(p->m_description));
}
trimTrailingComma(result);
result += "]";
} else {
if (PLUGINS.size() == 0)
return "no plugins loaded";
for (auto const& p : PLUGINS) {
result += std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->m_name, p->m_author, (uintptr_t)p->m_handle, p->m_version,
p->m_description);
}
}
return result;
} else {
return "unknown opt";
}
return "ok";
}
static std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) {
2023-03-20 16:00:54 +00:00
CVarList vars(request, 0, ' ');
if (vars.size() < 5)
return "not enough args";
const auto ICON = vars[1];
if (!isNumber(ICON))
return "invalid arg 1";
int icon = -1;
try {
icon = std::stoi(ICON);
} catch (std::exception& e) { return "invalid arg 1"; }
if (icon > ICON_NONE || icon < 0)
2023-03-20 16:00:54 +00:00
icon = ICON_NONE;
const auto TIME = vars[2];
int time = 0;
try {
time = std::stoi(TIME);
} catch (std::exception& e) { return "invalid arg 2"; }
const auto COLOR_RESULT = configStringToInt(vars[3]);
if (!COLOR_RESULT)
return "invalid arg 3";
CHyprColor color = *COLOR_RESULT;
2023-03-20 16:00:54 +00:00
size_t msgidx = 4;
float fontsize = 13.f;
if (vars[msgidx].length() > 9 && vars[msgidx].compare(0, 9, "fontsize:") == 0) {
const auto FONTSIZE = vars[msgidx].substr(9);
2023-03-20 16:00:54 +00:00
if (!isNumber(FONTSIZE, true))
return "invalid fontsize kwarg";
try {
fontsize = std::stoi(FONTSIZE);
} catch (std::exception& e) { return "invalid fontsize karg"; }
++msgidx;
2023-03-20 16:00:54 +00:00
}
if (vars.size() <= msgidx)
return "not enough args";
const auto MESSAGE = vars.join(" ", msgidx);
2023-03-20 16:00:54 +00:00
g_pHyprNotificationOverlay->addNotification(MESSAGE, color, time, (eIcons)icon, fontsize);
2023-03-20 16:00:54 +00:00
return "ok";
}
static std::string dispatchDismissNotify(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
int amount = -1;
if (vars.size() > 1) {
const auto AMOUNT = vars[1];
if (!isNumber(AMOUNT))
return "invalid arg 1";
try {
amount = std::stoi(AMOUNT);
} catch (std::exception& e) { return "invalid arg 1"; }
}
g_pHyprNotificationOverlay->dismissNotifications(amount);
return "ok";
}
static std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
std::string lockedStr = g_pSessionLockManager->isSessionLocked() ? "true" : "false";
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
lockedStr = std::format(R"#(
{{
"locked": {}
}}
)#",
lockedStr);
return lockedStr;
}
static std::string getDescriptions(eHyprCtlOutputFormat format, std::string request) {
std::string json = "{";
const auto& DESCS = g_pConfigManager->getAllDescriptions();
for (const auto& d : DESCS) {
json += d.jsonify() + ",\n";
}
json.pop_back();
json.pop_back();
json += "}\n";
return json;
}
static std::string submapRequest(eHyprCtlOutputFormat format, std::string request) {
std::string submap = g_pKeybindManager->getCurrentSubmap();
if (submap.empty())
submap = "default";
return format == FORMAT_JSON ? std::format("{{\"{}\"}}\n", escapeJSONStrings(submap)) : (submap + "\n");
}
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
if (g_pHyprOpenGL->initShaders())
return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
else
return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
}
CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
registerCommand(SHyprCtlCommand{"activeworkspace", true, activeWorkspaceRequest});
registerCommand(SHyprCtlCommand{"clients", true, clientsRequest});
registerCommand(SHyprCtlCommand{"kill", true, killRequest});
registerCommand(SHyprCtlCommand{"activewindow", true, activeWindowRequest});
registerCommand(SHyprCtlCommand{"layers", true, layersRequest});
registerCommand(SHyprCtlCommand{"version", true, versionRequest});
registerCommand(SHyprCtlCommand{"devices", true, devicesRequest});
registerCommand(SHyprCtlCommand{"splash", true, splashRequest});
registerCommand(SHyprCtlCommand{"cursorpos", true, cursorPosRequest});
registerCommand(SHyprCtlCommand{"binds", true, bindsRequest});
registerCommand(SHyprCtlCommand{"globalshortcuts", true, globalShortcutsRequest});
registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest});
registerCommand(SHyprCtlCommand{"animations", true, animationsRequest});
registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = true, .fn = reloadShaders});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
registerCommand(SHyprCtlCommand{"output", false, dispatchOutput});
registerCommand(SHyprCtlCommand{"dispatch", false, dispatchRequest});
registerCommand(SHyprCtlCommand{"keyword", false, dispatchKeyword});
registerCommand(SHyprCtlCommand{"setcursor", false, dispatchSetCursor});
registerCommand(SHyprCtlCommand{"getoption", false, dispatchGetOption});
registerCommand(SHyprCtlCommand{"decorations", false, decorationRequest});
registerCommand(SHyprCtlCommand{"[[BATCH]]", false, dispatchBatch});
startHyprCtlSocket();
}
CHyprCtl::~CHyprCtl() {
if (m_eventSource)
wl_event_source_remove(m_eventSource);
if (!m_socketPath.empty())
unlink(m_socketPath.c_str());
}
SP<SHyprCtlCommand> CHyprCtl::registerCommand(SHyprCtlCommand cmd) {
return m_commands.emplace_back(makeShared<SHyprCtlCommand>(cmd));
}
void CHyprCtl::unregisterCommand(const SP<SHyprCtlCommand>& cmd) {
std::erase(m_commands, cmd);
}
std::string CHyprCtl::getReply(std::string request) {
auto format = eHyprCtlOutputFormat::FORMAT_NORMAL;
bool reloadAll = false;
m_currentRequestParams.all = false;
m_currentRequestParams.sysInfoConfig = false;
2022-07-12 16:34:28 -06:00
// process flags for non-batch requests
if (!request.starts_with("[[BATCH]]") && request.contains("/")) {
2022-07-13 15:48:47 +02:00
long unsigned int sepIndex = 0;
2022-07-12 16:34:28 -06:00
for (const auto& c : request) {
if (c == '/') { // stop at separator
break;
}
// after whitespace assume the first word as a keyword,
// so its value can have slashes (e.g., a path)
if (c == ' ') {
sepIndex = request.size();
break;
}
2022-07-12 16:34:28 -06:00
sepIndex++;
2022-09-25 20:07:48 +02:00
2022-07-12 16:34:28 -06:00
if (c == 'j')
format = eHyprCtlOutputFormat::FORMAT_JSON;
else if (c == 'r')
2024-02-24 14:01:58 +00:00
reloadAll = true;
else if (c == 'a')
m_currentRequestParams.all = true;
else if (c == 'c')
m_currentRequestParams.sysInfoConfig = true;
}
2022-07-12 16:34:28 -06:00
if (sepIndex < request.size())
request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string
}
2024-02-24 14:01:58 +00:00
std::string result = "";
// parse exact cmds first, then non-exact.
for (auto const& cmd : m_commands) {
if (!cmd->exact)
continue;
2024-02-24 14:01:58 +00:00
if (cmd->name == request) {
result = cmd->fn(format, request);
break;
}
}
2024-02-24 14:01:58 +00:00
if (result.empty())
for (auto const& cmd : m_commands) {
2024-02-24 14:01:58 +00:00
if (cmd->exact)
continue;
2024-02-24 14:01:58 +00:00
if (request.starts_with(cmd->name)) {
result = cmd->fn(format, request);
break;
}
}
if (result.empty())
return "unknown request";
if (reloadAll) {
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
2024-02-24 14:01:58 +00:00
g_pInputManager->setKeyboardLayout(); // update kb layout
g_pInputManager->setPointerConfigs(); // update mouse cfgs
g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs
g_pInputManager->setTabletConfigs(); // update tablets
static auto PLAYOUT = CConfigValue<std::string>("general:layout");
2024-02-24 14:01:58 +00:00
g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout
g_pHyprOpenGL->m_bReloadScreenShader = true;
for (auto& [m, rd] : g_pHyprOpenGL->m_mMonitorRenderResources) {
rd.blurFBDirty = true;
}
for (auto const& w : g_pCompositor->m_windows) {
if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible())
continue;
w->updateDynamicRules();
g_pCompositor->updateWindowAnimatedDecorationValues(w);
}
for (auto const& m : g_pCompositor->m_monitors) {
g_pHyprRenderer->damageMonitor(m);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
2024-02-24 14:01:58 +00:00
}
}
2022-04-29 19:44:09 +02:00
2024-02-24 14:01:58 +00:00
return result;
2022-04-29 19:44:09 +02:00
}
std::string CHyprCtl::makeDynamicCall(const std::string& input) {
return getReply(input);
}
static bool successWrite(int fd, const std::string& data, bool needLog = true) {
if (write(fd, data.c_str(), data.length()) > 0)
return true;
if (errno == EAGAIN)
return true;
if (needLog)
Debug::log(ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
return false;
}
static void runWritingDebugLogThread(const int conn) {
using namespace std::chrono_literals;
Debug::log(LOG, "In followlog thread, got connection, start writing: {}", conn);
//will be finished, when reading side close connection
std::thread([conn]() {
while (Debug::SRollingLogFollow::get().isRunning()) {
if (Debug::SRollingLogFollow::get().isEmpty(conn)) {
std::this_thread::sleep_for(1000ms);
continue;
}
auto line = Debug::SRollingLogFollow::get().getLog(conn);
if (!successWrite(conn, line))
// We cannot write, when connection is closed. So thread will successfully exit by itself
break;
std::this_thread::sleep_for(100ms);
}
close(conn);
Debug::SRollingLogFollow::get().stopFor(conn);
}).detach();
}
static bool isFollowUpRollingLogRequest(const std::string& request) {
return request.contains("rollinglog") && request.contains("f");
}
static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
2022-09-10 21:21:28 +02:00
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
return 0;
if (!g_pHyprCtl->m_socketFD.isValid())
core: begin using CFileDescriptor from hyprutils (#9122) * config: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * hyprctl: make fd use CFileDescriptor make use of the new hyprutils CFileDescriptor instead of manual FD handling. * ikeyboard: make fd use CFileDescriptor make use of the new CFileDescriptor instead of manual FD handling, also in sendKeymap remove dead code, it already early returns if keyboard isnt valid, and dont try to close the FD that ikeyboard owns. * core: make SHMFile functions use CFileDescriptor make SHMFile misc functions use CFileDescriptor and its associated usage in dmabuf and keyboard. * core: make explicit sync use CFileDescriptor begin using CFileDescriptor in explicit sync and its timelines and eglsync usage in opengl, there is still a bit left with manual handling that requires future aquamarine change aswell. * eventmgr: make fd and sockets use CFileDescriptor make use of the hyprutils CFileDescriptor instead of manual FD and socket handling and closing. * eventloopmgr: make timerfd use CFileDescriptor make the timerfd use CFileDescriptor instead of manual fd handling * opengl: make gbm fd use CFileDescriptor make the gbm rendernode fd use CFileDescriptor instead of manual fd handling * core: make selection source/offer use CFileDescriptor make data selection source and offers use CFileDescriptor and its associated use in xwm and protocols * protocols: convert protocols fd to CFileDescriptor make most fd handling use CFileDescriptor in protocols * shm: make SHMPool use CfileDescriptor make SHMPool use CFileDescriptor instead of manual fd handling. * opengl: duplicate fd with CFileDescriptor duplicate fenceFD with CFileDescriptor duplicate instead. * xwayland: make sockets and fds use CFileDescriptor instead of manual opening/closing make sockets and fds use CFileDescriptor * keybindmgr: make sockets and fds use CFileDescriptor make sockets and fds use CFileDescriptor instead of manual handling.
2025-01-30 12:30:12 +01:00
return 0;
sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress);
2022-09-10 21:21:28 +02:00
const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_socketFD.get(), (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
2022-09-10 21:21:28 +02:00
std::array<char, 1024> readBuffer;
2022-09-10 21:21:28 +02:00
// try to get creds
CRED_T creds;
uint32_t len = sizeof(creds);
if (getsockopt(ACCEPTEDCONNECTION, CRED_LVL, CRED_OPT, &creds, &len) == -1)
Debug::log(ERR, "Hyprctl: failed to get peer creds");
else {
g_pHyprCtl->m_currentRequestParams.pid = creds.CRED_PID;
Debug::log(LOG, "Hyprctl: new connection from pid {}", creds.CRED_PID);
}
//
pollfd pollfds[1] = {
{
.fd = ACCEPTEDCONNECTION,
.events = POLLIN,
},
};
int ret = poll(pollfds, 1, 5000);
2023-07-21 17:20:23 +02:00
if (ret <= 0) {
2023-07-21 17:20:23 +02:00
close(ACCEPTEDCONNECTION);
return 0;
}
std::string request;
while (true) {
readBuffer.fill(0);
auto messageSize = read(ACCEPTEDCONNECTION, readBuffer.data(), 1023);
if (messageSize < 1)
break;
std::string recvd = readBuffer.data();
request += recvd;
if (messageSize < 1023)
break;
}
2022-04-21 22:00:03 +02:00
std::string reply = "";
try {
reply = g_pHyprCtl->getReply(request);
2022-04-21 22:00:03 +02:00
} catch (std::exception& e) {
2023-09-06 12:51:36 +02:00
Debug::log(ERR, "Error in request: {}", e.what());
2022-04-21 22:00:03 +02:00
reply = "Err: " + std::string(e.what());
}
if (g_pHyprCtl->m_currentRequestParams.pendingPromise) {
// we have a promise pending
g_pHyprCtl->m_currentRequestParams.pendingPromise->then([ACCEPTEDCONNECTION, request](SP<CPromiseResult<std::string>> result) {
const auto RES = result->hasError() ? result->error() : result->result();
successWrite(ACCEPTEDCONNECTION, RES);
2022-04-21 22:00:03 +02:00
// No rollinglog or ensureMonitor here. These are only for plugins for now.
close(ACCEPTEDCONNECTION);
});
g_pHyprCtl->m_currentRequestParams.pendingPromise.reset();
} else {
successWrite(ACCEPTEDCONNECTION, reply);
if (isFollowUpRollingLogRequest(request)) {
Debug::log(LOG, "Followup rollinglog request received. Starting thread to write to socket.");
Debug::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION);
runWritingDebugLogThread(ACCEPTEDCONNECTION);
Debug::log(LOG, Debug::SRollingLogFollow::get().debugInfo());
} else
close(ACCEPTEDCONNECTION);
if (g_pConfigManager->m_wantsMonitorReload)
g_pConfigManager->ensureMonitorStatus();
g_pHyprCtl->m_currentRequestParams.pid = 0;
}
2022-04-21 22:00:03 +02:00
2022-09-10 21:21:28 +02:00
return 0;
2022-04-21 22:00:03 +02:00
}
void CHyprCtl::startHyprCtlSocket() {
m_socketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};
2022-03-21 18:29:41 +01:00
if (!m_socketFD.isValid()) {
2022-09-10 21:21:28 +02:00
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
return;
}
2022-03-21 18:29:41 +01:00
2022-09-10 21:21:28 +02:00
sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX};
2022-03-21 18:29:41 +01:00
m_socketPath = g_pCompositor->m_instancePath + "/.socket.sock";
2022-03-21 18:29:41 +01:00
strcpy(SERVERADDRESS.sun_path, m_socketPath.c_str());
2022-03-21 18:29:41 +01:00
if (bind(m_socketFD.get(), (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
return;
}
2022-03-21 18:29:41 +01:00
2022-09-10 21:21:28 +02:00
// 10 max queued.
listen(m_socketFD.get(), 10);
2022-03-22 16:54:45 +01:00
Debug::log(LOG, "Hypr socket started at {}", m_socketPath);
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_socketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
2022-08-04 16:25:56 -07:00
}