summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUjinT34 <41110182+UjinT34@users.noreply.github.com>2025-02-02 22:25:29 +0300
committerGitHub <noreply@github.com>2025-02-02 20:25:29 +0100
commit31431a92714ab7f53c25d2ececb26c5b7264b7e3 (patch)
treeb5a9ff0e57493b503b09eb9f0e70e96075d70add
parent70d94fec134c4b91916812901310e7765acf8405 (diff)
protocols: Support content-type-v1 proto (#9226)
-rw-r--r--CMakeLists.txt9
-rw-r--r--meson.build4
-rw-r--r--protocols/meson.build1
-rw-r--r--src/Compositor.cpp3
-rw-r--r--src/config/ConfigDescriptions.hpp13
-rw-r--r--src/config/ConfigManager.cpp24
-rw-r--r--src/desktop/Window.cpp15
-rw-r--r--src/desktop/Window.hpp161
-rw-r--r--src/desktop/WindowRule.cpp7
-rw-r--r--src/desktop/WindowRule.hpp2
-rw-r--r--src/events/Windows.cpp9
-rw-r--r--src/helpers/Monitor.cpp5
-rw-r--r--src/macros.hpp1
-rw-r--r--src/managers/ProtocolManager.cpp3
-rw-r--r--src/protocols/ContentType.cpp95
-rw-r--r--src/protocols/ContentType.hpp59
-rw-r--r--src/protocols/core/Compositor.hpp2
-rw-r--r--src/protocols/types/ContentType.cpp37
-rw-r--r--src/protocols/types/ContentType.hpp18
-rw-r--r--src/render/Renderer.cpp14
20 files changed, 387 insertions, 95 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 34e9a8ed..a98f697d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -107,7 +107,15 @@ pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.0)
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1)
+string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
+list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR)
+list(GET AQ_VERSION_LIST 1 AQ_VERSION_MINOR)
+list(GET AQ_VERSION_LIST 2 AQ_VERSION_PATCH)
+
add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}")
+add_compile_definitions(AQUAMARINE_VERSION_MAJOR=${AQ_VERSION_MAJOR})
+add_compile_definitions(AQUAMARINE_VERSION_MINOR=${AQ_VERSION_MINOR})
+add_compile_definitions(AQUAMARINE_VERSION_PATCH=${AQ_VERSION_PATCH})
add_compile_definitions(HYPRLANG_VERSION="${hyprlang_dep_VERSION}")
add_compile_definitions(HYPRUTILS_VERSION="${hyprutils_dep_VERSION}")
add_compile_definitions(HYPRCURSOR_VERSION="${hyprcursor_dep_VERSION}")
@@ -368,6 +376,7 @@ protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
protocolnew("staging/security-context" "security-context-v1" false)
+protocolnew("staging/content-type" "content-type-v1" false)
protocolwayland()
diff --git a/meson.build b/meson.build
index ae6e3940..a367b166 100644
--- a/meson.build
+++ b/meson.build
@@ -36,7 +36,11 @@ hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
hyprutils = dependency('hyprutils', version: '>= 0.2.3')
+aquamarine_version_list = aquamarine.version().split('.')
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
+add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')
+add_project_arguments(['-DAQUAMARINE_VERSION_MINOR=@0@'.format(aquamarine_version_list.get(1))], language: 'cpp')
+add_project_arguments(['-DAQUAMARINE_VERSION_PATCH=@0@'.format(aquamarine_version_list.get(2))], language: 'cpp')
add_project_arguments(['-DHYPRCURSOR_VERSION="@0@"'.format(hyprcursor.version())], language: 'cpp')
add_project_arguments(['-DHYPRGRAPHICS_VERSION="@0@"'.format(hyprgraphics.version())], language: 'cpp')
add_project_arguments(['-DHYPRLANG_VERSION="@0@"'.format(hyprlang.version())], language: 'cpp')
diff --git a/protocols/meson.build b/protocols/meson.build
index 6ed1b11a..7c57470b 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -70,6 +70,7 @@ protocols = [
wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
+ wayland_protocol_dir / 'staging/content-type/content-type-v1.xml',
]
wl_protocols = []
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 6a241472..0aa0f13d 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -72,6 +72,7 @@
using namespace Hyprutils::String;
using namespace Aquamarine;
+using enum NContentType::eContentType;
static int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal {}", signo);
@@ -2323,7 +2324,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
// ignore if DS is disabled.
- if (*PDIRECTSCANOUT)
+ if (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME))
g_pHyprRenderer->setSurfaceScanoutMode(PWINDOW->m_pWLSurface->resource(), EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->self.lock() : nullptr);
g_pConfigManager->ensureVRR(PMONITOR);
diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp
index 2fa86817..28cd29c4 100644
--- a/src/config/ConfigDescriptions.hpp
+++ b/src/config/ConfigDescriptions.hpp
@@ -1314,9 +1314,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
SConfigOptionDescription{
.value = "render:direct_scanout",
.description = "Enables direct scanout. Direct scanout attempts to reduce lag when there is only one fullscreen application on a screen (e.g. game). It is also "
- "recommended to set this to false if the fullscreen application shows graphical glitches.",
- .type = CONFIG_OPTION_BOOL,
- .data = SConfigOptionDescription::SBoolData{false},
+ "recommended to set this to false if the fullscreen application shows graphical glitches. 0 - off, 1 - on, 2 - auto (on with content type 'game')",
+ .type = CONFIG_OPTION_INT,
+ .data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 2},
},
SConfigOptionDescription{
.value = "render:expand_undersized_textures",
@@ -1362,9 +1362,10 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
},
SConfigOptionDescription{
.value = "cursor:no_break_fs_vrr",
- .description = "disables scheduling new frames on cursor movement for fullscreen apps with VRR enabled to avoid framerate spikes (requires no_hardware_cursors = true)",
- .type = CONFIG_OPTION_BOOL,
- .data = SConfigOptionDescription::SBoolData{false},
+ .description = "disables scheduling new frames on cursor movement for fullscreen apps with VRR enabled to avoid framerate spikes (may require no_hardware_cursors = true) "
+ "0 - off, 1 - on, 2 - auto (on with content type 'game')",
+ .type = CONFIG_OPTION_INT,
+ .data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
},
SConfigOptionDescription{
.value = "cursor:min_refresh_rate",
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 8bb6d157..42a1a91c 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -26,6 +26,7 @@
#include "../plugins/PluginSystem.hpp"
#include "managers/HookSystemManager.hpp"
+#include "protocols/types/ContentType.hpp"
#include <cstddef>
#include <cstdint>
#include <hyprutils/path/Path.hpp>
@@ -621,7 +622,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("opengl:nvidia_anti_flicker", Hyprlang::INT{1});
m_pConfig->addConfigValue("cursor:no_hardware_cursors", Hyprlang::INT{0});
- m_pConfig->addConfigValue("cursor:no_break_fs_vrr", Hyprlang::INT{0});
+ m_pConfig->addConfigValue("cursor:no_break_fs_vrr", Hyprlang::INT{2});
m_pConfig->addConfigValue("cursor:min_refresh_rate", Hyprlang::INT{24});
m_pConfig->addConfigValue("cursor:hotspot_padding", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:inactive_timeout", {0.f});
@@ -1347,6 +1348,14 @@ std::vector<SP<CWindowRule>> CConfigManager::getMatchingRules(PHLWINDOW pWindow,
continue;
}
+ if (!rule->szContentType.empty()) {
+ try {
+ const auto contentType = NContentType::fromString(rule->szContentType);
+ if (pWindow->getContentType() != contentType)
+ continue;
+ } catch (std::exception& e) { Debug::log(ERR, "Rule \"content:{}\" failed with: {}", rule->szContentType, e.what()); }
+ }
+
if (!rule->szWorkspace.empty()) {
const auto PWORKSPACE = pWindow->m_pWorkspace;
@@ -2361,6 +2370,7 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
const auto FOCUSPOS = VALUE.find("focus:");
const auto FULLSCREENSTATEPOS = VALUE.find("fullscreenstate:");
const auto ONWORKSPACEPOS = VALUE.find("onworkspace:");
+ const auto CONTENTTYPEPOS = VALUE.find("content:");
// find workspacepos that isn't onworkspacepos
size_t WORKSPACEPOS = std::string::npos;
@@ -2373,8 +2383,8 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
currentPos = VALUE.find("workspace:", currentPos + 1);
}
- const auto checkPos = std::unordered_set{TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS,
- FULLSCREENPOS, PINNEDPOS, FULLSCREENSTATEPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS};
+ const auto checkPos = std::unordered_set{TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS,
+ FULLSCREENPOS, PINNEDPOS, FULLSCREENSTATEPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS, CONTENTTYPEPOS};
if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) {
Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE);
return "Invalid rulev2 syntax: " + VALUE;
@@ -2411,6 +2421,8 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
min = WORKSPACEPOS;
if (FOCUSPOS > pos && FOCUSPOS < min)
min = FOCUSPOS;
+ if (CONTENTTYPEPOS > pos && CONTENTTYPEPOS < min)
+ min = CONTENTTYPEPOS;
result = result.substr(0, min - pos);
@@ -2469,6 +2481,9 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
if (ONWORKSPACEPOS != std::string::npos)
rule->szOnWorkspace = extract(ONWORKSPACEPOS + 12);
+ if (CONTENTTYPEPOS != std::string::npos)
+ rule->szContentType = extract(CONTENTTYPEPOS + 8);
+
if (RULE == "unset") {
std::erase_if(m_vWindowRules, [&](const auto& other) {
if (!other->v2)
@@ -2513,6 +2528,9 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
if (!rule->szOnWorkspace.empty() && rule->szOnWorkspace != other->szOnWorkspace)
return false;
+ if (!rule->szContentType.empty() && rule->szContentType != other->szContentType)
+ return false;
+
return true;
}
});
diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp
index 6e580e49..6a523eec 100644
--- a/src/desktop/Window.cpp
+++ b/src/desktop/Window.cpp
@@ -15,6 +15,7 @@
#include "../managers/AnimationManager.hpp"
#include "../protocols/XDGShell.hpp"
#include "../protocols/core/Compositor.hpp"
+#include "../protocols/ContentType.hpp"
#include "../xwayland/XWayland.hpp"
#include "../helpers/Color.hpp"
#include "../events/Events.hpp"
@@ -29,6 +30,7 @@
using namespace Hyprutils::String;
using namespace Hyprutils::Animation;
+using enum NContentType::eContentType;
PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface));
@@ -1724,3 +1726,16 @@ void CWindow::sendWindowSize(Vector2D size, bool force, std::optional<Vector2D>
else if (m_pXDGSurface && m_pXDGSurface->toplevel)
m_vPendingSizeAcks.emplace_back(m_pXDGSurface->toplevel->setSize(size), size.floor());
}
+
+NContentType::eContentType CWindow::getContentType() {
+ return m_pWLSurface->resource()->contentType.valid() ? m_pWLSurface->resource()->contentType->value : CONTENT_TYPE_NONE;
+}
+
+void CWindow::setContentType(NContentType::eContentType contentType) {
+ if (!m_pWLSurface->resource()->contentType.valid())
+ m_pWLSurface->resource()->contentType = PROTO::contentType->getContentType(m_pWLSurface->resource());
+ // else disallow content type change if proto is used?
+
+ Debug::log(INFO, "ContentType for window {}", (int)contentType);
+ m_pWLSurface->resource()->contentType->value = contentType;
+}
diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp
index ce2f8eb2..b3c3d84f 100644
--- a/src/desktop/Window.hpp
+++ b/src/desktop/Window.hpp
@@ -19,6 +19,7 @@
#include "WLSurface.hpp"
#include "Workspace.hpp"
#include "WindowRule.hpp"
+#include "protocols/types/ContentType.hpp"
class CXDGSurfaceResource;
class CXWaylandSurface;
@@ -393,85 +394,87 @@ class CWindow {
}
// methods
- CBox getFullWindowBoundingBox();
- SBoxExtents getFullWindowExtents();
- CBox getWindowBoxUnified(uint64_t props);
- CBox getWindowIdealBoundingBoxIgnoreReserved();
- void addWindowDeco(UP<IHyprWindowDecoration> deco);
- void updateWindowDecos();
- void removeWindowDeco(IHyprWindowDecoration* deco);
- void uncacheWindowDecos();
- bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
- pid_t getPID();
- IHyprWindowDecoration* getDecorationByType(eDecorationType);
- void updateToplevel();
- void updateSurfaceScaleTransformDetails(bool force = false);
- void moveToWorkspace(PHLWORKSPACE);
- PHLWINDOW x11TransientFor();
- void onUnmap();
- void onMap();
- void setHidden(bool hidden);
- bool isHidden();
- void applyDynamicRule(const SP<CWindowRule>& r);
- void updateDynamicRules();
- SBoxExtents getFullWindowReservedArea();
- Vector2D middle();
- bool opaque();
- float rounding();
- float roundingPower();
- bool canBeTorn();
- void setSuspended(bool suspend);
- bool visibleOnMonitor(PHLMONITOR pMonitor);
- WORKSPACEID workspaceID();
- MONITORID monitorID();
- bool onSpecialWorkspace();
- void activate(bool force = false);
- int surfacesCount();
- void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
- bool isFullscreen();
- bool isEffectiveInternalFSMode(const eFullscreenMode);
- int getRealBorderSize();
- float getScrollMouse();
- float getScrollTouchpad();
- void updateWindowData();
- void updateWindowData(const struct SWorkspaceRule&);
- void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
- bool isInCurvedCorner(double x, double y);
- bool hasPopupAt(const Vector2D& pos);
- int popupsCount();
- void applyGroupRules();
- void createGroup();
- void destroyGroup();
- PHLWINDOW getGroupHead();
- PHLWINDOW getGroupTail();
- PHLWINDOW getGroupCurrent();
- PHLWINDOW getGroupPrevious();
- PHLWINDOW getGroupWindowByIndex(int);
- int getGroupSize();
- bool canBeGroupedInto(PHLWINDOW pWindow);
- void setGroupCurrent(PHLWINDOW pWindow);
- void insertWindowToGroup(PHLWINDOW pWindow);
- void updateGroupOutputs();
- void switchWithWindowInGroup(PHLWINDOW pWindow);
- void setAnimationsToMove();
- void onWorkspaceAnimUpdate();
- void onFocusAnimUpdate();
- void onUpdateState();
- void onUpdateMeta();
- void onX11Configure(CBox box);
- void onResourceChangeX11();
- std::string fetchTitle();
- std::string fetchClass();
- void warpCursor(bool force = false);
- PHLWINDOW getSwallower();
- void unsetWindowData(eOverridePriority priority);
- bool isX11OverrideRedirect();
- bool isModal();
- Vector2D requestedMinSize();
- Vector2D requestedMaxSize();
- void sendWindowSize(Vector2D size, bool force = false, std::optional<Vector2D> overridePos = std::nullopt);
-
- CBox getWindowMainSurfaceBox() const {
+ CBox getFullWindowBoundingBox();
+ SBoxExtents getFullWindowExtents();
+ CBox getWindowBoxUnified(uint64_t props);
+ CBox getWindowIdealBoundingBoxIgnoreReserved();
+ void addWindowDeco(UP<IHyprWindowDecoration> deco);
+ void updateWindowDecos();
+ void removeWindowDeco(IHyprWindowDecoration* deco);
+ void uncacheWindowDecos();
+ bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
+ pid_t getPID();
+ IHyprWindowDecoration* getDecorationByType(eDecorationType);
+ void updateToplevel();
+ void updateSurfaceScaleTransformDetails(bool force = false);
+ void moveToWorkspace(PHLWORKSPACE);
+ PHLWINDOW x11TransientFor();
+ void onUnmap();
+ void onMap();
+ void setHidden(bool hidden);
+ bool isHidden();
+ void applyDynamicRule(const SP<CWindowRule>& r);
+ void updateDynamicRules();
+ SBoxExtents getFullWindowReservedArea();
+ Vector2D middle();
+ bool opaque();
+ float rounding();
+ float roundingPower();
+ bool canBeTorn();
+ void setSuspended(bool suspend);
+ bool visibleOnMonitor(PHLMONITOR pMonitor);
+ WORKSPACEID workspaceID();
+ MONITORID monitorID();
+ bool onSpecialWorkspace();
+ void activate(bool force = false);
+ int surfacesCount();
+ void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
+ bool isFullscreen();
+ bool isEffectiveInternalFSMode(const eFullscreenMode);
+ int getRealBorderSize();
+ float getScrollMouse();
+ float getScrollTouchpad();
+ void updateWindowData();
+ void updateWindowData(const struct SWorkspaceRule&);
+ void onBorderAngleAnimEnd(WP<Hyprutils::Animation::CBaseAnimatedVariable> pav);
+ bool isInCurvedCorner(double x, double y);
+ bool hasPopupAt(const Vector2D& pos);
+ int popupsCount();
+ void applyGroupRules();
+ void createGroup();
+ void destroyGroup();
+ PHLWINDOW getGroupHead();
+ PHLWINDOW getGroupTail();
+ PHLWINDOW getGroupCurrent();
+ PHLWINDOW getGroupPrevious();
+ PHLWINDOW getGroupWindowByIndex(int);
+ int getGroupSize();
+ bool canBeGroupedInto(PHLWINDOW pWindow);
+ void setGroupCurrent(PHLWINDOW pWindow);
+ void insertWindowToGroup(PHLWINDOW pWindow);
+ void updateGroupOutputs();
+ void switchWithWindowInGroup(PHLWINDOW pWindow);
+ void setAnimationsToMove();
+ void onWorkspaceAnimUpdate();
+ void onFocusAnimUpdate();
+ void onUpdateState();
+ void onUpdateMeta();
+ void onX11Configure(CBox box);
+ void onResourceChangeX11();
+ std::string fetchTitle();
+ std::string fetchClass();
+ void warpCursor(bool force = false);
+ PHLWINDOW getSwallower();
+ void unsetWindowData(eOverridePriority priority);
+ bool isX11OverrideRedirect();
+ bool isModal();
+ Vector2D requestedMinSize();
+ Vector2D requestedMaxSize();
+ void sendWindowSize(Vector2D size, bool force = false, std::optional<Vector2D> overridePos = std::nullopt);
+ NContentType::eContentType getContentType();
+ void setContentType(NContentType::eContentType contentType);
+
+ CBox getWindowMainSurfaceBox() const {
return {m_vRealPosition->value().x, m_vRealPosition->value().y, m_vRealSize->value().x, m_vRealSize->value().y};
}
diff --git a/src/desktop/WindowRule.cpp b/src/desktop/WindowRule.cpp
index 306a25ce..fea2d43b 100644
--- a/src/desktop/WindowRule.cpp
+++ b/src/desktop/WindowRule.cpp
@@ -8,8 +8,9 @@ static const auto RULES = std::unordered_set<std::string>{
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused",
};
static const auto RULES_PREFIX = std::unordered_set<std::string>{
- "animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move", "opacity",
- "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad", "size", "suppressevent", "tag", "workspace", "xray",
+ "animation", "bordercolor", "bordersize", "center", "content", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize",
+ "monitor", "move", "opacity", "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad",
+ "size", "suppressevent", "tag", "workspace", "xray",
};
CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : szValue(value), szRule(rule), v2(isV2), execRule(isExecRule) {
@@ -74,6 +75,8 @@ CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool
ruleType = RULE_WORKSPACE;
else if (rule.starts_with("prop"))
ruleType = RULE_PROP;
+ else if (rule.starts_with("content"))
+ ruleType = RULE_CONTENT;
else {
// check if this is a prop.
const CVarList VARS(rule, 0, 's', true);
diff --git a/src/desktop/WindowRule.hpp b/src/desktop/WindowRule.hpp
index be9c2d9c..50e221f3 100644
--- a/src/desktop/WindowRule.hpp
+++ b/src/desktop/WindowRule.hpp
@@ -36,6 +36,7 @@ class CWindowRule {
RULE_TAG,
RULE_WORKSPACE,
RULE_PROP,
+ RULE_CONTENT,
};
eRuleType ruleType = RULE_INVALID;
@@ -58,6 +59,7 @@ class CWindowRule {
std::string szFullscreenState = ""; // empty means any
std::string szOnWorkspace = ""; // empty means any
std::string szWorkspace = ""; // empty means any
+ std::string szContentType = ""; // empty means any
// precompiled regexes
CRuleRegexContainer rTitle;
diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp
index f6ef9ced..8eaabd29 100644
--- a/src/events/Windows.cpp
+++ b/src/events/Windows.cpp
@@ -11,11 +11,11 @@
#include "../protocols/XDGShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ToplevelExport.hpp"
+#include "protocols/types/ContentType.hpp"
#include "../xwayland/XSurface.hpp"
#include "managers/AnimationManager.hpp"
#include "managers/PointerManager.hpp"
#include "../desktop/LayerSurface.hpp"
-#include "../managers/input/InputManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/EventManager.hpp"
#include "../managers/AnimationManager.hpp"
@@ -307,6 +307,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
break;
}
+ case CWindowRule::RULE_CONTENT: {
+ const CVarList VARS(r->szRule, 0, ' ');
+ try {
+ PWINDOW->setContentType(NContentType::fromString(VARS[1]));
+ } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); }
+ break;
+ }
default: break;
}
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index 9aa6efce..3ea6d49b 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -33,6 +33,7 @@
using namespace Hyprutils::String;
using namespace Hyprutils::Utils;
using namespace Hyprutils::OS;
+using enum NContentType::eContentType;
static int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor(((CMonitor*)data)->self.lock());
@@ -799,8 +800,8 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
static auto PMINRR = CConfigValue<Hyprlang::INT>("cursor:min_refresh_rate");
// skip scheduling extra frames for fullsreen apps with vrr
- bool shouldSkip =
- *PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
+ const bool shouldSkip = activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN &&
+ (*PNOBREAK == 1 || (*PNOBREAK == 2 && activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) && output->state->state().adaptiveSync;
// keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) {
diff --git a/src/macros.hpp b/src/macros.hpp
index a5176675..d2cad6b7 100644
--- a/src/macros.hpp
+++ b/src/macros.hpp
@@ -111,6 +111,7 @@
} \
}
+#define AQUAMARINE_VERSION_NUMBER (AQUAMARINE_VERSION_MAJOR * 10000 + AQUAMARINE_VERSION_MINOR * 100 + AQUAMARINE_VERSION_PATCH)
#define AQUAMARINE_FORWARD(name) \
namespace Aquamarine { \
class name; \
diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp
index 62736ae5..6cd3a608 100644
--- a/src/managers/ProtocolManager.cpp
+++ b/src/managers/ProtocolManager.cpp
@@ -58,10 +58,12 @@
#include "../protocols/core/Shm.hpp"
#include "../protocols/ColorManagement.hpp"
#include "../protocols/FrogColorManagement.hpp"
+#include "../protocols/ContentType.hpp"
#include "../helpers/Monitor.hpp"
#include "../render/Renderer.hpp"
#include "../Compositor.hpp"
+#include "content-type-v1.hpp"
#include <aquamarine/buffer/Buffer.hpp>
#include <aquamarine/backend/Backend.hpp>
@@ -169,6 +171,7 @@ CProtocolManager::CProtocolManager() {
PROTO::securityContext = makeUnique<CSecurityContextProtocol>(&wp_security_context_manager_v1_interface, 1, "SecurityContext");
PROTO::ctm = makeUnique<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 2, "CTMControl");
PROTO::hyprlandSurface = makeUnique<CHyprlandSurfaceProtocol>(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface");
+ PROTO::contentType = makeUnique<CContentTypeProtocol>(&wp_content_type_manager_v1_interface, 1, "ContentType");
if (*PENABLEXXCM) {
PROTO::colorManagement = makeUnique<CColorManagementProtocol>(&xx_color_manager_v4_interface, 1, "ColorManagement");
diff --git a/src/protocols/ContentType.cpp b/src/protocols/ContentType.cpp
new file mode 100644
index 00000000..0dd5481a
--- /dev/null
+++ b/src/protocols/ContentType.cpp
@@ -0,0 +1,95 @@
+#include "ContentType.hpp"
+#include "content-type-v1.hpp"
+#include "protocols/types/ContentType.hpp"
+
+CContentTypeManager::CContentTypeManager(SP<CWpContentTypeManagerV1> resource) : m_resource(resource) {
+ if UNLIKELY (!good())
+ return;
+
+ resource->setDestroy([](CWpContentTypeManagerV1* r) {});
+ resource->setOnDestroy([this](CWpContentTypeManagerV1* r) { PROTO::contentType->destroyResource(this); });
+
+ resource->setGetSurfaceContentType([](CWpContentTypeManagerV1* r, uint32_t id, wl_resource* surface) {
+ LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface);
+ auto SURF = CWLSurfaceResource::fromResource(surface);
+
+ if (!SURF) {
+ LOGM(ERR, "No surface for resource {}", (uintptr_t)surface);
+ r->error(-1, "Invalid surface (2)");
+ return;
+ }
+
+ if (SURF->colorManagement) {
+ r->error(WP_CONTENT_TYPE_MANAGER_V1_ERROR_ALREADY_CONSTRUCTED, "CT manager already exists");
+ return;
+ }
+
+ const auto RESOURCE = PROTO::contentType->m_vContentTypes.emplace_back(makeShared<CContentType>(makeShared<CWpContentTypeV1>(r->client(), r->version(), id)));
+ if UNLIKELY (!RESOURCE->good()) {
+ r->noMemory();
+ PROTO::contentType->m_vContentTypes.pop_back();
+ return;
+ }
+
+ RESOURCE->self = RESOURCE;
+
+ SURF->contentType = RESOURCE;
+ });
+}
+
+bool CContentTypeManager::good() {
+ return m_resource->resource();
+}
+
+CContentType::CContentType(WP<CWLSurfaceResource> surface) {
+ destroy = surface->events.destroy.registerListener([this](std::any d) { PROTO::contentType->destroyResource(this); });
+}
+
+CContentType::CContentType(SP<CWpContentTypeV1> resource) : m_resource(resource) {
+ if UNLIKELY (!good())
+ return;
+
+ m_pClient = resource->client();
+
+ resource->setDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); });
+ resource->setOnDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); });
+
+ resource->setSetContentType([this](CWpContentTypeV1* r, wpContentTypeV1Type type) { value = NContentType::fromWP(type); });
+}
+
+bool CContentType::good() {
+ return m_resource && m_resource->resource();
+}
+
+wl_client* CContentType::client() {
+ return m_pClient;
+}
+
+CContentTypeProtocol::CContentTypeProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ ;
+}
+
+void CContentTypeProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_vManagers.emplace_back(makeShared<CContentTypeManager>(makeShared<CWpContentTypeManagerV1>(client, ver, id)));
+
+ if UNLIKELY (!RESOURCE->good()) {
+ wl_client_post_no_memory(client);
+ m_vManagers.pop_back();
+ return;
+ }
+}
+
+SP<CContentType> CContentTypeProtocol::getContentType(WP<CWLSurfaceResource> surface) {
+ if (surface->contentType.valid())
+ return surface->contentType.lock();
+
+ return m_vContentTypes.emplace_back(makeShared<CContentType>(surface));
+}
+
+void CContentTypeProtocol::destroyResource(CContentTypeManager* resource) {
+ std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
+}
+
+void CContentTypeProtocol::destroyResource(CContentType* resource) {
+ std::erase_if(m_vContentTypes, [&](const auto& other) { return other.get() == resource; });
+}
diff --git a/src/protocols/ContentType.hpp b/src/protocols/ContentType.hpp
new file mode 100644
index 00000000..c0359bf1
--- /dev/null
+++ b/src/protocols/ContentType.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "WaylandProtocol.hpp"
+#include "core/Compositor.hpp"
+#include "content-type-v1.hpp"
+#include "protocols/types/ContentType.hpp"
+
+class CContentTypeManager {
+ public:
+ CContentTypeManager(SP<CWpContentTypeManagerV1> resource);
+
+ bool good();
+
+ private:
+ SP<CWpContentTypeManagerV1> m_resource;
+};
+
+class CContentType {
+ public:
+ CContentType(SP<CWpContentTypeV1> resource);
+ CContentType(WP<CWLSurfaceResource> surface);
+
+ bool good();
+ wl_client* client();
+ NContentType::eContentType value = NContentType::CONTENT_TYPE_NONE;
+
+ WP<CContentType> self;
+
+ private:
+ SP<CWpContentTypeV1> m_resource;
+ wl_client* m_pClient = nullptr;
+
+ CHyprSignalListener destroy;
+
+ friend class CContentTypeProtocol;
+};
+
+class CContentTypeProtocol : public IWaylandProtocol {
+ public:
+ CContentTypeProtocol(const wl_interface* iface, const int& ver, const std::string& name);
+
+ virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
+
+ SP<CContentType> getContentType(WP<CWLSurfaceResource> surface);
+
+ private:
+ void destroyResource(CContentTypeManager* resource);
+ void destroyResource(CContentType* resource);
+
+ std::vector<SP<CContentTypeManager>> m_vManagers;
+ std::vector<SP<CContentType>> m_vContentTypes;
+
+ friend class CContentTypeManager;
+ friend class CContentType;
+};
+
+namespace PROTO {
+ inline UP<CContentTypeProtocol> contentType;
+};
diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp
index 20614813..aaaf9b1a 100644
--- a/src/protocols/core/Compositor.hpp
+++ b/src/protocols/core/Compositor.hpp
@@ -26,6 +26,7 @@ class CViewportResource;
class CDRMSyncobjSurfaceResource;
class CColorManagementSurface;
class CFrogColorManagementSurface;
+class CContentType;
class CWLCallbackResource {
public:
@@ -123,6 +124,7 @@ class CWLSurfaceResource {
WP<CViewportResource> viewportResource;
WP<CDRMSyncobjSurfaceResource> syncobj; // may not be present
WP<CColorManagementSurface> colorManagement;
+ WP<CContentType> contentType;
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
CRegion accumulateCurrentBufferDamage();
diff --git a/src/protocols/types/ContentType.cpp b/src/protocols/types/ContentType.cpp
new file mode 100644
index 00000000..c0a3d30f
--- /dev/null
+++ b/src/protocols/types/ContentType.cpp
@@ -0,0 +1,37 @@
+#include "ContentType.hpp"
+#include <drm_mode.h>
+#include <stdexcept>
+#include <format>
+
+namespace NContentType {
+ static std::unordered_map<std::string, eContentType> const table = {
+ {"none", CONTENT_TYPE_NONE}, {"photo", CONTENT_TYPE_PHOTO}, {"video", CONTENT_TYPE_VIDEO}, {"game", CONTENT_TYPE_GAME}};
+
+ eContentType fromString(const std::string name) {
+ auto it = table.find(name);
+ if (it != table.end())
+ return it->second;
+ else
+ throw std::invalid_argument(std::format("Unknown content type {}", name));
+ }
+
+ eContentType fromWP(wpContentTypeV1Type contentType) {
+ switch (contentType) {
+ case WP_CONTENT_TYPE_V1_TYPE_NONE: return CONTENT_TYPE_NONE;
+ case WP_CONTENT_TYPE_V1_TYPE_PHOTO: return CONTENT_TYPE_PHOTO;
+ case WP_CONTENT_TYPE_V1_TYPE_VIDEO: return CONTENT_TYPE_VIDEO;
+ case WP_CONTENT_TYPE_V1_TYPE_GAME: return CONTENT_TYPE_GAME;
+ default: return CONTENT_TYPE_NONE;
+ }
+ }
+
+ uint16_t toDRM(eContentType contentType) {
+ switch (contentType) {
+ case CONTENT_TYPE_NONE: return DRM_MODE_CONTENT_TYPE_GRAPHICS;
+ case CONTENT_TYPE_PHOTO: return DRM_MODE_CONTENT_TYPE_PHOTO;
+ case CONTENT_TYPE_VIDEO: return DRM_MODE_CONTENT_TYPE_CINEMA;
+ case CONTENT_TYPE_GAME: return DRM_MODE_CONTENT_TYPE_GAME;
+ default: return DRM_MODE_CONTENT_TYPE_NO_DATA;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/protocols/types/ContentType.hpp b/src/protocols/types/ContentType.hpp
new file mode 100644
index 00000000..66fcbca7
--- /dev/null
+++ b/src/protocols/types/ContentType.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "content-type-v1.hpp"
+#include <cstdint>
+
+namespace NContentType {
+
+ enum eContentType : uint8_t {
+ CONTENT_TYPE_NONE = 0,
+ CONTENT_TYPE_PHOTO = 1,
+ CONTENT_TYPE_VIDEO = 2,
+ CONTENT_TYPE_GAME = 3,
+ };
+
+ eContentType fromString(const std::string name);
+ eContentType fromWP(wpContentTypeV1Type contentType);
+ uint16_t toDRM(eContentType contentType);
+} \ No newline at end of file
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 42445728..6d7b409c 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -33,10 +33,14 @@
#include "pass/SurfacePassElement.hpp"
#include "debug/Log.hpp"
#include "protocols/ColorManagement.hpp"
+#if AQUAMARINE_VERSION_NUMBER > 702 // >0.7.2
+#include "protocols/types/ContentType.hpp"
+#endif
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils;
using namespace Hyprutils::OS;
+using enum NContentType::eContentType;
extern "C" {
#include <xf86drm.h>
@@ -1192,7 +1196,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
pMonitor->tearingState.activelyTearing = shouldTear;
- if (*PDIRECTSCANOUT && !shouldTear) {
+ if ((*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && pMonitor->activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) && !shouldTear) {
if (pMonitor->attemptDirectScanout()) {
return;
} else if (!pMonitor->lastScanout.expired()) {
@@ -1509,6 +1513,14 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
}
}
+#if AQUAMARINE_VERSION_NUMBER > 702 // >0.7.2
+ if (pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
+ const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
+ pMonitor->output->state->setContentType(NContentType::toDRM(WINDOW->getContentType()));
+ } else
+ pMonitor->output->state->setContentType(NContentType::toDRM(CONTENT_TYPE_NONE));
+#endif
+
if (pMonitor->ctmUpdated) {
pMonitor->ctmUpdated = false;
pMonitor->output->state->setCTM(pMonitor->ctm);