Merge branch 'hyprwm:main' into main

This commit is contained in:
function i_use_lfs_btw() 2026-05-01 16:55:34 +03:00 committed by GitHub
commit f4249c192b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 492 additions and 391 deletions

View file

@ -588,6 +588,28 @@ TEST_CASE(issue14038) {
// this should not crash hyprland. If we are alive, we good.
}
TEST_CASE(specialFloatRecenters) {
if (!spawnKitty("kitty_special_float_recenter"))
FAIL_TEST("Could not spawn kitty");
OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set', window = 'class:kitty_special_float_recenter' })"));
OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 10, y = 10, window = 'class:kitty_special_float_recenter' })"));
OK(getFromSocket("/dispatch hl.dsp.window.move({ workspace = 'special:recenter', follow = false, window = 'class:kitty_special_float_recenter' })"));
OK(getFromSocket("/dispatch hl.dsp.window.move({ x = 50000, y = 50000, window = 'class:kitty_special_float_recenter' })"));
OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('recenter')"));
OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_special_float_recenter' })"));
const auto active = getFromSocket("/activewindow");
EXPECT_CONTAINS(active, "class: kitty_special_float_recenter");
EXPECT_CONTAINS(active, "size: 10,10");
EXPECT_CONTAINS(active, "at: 955,535");
OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('recenter')"));
Tests::killAllWindows();
ASSERT(Tests::windowCount(), 0);
}
// TODO: decompose this into multiple test cases
TEST_CASE(windows) {
// test on workspace "window"

View file

@ -793,6 +793,7 @@ local __HL_API = {}
---@field force_renderer_reload fun(...): any
---@field global fun(...): any
---@field layout fun(...): any
---@field no_op fun(...): any
---@field pass fun(...): any
---@field send_key_state fun(...): any
---@field send_shortcut fun(...): any

View file

@ -10,6 +10,7 @@
#include "desktop/history/WorkspaceHistoryTracker.hpp"
#include "desktop/view/Group.hpp"
#include "helpers/Splashes.hpp"
#include "helpers/SystemInfo.hpp"
#include "config/ConfigValue.hpp"
#include "config/legacy/ConfigManager.hpp"
#include "config/shared/inotify/ConfigWatcher.hpp"
@ -232,26 +233,7 @@ CCompositor::CCompositor(bool onlyConfig) : m_onlyConfigVerification(onlyConfig)
Log::logger->initIS(m_instancePath);
Log::logger->log(Log::DEBUG, "Instance Signature: {}", m_instanceSignature);
Log::logger->log(Log::DEBUG, "Runtime directory: {}", m_instancePath);
Log::logger->log(Log::DEBUG, "Hyprland PID: {}", m_hyprlandPID);
Log::logger->log(Log::DEBUG, "===== SYSTEM INFO: =====");
logSystemInfo();
Log::logger->log(Log::DEBUG, "========================");
Log::logger->log(Log::DEBUG, "\n\n"); // pad
Log::logger->log(Log::INFO, "If you are crashing, or encounter any bugs, please consult https://wiki.hypr.land/Crashes-and-Bugs/\n\n");
setRandomSplash();
Log::logger->log(Log::DEBUG, "\nCurrent splash: {}\n\n", m_currentSplash);
bumpNofile();
}
@ -365,6 +347,16 @@ void CCompositor::initServer(std::string socketName, int socketFd) {
m_initialized = true;
Log::logger->log(Log::DEBUG, "Instance Signature: {}", m_instanceSignature);
Log::logger->log(Log::DEBUG, "Runtime directory: {}", m_instancePath);
Log::logger->log(Log::DEBUG, "Hyprland PID: {}", m_hyprlandPID);
Log::logger->log(Log::DEBUG, "===== SYSTEM INFO: =====");
Log::logger->log(Log::DEBUG, "{}", Helpers::SystemInfo::getSystemInfo());
Log::logger->log(Log::DEBUG, "========================");
Log::logger->log(Log::DEBUG, "\n\n"); // pad
Log::logger->log(Log::INFO, "If you are crashing, or encounter any bugs, please consult https://wiki.hypr.land/Crashes-and-Bugs/\n\n");
Log::logger->log(Log::DEBUG, "\nCurrent splash: {}\n\n", m_currentSplash);
m_drm.fd = m_aqBackend->drmFD();
Log::logger->log(Log::DEBUG, "Running on DRMFD: {}", m_drm.fd);
@ -1699,7 +1691,7 @@ WORKSPACEID CCompositor::getNextAvailableNamedWorkspace() {
// Give priority to persistent workspaces to avoid any conflicts between them.
for (auto const& rule : Config::workspaceRuleMgr()->getAllWorkspaceRules()) {
if (!rule.m_isPersistent)
if (!rule.m_isPersistent.value_or(false))
continue;
if (rule.m_workspaceId < -1 && rule.m_workspaceId < lowest)
lowest = rule.m_workspaceId;
@ -3096,7 +3088,7 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<Config::CW
std::vector<PHLWORKSPACE> persistentFound;
for (const auto& rule : rules) {
if (!rule.m_isPersistent)
if (!rule.m_isPersistent.value_or(false))
continue;
PHLWORKSPACE PWORKSPACE = nullptr;

View file

@ -1093,6 +1093,15 @@ static int hlFocus(lua_State* L) {
return Internal::configError(L, "hl.focus: unrecognized arguments. Expected one of: direction, monitor, window, urgent_or_last, last");
}
static int dsp_noop(lua_State* L) {
return 0;
}
static int hlNoop(lua_State* L) {
lua_pushcclosure(L, dsp_noop, 0);
return 1;
}
static int dsp_toggleSpecial(lua_State* L) {
std::string name = lua_isnil(L, lua_upvalueindex(1)) ? "" : lua_tostring(L, lua_upvalueindex(1));
const auto& [workspaceID, workspaceName, isAutoID] = getWorkspaceIDNameFromString("special:" + name);
@ -1266,6 +1275,7 @@ void Internal::registerDispatcherBindings(lua_State* L) {
Internal::setFn(L, "force_renderer_reload", hlForceRendererReload);
Internal::setFn(L, "force_idle", hlForceIdle);
Internal::setFn(L, "focus", hlFocus);
Internal::setFn(L, "no_op", hlNoop);
}
lua_setfield(L, -2, "dsp");

View file

@ -3,7 +3,7 @@
using namespace Config;
void CWorkspaceRule::mergeLeft(const CWorkspaceRule& other) {
if (m_monitor.empty())
if (!other.m_monitor.empty())
m_monitor = other.m_monitor;
if (m_workspaceString.empty())
m_workspaceString = other.m_workspaceString;
@ -12,10 +12,10 @@ void CWorkspaceRule::mergeLeft(const CWorkspaceRule& other) {
if (m_workspaceId == WORKSPACE_INVALID)
m_workspaceId = other.m_workspaceId;
if (other.m_isDefault)
m_isDefault = true;
if (other.m_isPersistent)
m_isPersistent = true;
if (other.m_isDefault.has_value())
m_isDefault = other.m_isDefault;
if (other.m_isPersistent.has_value())
m_isPersistent = other.m_isPersistent;
if (other.m_gapsIn.has_value())
m_gapsIn = other.m_gapsIn;
if (other.m_gapsOut.has_value())

View file

@ -27,8 +27,8 @@ namespace Config {
std::string m_workspaceString = "";
std::string m_workspaceName = "";
WORKSPACEID m_workspaceId = -1;
bool m_isDefault = false;
bool m_isPersistent = false;
std::optional<bool> m_isDefault;
std::optional<bool> m_isPersistent;
std::optional<CCssGapData> m_gapsIn;
std::optional<CCssGapData> m_gapsOut;
std::optional<CCssGapData> m_floatGaps = m_gapsOut;

View file

@ -55,7 +55,7 @@ std::string CWorkspaceRuleManager::getDefaultWorkspaceFor(const std::string& nam
if (!other->m_enabled)
continue;
if (other->m_isDefault) {
if (other->m_isDefault.value_or(false)) {
if (other->m_monitor == name)
return other->m_workspaceString;
if (other->m_monitor.starts_with("desc:")) {

View file

@ -49,9 +49,10 @@ using namespace Hyprutils::OS;
#include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "debug/log/RollingLogFollow.hpp"
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
#include "../debug/log/RollingLogFollow.hpp"
#include "../config/ConfigManager.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../helpers/SystemInfo.hpp"
#include "../desktop/view/LayerSurface.hpp"
#include "../desktop/view/Group.hpp"
#include "../desktop/rule/Engine.hpp"
@ -507,8 +508,8 @@ static std::string getWorkspaceRuleData(const Config::CWorkspaceRule& r, eHyprCt
const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; };
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
const std::string monitor = r.m_monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.m_monitor));
const std::string default_ = sc<bool>(r.m_isDefault) ? std::format(",\n \"default\": {}", boolToString(r.m_isDefault)) : "";
const std::string persistent = sc<bool>(r.m_isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.m_isPersistent)) : "";
const std::string default_ = sc<bool>(r.m_isDefault) ? std::format(",\n \"default\": {}", boolToString(r.m_isDefault.value())) : "";
const std::string persistent = sc<bool>(r.m_isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.m_isPersistent.value())) : "";
const std::string gapsIn = sc<bool>(r.m_gapsIn) ?
std::format(",\n \"gapsIn\": [{}, {}, {}, {}]", r.m_gapsIn.value().m_top, r.m_gapsIn.value().m_right, r.m_gapsIn.value().m_bottom, r.m_gapsIn.value().m_left) :
"";
@ -531,8 +532,8 @@ static std::string getWorkspaceRuleData(const Config::CWorkspaceRule& r, eHyprCt
return result;
} else {
const std::string monitor = std::format("\tmonitor: {}\n", r.m_monitor.empty() ? "<unset>" : escapeJSONStrings(r.m_monitor));
const std::string default_ = std::format("\tdefault: {}\n", sc<bool>(r.m_isDefault) ? boolToString(r.m_isDefault) : "<unset>");
const std::string persistent = std::format("\tpersistent: {}\n", sc<bool>(r.m_isPersistent) ? boolToString(r.m_isPersistent) : "<unset>");
const std::string default_ = std::format("\tdefault: {}\n", sc<bool>(r.m_isDefault) ? boolToString(r.m_isDefault.value()) : "<unset>");
const std::string persistent = std::format("\tpersistent: {}\n", sc<bool>(r.m_isPersistent) ? boolToString(r.m_isPersistent.value()) : "<unset>");
const std::string gapsIn = sc<bool>(r.m_gapsIn) ?
std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.m_gapsIn.value().m_top), std::to_string(r.m_gapsIn.value().m_right),
std::to_string(r.m_gapsIn.value().m_bottom), std::to_string(r.m_gapsIn.value().m_left)) :
@ -1061,193 +1062,19 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request
}
std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
return Helpers::SystemInfo::getVersion(format);
}
auto commitMsg = trim(GIT_COMMIT_MESSAGE);
std::ranges::replace(commitMsg, '#', ' ');
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n"
"Date: {}\n"
"Tag: {}, commits: {}\n",
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS);
result += "\n";
result += getBuiltSystemLibraryNames();
result += "\n";
result += "Version ABI string: ";
result += __hyprland_api_get_hash();
result += "\n";
#if (!ISDEBUG && !defined(NO_XWAYLAND) && !defined(BUILT_WITH_NIX))
result += "no flags were set\n";
#else
result += "flags set:\n";
#if ISDEBUG
result += "debug\n";
#endif
#ifdef NO_XWAYLAND
result += "no xwayland\n";
#endif
#ifdef BUILT_WITH_NIX
result += "nix\n";
#endif
#endif
return result;
} else {
std::string result = std::format(
R"#({{
"branch": "{}",
"commit": "{}",
"version": "{}",
"dirty": {},
"commit_message": "{}",
"commit_date": "{}",
"tag": "{}",
"commits": "{}",
"buildAquamarine": "{}",
"buildHyprlang": "{}",
"buildHyprutils": "{}",
"buildHyprcursor": "{}",
"buildHyprgraphics": "{}",
"systemAquamarine": "{}",
"systemHyprlang": "{}",
"systemHyprutils": "{}",
"systemHyprcursor": "{}",
"systemHyprgraphics": "{}",
"abiHash": "{}",
"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, getSystemLibraryVersion("aquamarine"),
getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics"),
__hyprland_api_get_hash());
#if ISDEBUG
result += "\"debug\",";
#endif
#ifdef NO_XWAYLAND
result += "\"no xwayland\",";
#endif
#ifdef BUILT_WITH_NIX
result += "\"nix\",";
#endif
trimTrailingComma(result);
result += "]\n}";
return result;
}
return ""; // make the compiler happy
static std::string statusRequest(eHyprCtlOutputFormat format, std::string request) {
return Helpers::SystemInfo::getStatus(format);
}
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) {
std::string result = versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "");
static auto check = [](bool y) -> std::string { return y ? "✔️" : ""; };
static auto backend = [](Aquamarine::eBackendType t) -> std::string {
switch (t) {
case Aquamarine::AQ_BACKEND_DRM: return "drm";
case Aquamarine::AQ_BACKEND_HEADLESS: return "headless";
case Aquamarine::AQ_BACKEND_WAYLAND: return "wayland";
default: break;
}
return "?";
};
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";
result += getBuiltSystemLibraryNames();
result += "\n";
result += "\n\n";
#if defined(__DragonFly__) || defined(__FreeBSD__)
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"; }
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep -E '(VGA|Display|3D)'");
#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";
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";
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";
if (g_pHyprOpenGL) {
result += std::format("\nExplicit sync: {}", g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext ? "supported" : "missing");
result += std::format("\nGL ver: {}", g_pHyprOpenGL->m_eglContextVersion == CHyprOpenGLImpl::EGL_CONTEXT_GLES_3_2 ? "3.2" : "3.0");
}
if (g_pCompositor) {
result += std::format("\nBackend: {}", g_pCompositor->m_aqBackend->hasSession() ? "drm" : "sessionless");
result += "\n\nMonitor info:";
for (const auto& m : g_pCompositor->m_monitors) {
result += std::format("\n\tPanel {}: {}x{}, {} {} {} {} -> backend {}\n\t\texplicit {}\n\t\tedid:\n\t\t\thdr {}\n\t\t\tchroma {}\n\t\t\tbt2020 {}\n\t\tvrr capable "
"{}\n\t\tnon-desktop {}\n\t\t",
m->m_name, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_output->name, m->m_output->make, m->m_output->model, m->m_output->serial,
backend(m->m_output->getBackend()->type()), check(m->m_output->supportsExplicit), check(m->m_output->parsedEDID.hdrMetadata.has_value()),
check(m->m_output->parsedEDID.chromaticityCoords.has_value()), check(m->m_output->parsedEDID.supportsBT2020), check(m->m_output->vrrCapable),
check(m->m_output->nonDesktop));
}
}
auto result = Helpers::SystemInfo::getSystemInfo();
if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.sysInfoConfig) {
result += "\n======Config-Start======\n";
result += "\n\n======Config-Start======\n";
result += Config::mgr()->getConfigString();
result += "\n======Config-End========\n";
}
@ -2097,43 +1924,6 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques
return format == FORMAT_JSON ? std::format("\"{}\"\n", escapeJSONStrings(submap)) : (submap + "\n");
}
static std::string statusRequest(eHyprCtlOutputFormat format, std::string request) {
Aquamarine::eBackendType backendType = Aquamarine::eBackendType::AQ_BACKEND_NULL;
for (const auto& i : g_pCompositor->m_aqBackend->getImplementations()) {
if (i->type() == Aquamarine::eBackendType::AQ_BACKEND_NULL || i->type() == Aquamarine::eBackendType::AQ_BACKEND_HEADLESS)
continue;
backendType = i->type();
break;
}
std::string backendStr;
switch (backendType) {
case Aquamarine::AQ_BACKEND_DRM: backendStr = "drm"; break;
case Aquamarine::AQ_BACKEND_WAYLAND: backendStr = "wayland"; break;
default: backendStr = "error"; break;
}
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(R"#(
{{
"configProvider": "{}",
"backend": "{}"
}}
)#",
Config::typeToString(Config::mgr()->type()), backendStr);
}
return std::format(R"#(
configProvider: {}
backend: {}
)#",
Config::typeToString(Config::mgr()->type()), backendStr);
}
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');

View file

@ -52,7 +52,7 @@ void CWorkspace::init(PHLWORKSPACE self) {
m_inert = false;
const auto WORKSPACERULE = Config::workspaceRuleMgr()->getWorkspaceRuleFor(self).value_or(Config::CWorkspaceRule{});
setPersistent(WORKSPACERULE.m_isPersistent);
setPersistent(WORKSPACERULE.m_isPersistent.value_or(false));
if (self->m_wasCreatedEmpty)
if (auto cmd = WORKSPACERULE.m_onCreatedEmptyRunCmd)
@ -517,9 +517,9 @@ void CWorkspace::rename(const std::string& name) {
m_name = name;
const auto WORKSPACERULE = Config::workspaceRuleMgr()->getWorkspaceRuleFor(m_self.lock()).value_or(Config::CWorkspaceRule{});
setPersistent(WORKSPACERULE.m_isPersistent);
setPersistent(WORKSPACERULE.m_isPersistent.value_or(false));
if (WORKSPACERULE.m_isPersistent)
if (WORKSPACERULE.m_isPersistent.value_or(false))
g_pCompositor->ensurePersistentWorkspacesPresent(std::vector<Config::CWorkspaceRule>{WORKSPACERULE}, m_self.lock());
g_pEventManager->postEvent({.event = "renameworkspace", .data = std::to_string(m_id) + "," + m_name});

View file

@ -523,54 +523,6 @@ std::string execAndGet(const char* cmd) {
return proc.stdOut();
}
void logSystemInfo() {
struct utsname unameInfo;
uname(&unameInfo);
Log::logger->log(Log::DEBUG, "System name: {}", std::string{unameInfo.sysname});
Log::logger->log(Log::DEBUG, "Node name: {}", std::string{unameInfo.nodename});
Log::logger->log(Log::DEBUG, "Release: {}", std::string{unameInfo.release});
Log::logger->log(Log::DEBUG, "Version: {}", std::string{unameInfo.version});
Log::logger->log(Log::DEBUG, "\n");
#if defined(__DragonFly__) || defined(__FreeBSD__)
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"; }
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep -E '(VGA|Display|3D)'");
#endif
Log::logger->log(Log::DEBUG, "GPU information:\n{}\n", GPUINFO);
if (GPUINFO.contains("NVIDIA")) {
Log::logger->log(Log::WARN, "Warning: you're using an NVIDIA GPU. Make sure you follow the instructions on the wiki if anything is amiss.\n");
}
// log etc
Log::logger->log(Log::DEBUG, "os-release:");
Log::logger->log(Log::DEBUG, "{}", NFsUtils::readFileAsString("/etc/os-release").value_or("error"));
}
int64_t getPPIDof(int64_t pid) {
#if defined(KERN_PROC_PID)
int mib[] = {

View file

@ -27,7 +27,6 @@ bool isDirection(const char&);
SWorkspaceIDName getWorkspaceIDNameFromString(const std::string&);
std::optional<std::string> cleanCmdForWorkspace(const std::string&, std::string);
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2);
void logSystemInfo();
std::string execAndGet(const char*);
int64_t getPPIDof(int64_t pid);
std::expected<int64_t, std::string> configStringToInt(const std::string&);

View file

@ -1564,7 +1564,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
if (VECNOTINRECT(MIDDLE, PMONFROMMIDDLE->m_position.x, PMONFROMMIDDLE->m_position.y, PMONFROMMIDDLE->m_position.x + PMONFROMMIDDLE->m_size.x,
PMONFROMMIDDLE->m_position.y + PMONFROMMIDDLE->m_size.y)) {
// not on any monitor, center
pos = middle() / 2.f - w->m_realSize->goal() / 2.f;
pos = middle() - w->m_realSize->goal() / 2.f;
} else
pos = pos - PMONFROMMIDDLE->m_position + m_position;

View file

@ -58,49 +58,22 @@ namespace NSplashes {
"Thanks ThatOneCalculator!",
"The AUR packages always work, except for the times they don't.",
"Funny animation compositor woo",
"3 years!",
"4 years!",
"Beauty will save the world", // 4th ricing comp winner - zacoons' choice
// music reference / quote section
"J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!",
"Wir sind schon sehr lang zusammen...",
"I see a red door and I want it painted black.",
"Take on me, take me on...",
"You spin me right round baby right round",
"Stayin' alive, stayin' alive",
"Say no way, say no way ya, no way!",
"Ground control to Major Tom...",
"Alors on danse",
"And all that I can see, is just a yellow lemon tree.",
"Got a one-way ticket to the blues",
"Is this the real life, is this just fantasy",
"What's in your head, in your head?",
"We're all living in America, America, America.",
"I'm still standing, better than I ever did",
"Here comes the sun, bringing you love and shining on everyone",
"Two trailer park girls go round the outside",
"With the lights out, it's less dangerous",
"Here we go back, this is the moment, tonight is the night",
"Now you're just somebody that I used to know...",
"Black bird, black moon, black sky",
"Some legends are told, some turn to dust or to gold",
"Your brain gets smart, but your head gets dumb.",
"Save your mercy for someone who needs it more",
"You're gonna hear my voice when I shout it out loud",
"Ding ding pch n daa, bam-ba-ba-re-bam baram bom bom baba-bam-bam-bommm",
"Súbeme la radio que esta es mi canción",
"I'm beggin', beggin' you",
"Never gonna let you down (I am trying!)",
"Hier kommt die Sonne",
"Kickstart my heart, give it a start",
"Fear of the dark, I have a constant fear that something's always near",
"Komm mit, reih dich ein.",
"I wish I had an angel for one moment of love",
"We're the children of the dark",
"You float like a feather, in a beautiful world",
"Demons come at night and they bring the end",
"All I wanna say is that they don't really care about us",
"Has he lost his mind? Can he see or is he blind?",
"Configration",
"RIP hyprlang",
"better call vaxry",
"i hypr therefore i land",
"five. hundred. config errors.",
"bundled with anime girls for your convenience",
"now with 200% more hypr and land",
"daily dose of rice",
"Removed Herobrine",
"You should try quickshell!",
"He was an X11 boy, she was a Wayland girl...",
"How do I exit vim????",
"Now with lua, it might as well be awesome.",
"Have you ran your daily fastfetch yet?"
// clang-format on
};

257
src/helpers/SystemInfo.cpp Normal file
View file

@ -0,0 +1,257 @@
#include "SystemInfo.hpp"
#include "../Compositor.hpp"
#include "../version.h"
#include "../plugins/PluginAPI.hpp"
#include "../plugins/PluginSystem.hpp"
#include "../render/OpenGL.hpp"
#include "../config/ConfigManager.hpp"
#include <hyprutils/string/String.hpp>
#include <sys/utsname.h>
#include <fstream>
#include <sstream>
#include <format>
#include <algorithm>
using namespace Helpers::SystemInfo;
using namespace Helpers;
using namespace Hyprutils::String;
using namespace Render::GL;
static void trimTrailingComma(std::string& str) {
if (!str.empty() && str.back() == ',')
str.pop_back();
}
std::string SystemInfo::getStatus(eHyprCtlOutputFormat fmt) {
Aquamarine::eBackendType backendType = Aquamarine::eBackendType::AQ_BACKEND_NULL;
for (const auto& i : g_pCompositor->m_aqBackend->getImplementations()) {
if (i->type() == Aquamarine::eBackendType::AQ_BACKEND_NULL || i->type() == Aquamarine::eBackendType::AQ_BACKEND_HEADLESS)
continue;
backendType = i->type();
break;
}
std::string backendStr;
switch (backendType) {
case Aquamarine::AQ_BACKEND_DRM: backendStr = "drm"; break;
case Aquamarine::AQ_BACKEND_WAYLAND: backendStr = "wayland"; break;
default: backendStr = "error"; break;
}
if (fmt == eHyprCtlOutputFormat::FORMAT_JSON) {
return std::format(R"#(
{{
"configProvider": "{}",
"backend": "{}"
}}
)#",
Config::typeToString(Config::mgr()->type()), backendStr);
}
return std::format(R"#(
configProvider: {}
backend: {}
)#",
Config::typeToString(Config::mgr()->type()), backendStr);
}
std::string SystemInfo::getVersion(eHyprCtlOutputFormat fmt) {
auto commitMsg = trim(GIT_COMMIT_MESSAGE);
std::ranges::replace(commitMsg, '#', ' ');
if (fmt == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n"
"Date: {}\n"
"Tag: {}, commits: {}\n",
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS);
result += "\n";
result += getBuiltSystemLibraryNames();
result += "\n";
result += "Version ABI string: ";
result += __hyprland_api_get_hash();
result += "\n";
#if (!ISDEBUG && !defined(NO_XWAYLAND) && !defined(BUILT_WITH_NIX))
result += "no flags were set\n";
#else
result += "flags set:\n";
#if ISDEBUG
result += "debug\n";
#endif
#ifdef NO_XWAYLAND
result += "no xwayland\n";
#endif
#ifdef BUILT_WITH_NIX
result += "nix\n";
#endif
#endif
return result;
} else {
std::string result = std::format(
R"#({{
"branch": "{}",
"commit": "{}",
"version": "{}",
"dirty": {},
"commit_message": "{}",
"commit_date": "{}",
"tag": "{}",
"commits": "{}",
"buildAquamarine": "{}",
"buildHyprlang": "{}",
"buildHyprutils": "{}",
"buildHyprcursor": "{}",
"buildHyprgraphics": "{}",
"systemAquamarine": "{}",
"systemHyprlang": "{}",
"systemHyprutils": "{}",
"systemHyprcursor": "{}",
"systemHyprgraphics": "{}",
"abiHash": "{}",
"flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (GIT_DIRTY == std::string_view{"dirty"} ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION, getSystemLibraryVersion("aquamarine"),
getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics"),
__hyprland_api_get_hash());
#if ISDEBUG
result += "\"debug\",";
#endif
#ifdef NO_XWAYLAND
result += "\"no xwayland\",";
#endif
#ifdef BUILT_WITH_NIX
result += "\"nix\",";
#endif
trimTrailingComma(result);
result += "]\n}";
return result;
}
return ""; // make the compiler happy
}
std::string SystemInfo::getSystemInfo() {
std::string result = getVersion(eHyprCtlOutputFormat::FORMAT_NORMAL);
static auto check = [](bool y) -> std::string { return y ? "✔️" : ""; };
static auto backend = [](Aquamarine::eBackendType t) -> std::string {
switch (t) {
case Aquamarine::AQ_BACKEND_DRM: return "drm";
case Aquamarine::AQ_BACKEND_HEADLESS: return "headless";
case Aquamarine::AQ_BACKEND_WAYLAND: return "wayland";
default: break;
}
return "?";
};
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";
result += getBuiltSystemLibraryNames();
result += "\n";
result += "\n\n";
#if defined(__DragonFly__) || defined(__FreeBSD__)
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"; }
#else
const std::string GPUINFO = execAndGet("lspci -vnn | grep -E '(VGA|Display|3D)'");
#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";
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";
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";
if (g_pHyprOpenGL) {
result += std::format("\nExplicit sync: {}", g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext ? "supported" : "missing");
result += std::format("\nGL ver: {}", g_pHyprOpenGL->m_eglContextVersion == CHyprOpenGLImpl::EGL_CONTEXT_GLES_3_2 ? "3.2" : "3.0");
}
if (g_pCompositor) {
result += std::format("\nBackend: {}", g_pCompositor->m_aqBackend->hasSession() ? "drm" : "sessionless");
result += "\n\nMonitor info:";
for (const auto& m : g_pCompositor->m_monitors) {
result += std::format("\n\tPanel {}: {}x{}, {} {} {} {} -> backend {}\n\t\texplicit {}\n\t\tedid:\n\t\t\thdr {}\n\t\t\tchroma {}\n\t\t\tbt2020 {}\n\t\tvrr capable "
"{}\n\t\tnon-desktop {}\n\t\t",
m->m_name, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_output->name, m->m_output->make, m->m_output->model, m->m_output->serial,
backend(m->m_output->getBackend()->type()), check(m->m_output->supportsExplicit), check(m->m_output->parsedEDID.hdrMetadata.has_value()),
check(m->m_output->parsedEDID.chromaticityCoords.has_value()), check(m->m_output->parsedEDID.supportsBT2020), check(m->m_output->vrrCapable),
check(m->m_output->nonDesktop));
}
}
result += "\n\nState:\n";
result += getStatus(FORMAT_NORMAL);
result += "\n\n";
return result;
}

View file

@ -0,0 +1,11 @@
#pragma once
#include <string>
#include "../SharedDefs.hpp"
namespace Helpers::SystemInfo {
std::string getSystemInfo();
std::string getVersion(eHyprCtlOutputFormat fmt);
std::string getStatus(eHyprCtlOutputFormat fmt);
};

View file

@ -476,10 +476,12 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
return stateErr("no master node");
const auto NEWCHILD = PMASTER->pTarget.lock();
if (!NEWCHILD)
return stateErr("master target expired");
const bool IGNORE_IF_MASTER = vars.size() >= 2 && std::ranges::any_of(vars, [](const auto& e) { return e == "ignoremaster"; });
if (PMASTER->pTarget.lock() != PWINDOW->layoutTarget()) {
if (NEWCHILD != PWINDOW->layoutTarget()) {
const auto& NEWMASTER = PWINDOW->layoutTarget();
const bool newFocusToChild = vars.size() >= 2 && vars[1] == "child";
g_layoutManager->switchTargets(NEWMASTER, NEWCHILD);
@ -516,8 +518,12 @@ Config::ErrorResult CMasterAlgorithm::layoutMsg(const std::string_view& sv) {
const auto& ARG = vars[1]; // returns empty string if out of bounds
if (PMASTER->pTarget.lock() != PWINDOW->layoutTarget()) {
switchToWindow(PMASTER->pTarget.lock());
const auto TARGET = PMASTER->pTarget.lock();
if (!TARGET)
return stateErr("master target expired");
if (TARGET != PWINDOW->layoutTarget()) {
switchToWindow(TARGET);
// save previously focused window (only for `previous` mode)
if (ARG == "previous")
m_workspaceData.focusMasterPrev = PWINDOW->layoutTarget();

View file

@ -202,8 +202,41 @@ int main(int argc, char** argv) {
return 1;
}
if (!verifyConfig)
if (!verifyConfig) {
std::println("Welcome to Hyprland!");
std::println(R"#(
YY UJ
YYY UUJ
XXXY UUUU
zXXXX UUUUU
zzzzX UUUUJ
cczzz UUUUJ
vccccz UUUUUJ
vvcccc UUUUUJ
vvvvv UUUUJ
uuuvv UUUUJ
uuuuu UUUUU
nnnuu UUUUU
nnnnn YUUUU
xxnn YUUU
xxxn YYUU
xxxx YYUU
rxxx YYYY
rrrx YYYY
rrrx XXXY
rrrr XXXX
rrrr zzXX
rrrr zzzz
rrrrr ccczz
rrrrrx vccccc
rrrrxxxx uuvvvvvc
rrxxxxxxnnnnuuuuuv
xxxxxnnnnu
)#");
}
// let's init the compositor.
// it initializes basic Wayland stuff in the constructor.

View file

@ -16,6 +16,17 @@ using namespace Hyprutils::OS;
#define TIMESPEC_NSEC_PER_SEC 1000000000L
static uint64_t LAST_DO_LATER_SEQ = 1;
SEventLoopDoLaterLock::SEventLoopDoLaterLock(uint64_t seq_) : seq(seq_) {
;
}
SEventLoopDoLaterLock::~SEventLoopDoLaterLock() {
if (g_pEventLoopManager && seq > 0)
g_pEventLoopManager->removeDoLater(seq);
}
CEventLoopManager::CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop) {
m_timers.timerfd = CFileDescriptor{timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)};
m_wayland.loop = wlEventLoop;
@ -201,11 +212,13 @@ void CEventLoopManager::nudgeTimers() {
timerfd_settime(m_timers.timerfd.get(), TFD_TIMER_ABSTIME, &ts, nullptr);
}
void CEventLoopManager::doLater(const std::function<void()>& fn) {
m_idle.fns.emplace_back(fn);
uint64_t CEventLoopManager::doLater(const std::function<void()>& fn) {
const uint64_t NEW_SEQ = ++LAST_DO_LATER_SEQ;
m_idle.fns.emplace_back(std::make_pair<>(NEW_SEQ, fn));
if (m_idle.eventSource)
return;
return NEW_SEQ;
m_idle.eventSource = wl_event_loop_add_idle(
m_wayland.loop,
@ -215,11 +228,26 @@ void CEventLoopManager::doLater(const std::function<void()>& fn) {
IDLE->fns.clear();
IDLE->eventSource = nullptr;
for (auto& f : fns) {
if (f)
f();
if (f.second)
f.second();
}
},
&m_idle);
return NEW_SEQ;
}
void CEventLoopManager::removeDoLater(uint64_t seq) {
std::erase_if(m_idle.fns, [&seq](const auto& e) { return e.first == seq; });
if (m_idle.fns.empty() && m_idle.eventSource) {
wl_event_source_remove(m_idle.eventSource);
m_idle.eventSource = nullptr;
}
}
UP<SEventLoopDoLaterLock> CEventLoopManager::doLaterLock(const std::function<void()>& fn) {
return makeUnique<SEventLoopDoLaterLock>(doLater(fn));
}
void CEventLoopManager::doOnReadable(CFileDescriptor fd, std::function<void()>&& fn) {

View file

@ -14,6 +14,13 @@ namespace Aquamarine {
struct SPollFD;
};
struct SEventLoopDoLaterLock {
SEventLoopDoLaterLock(uint64_t seq);
~SEventLoopDoLaterLock();
uint64_t seq = 0;
};
class CEventLoopManager {
public:
CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop);
@ -30,12 +37,16 @@ class CEventLoopManager {
// schedules a recalc of the timers
void scheduleRecalc();
// schedules a function to run later, aka in a wayland idle event.
void doLater(const std::function<void()>& fn);
// schedules a function to run later, aka in a wayland idle event. Returns a sequence which can be used to remove it.
uint64_t doLater(const std::function<void()>& fn);
void removeDoLater(uint64_t seq);
// automatically cleaned up doLater instance
[[nodiscard]] UP<SEventLoopDoLaterLock> doLaterLock(const std::function<void()>& fn);
struct SIdleData {
wl_event_source* eventSource = nullptr;
std::vector<std::function<void()>> fns;
wl_event_source* eventSource = nullptr;
std::vector<std::pair<uint64_t, std::function<void()>>> fns;
};
struct SReadableWaiter {

View file

@ -4,6 +4,7 @@
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "../managers/ANRManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../helpers/Monitor.hpp"
#include "core/Seat.hpp"
#include "core/Compositor.hpp"
@ -286,7 +287,7 @@ bool CXDGToplevelResource::anyChildModal() {
uint32_t CXDGToplevelResource::setSize(const Vector2D& size) {
m_pendingApply.size = size;
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -300,7 +301,9 @@ uint32_t CXDGToplevelResource::setMaximized(bool maximized) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED);
else if (!maximized && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -314,7 +317,9 @@ uint32_t CXDGToplevelResource::setFullscreen(bool fullscreen) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN);
else if (!fullscreen && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -328,7 +333,9 @@ uint32_t CXDGToplevelResource::setActive(bool active) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED);
else if (!active && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
@ -345,22 +352,32 @@ uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED);
else if (!sus && set)
std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED);
applyState();
scheduleStateApplication();
return m_owner->scheduleConfigure();
}
void CXDGToplevelResource::applyState() {
wl_array arr;
wl_array_init(&arr);
void CXDGToplevelResource::scheduleStateApplication() {
if (!m_pendingApply.states.empty()) {
wl_array_add(&arr, m_pendingApply.states.size() * sizeof(int));
memcpy(arr.data, m_pendingApply.states.data(), m_pendingApply.states.size() * sizeof(int));
}
if (m_stateUpdate)
return;
m_resource->sendConfigure(m_pendingApply.size.x, m_pendingApply.size.y, &arr);
m_stateUpdate = g_pEventLoopManager->doLaterLock([this] {
wl_array arr;
wl_array_init(&arr);
wl_array_release(&arr);
if (!m_pendingApply.states.empty()) {
wl_array_add(&arr, m_pendingApply.states.size() * sizeof(int));
memcpy(arr.data, m_pendingApply.states.data(), m_pendingApply.states.size() * sizeof(int));
}
m_resource->sendConfigure(m_pendingApply.size.x, m_pendingApply.size.y, &arr);
wl_array_release(&arr);
m_stateUpdate.reset();
});
}
void CXDGToplevelResource::close() {
@ -516,8 +533,6 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
CXDGSurfaceResource::~CXDGSurfaceResource() {
m_events.destroy.emit();
if (m_configureSource)
wl_event_source_remove(m_configureSource);
if (m_surface)
m_surface->resetRole();
}
@ -531,22 +546,19 @@ SP<CXDGSurfaceResource> CXDGSurfaceResource::fromResource(wl_resource* res) {
return data ? data->m_self.lock() : nullptr;
}
static void onConfigure(void* data) {
sc<CXDGSurfaceResource*>(data)->configure();
}
uint32_t CXDGSurfaceResource::scheduleConfigure() {
if (m_configureSource)
if (m_stateUpdate)
return m_scheduledSerial;
m_configureSource = wl_event_loop_add_idle(g_pCompositor->m_wlEventLoop, onConfigure, this);
m_stateUpdate = g_pEventLoopManager->doLaterLock([this] { configure(); });
m_scheduledSerial = wl_display_next_serial(g_pCompositor->m_wlDisplay);
return m_scheduledSerial;
}
void CXDGSurfaceResource::configure() {
m_configureSource = nullptr;
m_stateUpdate.reset();
m_resource->sendConfigure(m_scheduledSerial);
}

View file

@ -18,6 +18,7 @@ class CXDGPopupResource;
class CSeatGrab;
class CWLSurfaceResource;
class CXDGDialogV1Resource;
struct SEventLoopDoLaterLock;
struct SXDGPositionerState {
Vector2D requestedSize;
@ -149,8 +150,11 @@ class CXDGToplevelResource {
std::vector<WP<CXDGToplevelResource>> m_children;
private:
SP<CXdgToplevel> m_resource;
void applyState();
SP<CXdgToplevel> m_resource;
UP<SEventLoopDoLaterLock> m_stateUpdate;
void scheduleStateApplication();
};
class CXDGSurfaceRole : public ISurfaceRole {
@ -202,12 +206,12 @@ class CXDGSurfaceResource {
void configure();
private:
SP<CXdgSurface> m_resource;
SP<CXdgSurface> m_resource;
uint32_t m_lastConfigureSerial = 0;
uint32_t m_scheduledSerial = 0;
UP<SEventLoopDoLaterLock> m_stateUpdate;
wl_event_source* m_configureSource = nullptr;
uint32_t m_lastConfigureSerial = 0;
uint32_t m_scheduledSerial = 0;
//
std::vector<WP<CXDGPopupResource>> m_popups;

View file

@ -89,7 +89,7 @@ TEST(ConfigLuaValueTypes, boolBadType) {
CLuaConfigBool value(false);
lua_pushinteger(L, 1);
lua_pushinteger(L, 2);
const auto err = value.parse(L);
lua_pop(L, 1);