Compare commits

...

51 Commits

Author SHA1 Message Date
Vaxry
29e2e59fdb version: bump to v0.48.1 2025-03-28 16:16:07 +00:00
Lee Bousfield
1fdb5ba09e xwayland: Cleanup server startup and FDs (#9769) 2025-03-28 16:14:46 +00:00
Lee Bousfield
aa421c2e95 core: Don't damage the entire surface every frame (#9763)
* core: Don't damage the entire surface every frame

* core: Damage buffer on dims or transform change

* core: Use guards for scale and tr equality checks
2025-03-28 16:14:46 +00:00
Vaxry
3c36e083f1 surfacestate: reset buffer bit before applying to current
fixes #9759
2025-03-28 16:14:46 +00:00
Vaxry
3fc0abcb56 workspaces: minor fixes to persistence
fixes #9741
2025-03-28 16:14:46 +00:00
Tom Englund
273f43bda6 internal: fix minor ubsan errors (#9743)
* opengl: check if g_pHyprOpengl exist

on compositor destruction we can hit a race where a CEGLSync destructs
and tries to call eglDestroySyncKHR on a null g_pHyprOpengl.

/src/render/OpenGL.cpp:3019:32: runtime error: member access within null pointer of type 'struct CHyprOpenGLImpl'
     #0 0x555565eed979 in CEGLSync::~CEGLSync() /src/render/OpenGL.cpp:3019
     #1 0x555565f6271e in std::default_delete<CEGLSync>::operator()(CEGLSync*)
     const /usr/lib/gcc/x86_64-pc-linux-gnu/14/include/g++-v14/bits/unique_ptr.h:93

* xdgshell: dont apply state on empty states

setsize can be called before a state has been added to pending,
resulting in calling ApplyState with a empty state.

/src/protocols/XDGShell.cpp:323:11: runtime error: null pointer passed as argument 2, which is declared to never be null
     #0 0x5555659bf67e in CXDGToplevelResource::applyState() /src/protocols/XDGShell.cpp:323
     #1 0x5555659bcedc in CXDGToplevelResource::setSize(Hyprutils::Math::Vector2D const&) /src/protocols/XDGShell.cpp:  256
     #2 0x555563eed0ef in Events::listener_commitWindow(void*, void*) /src/events/Windows.cpp:841
2025-03-28 16:14:46 +00:00
Vaxry
0a3948107a surfacestate: track and apply updated state
fixes #9729
2025-03-28 16:14:46 +00:00
Vaxry
189e18394e opengl: don't attempt to compile cm on gles3.0
also disable the error for the cm shader

fixes #9738
2025-03-28 16:14:46 +00:00
vaxerski
3eb859bb4e pass/rect: include clipBox in opaque calculations
ref #9730 ref #9709
2025-03-28 16:14:46 +00:00
vaxerski
eaa9663057 groupbar: round boxes 2025-03-28 16:14:46 +00:00
Tom Englund
5da8281d68 pass: remove unusued timeline in texpass (#9734)
remove unused timeline and waitpoint in texpass and especially remove
the passing it to renderTextureInternalWithDamage that implicitly
converted it to bool. setting discardActive and allowCustomUV
2025-03-28 16:14:46 +00:00
Arkady Buryakov
d2031ba3e0 Groupbar: apply scaling factor to text (#9731) 2025-03-28 16:14:46 +00:00
Tom Englund
c22f46768c xwl: dont close the fd to early (#9715)
dont close the fd until the wl_event_source is removed, so we dont get
another event triggered with an already closed fd.
2025-03-28 16:14:46 +00:00
Vaxry
66470020a7 seat: avoid sending null surfaces in leave/enter events
ref #9699
2025-03-28 16:14:46 +00:00
UjinT34
ed2f50d5ad renderer: Simplify and fix hdr metadata setting (#9706)
* simplify and fix hdr metadata setting

* keep incorrect(?) cm skip till #9600
2025-03-28 16:14:46 +00:00
Vaxry
5ee35f914f version: bump to 0.48.0 2025-03-23 14:56:13 +00:00
entailz
aa1bd647b1 core/Compositor.hpp: fix non-relative Texture import (#9703) 2025-03-23 14:49:10 +02:00
Lee Bousfield
fdb7ca6c8f core/compositor: Fix dropping cursor buffer data early (#9700) 2025-03-22 23:06:02 +01:00
UjinT34
6ab5a0befb renderer: fix cm_fs_passthrough (#9698) 2025-03-22 18:34:01 +01:00
Lee Bousfield
6384f4acf4 core/compositor: Correctly track SHM buffer damage (#9678) 2025-03-22 17:13:44 +01:00
Aaron Blasko
4600043a49 hyprpm: return 1 when plugins are outdated (#9694)
* hyprpm: return 1 when plugins are outdated

* clang-formatted
2025-03-22 17:01:35 +01:00
Lee Bousfield
279b06044c screencopy, render: Use explicit sync for screencopy (#9697)
* screencopy, render: Use explicit sync for screencopy

* screencopy: Check if explicit sync is enabled

* screencopy: Don't require explicit KMS enabled
2025-03-22 17:01:14 +01:00
Tom Englund
ccbdba7ee2 syncobj: refactor point timelines (#9689)
no need to store the resource, just store the csynctimeline as a shared
pointer and make the timeline own the syncobj fd.
2025-03-21 20:19:53 +01:00
UjinT34
c7f0519faf core: fix DS and VRR automation (#9334) 2025-03-21 14:33:07 +01:00
Lee Bousfield
7ea4fbf0ba types: Upgrade buffer ref from WP to SP (#9677) 2025-03-20 11:08:47 +00:00
Tom Englund
f6ca4bac51 syncobj: restore SHM buffer reset (#9675)
reset shm buffers early to mitigate stuttering animations, also reuse
the monitors eglSync and store the eglsync per monitor. this however
reintroduces flickering in dbeaver nonsyncobj application.
2025-03-20 11:39:55 +01:00
phonetic112
155eba57d8 groupbar: remove 2 pixel gap above groupbar (#9664) 2025-03-19 23:09:36 +01:00
Andrei V
7b10530a0d XWayland: restore the abstract socket, and make it optional (#9615)
* Revert "xwayland: don't create an abstract unix domain socket on linux (#8874)" (#9574)

This reverts commit 2b01a5bcf6.

* xwayland: make the abstract Unix domain socket optional (#9574)

* xwayland: extend the default permissions for a regular Unix domain socket (#9574)

* xwayland: a little refactoring for `createSocket`
2025-03-19 23:06:30 +01:00
Tom Englund
a25a214523 dmabuf: pop buffer on failure (#9620)
ensure it doesnt permanently gets stuck in the container on failure, pop
it from the container.
2025-03-19 17:49:20 +01:00
Lee Bousfield
c8d80a2920 ci: Fail on warnings (#9668)
* ci: Fail on warnings

* misc: Fix compiler warnings
2025-03-19 00:46:28 +01:00
Vaxry
03385fc07f seatmgr: avoid crash on null surfs 2025-03-18 19:43:05 +00:00
vaxerski
cca0f48b74 renderer: add an option to disable cm and auto-skip cm if not necessary
fixes #9641

adds render:cm_enabled default true
2025-03-18 11:30:08 +00:00
Kamikadze
60edb376f2 config/defaultConfig.hpp: windowrulev2 -> windowrule (#9663) 2025-03-18 13:13:03 +02:00
tachyglossues
6f74d8d7e9 example/hyprland.conf: windowrulev2 -> windowrule (#9662) 2025-03-18 12:24:43 +02:00
Vaxry
ec4bea7901 config: nuke windowrule v1 syntax 2025-03-18 01:37:00 +00:00
Honkazel
9171db1984 renderer: delete now redundant ifdefs (#9651)
Hyprland for now requires aquamarine =>0.8.0 anyway
2025-03-18 02:29:08 +01:00
Ikalco
5f60fc7d00 renderer: only commit hw cursor stuff if needed (#9654) 2025-03-17 22:06:41 +01:00
Lee Bousfield
c4f46473df monitor: Optimize direct scanout damage (#9653) 2025-03-17 22:05:44 +01:00
Lee Bousfield
011d7ccb91 internal: Fix compiler warnings (#9646) 2025-03-17 15:52:40 +01:00
Lee Bousfield
efc51eb7d1 managers: Use primary backend for cursor swapchain (#9645) 2025-03-17 15:51:18 +01:00
nyx
c2835b6b0f groupbar: remove recursive window recalc (#9561) 2025-03-17 15:50:49 +01:00
Mihai Fufezan
d5d7f69d1e flake.lock: update
aquamarine: bump to 0.8.0
hyprcursor: bump to 0.1.12
2025-03-17 14:50:35 +02:00
Ikalco
5cef2f44fe renderer: allow commits when buffer is unchanged but cursor changed (#9648) 2025-03-17 13:06:58 +01:00
Vaxry
22154fa272 opengl: simplify cm pipeline
fixes a few mistakes, and skips the CM shader in cpu instead of adding a costly branch

ref #9641
2025-03-16 21:50:20 +00:00
Mihai Fufezan
2ddd16ef28 CMake: install frag files (for real this time) 2025-03-16 19:35:37 +02:00
Mihai Fufezan
d7382aa8a1 CMake: install frag files 2025-03-16 18:27:27 +02:00
Mihai Fufezan
90306bdae6 Meson: include frags in globber 2025-03-16 16:30:50 +02:00
Vaxry
b1ab0f7539 splashes: update for 3ya 2025-03-16 03:03:37 +00:00
Tom Englund
bf5e4bf116 syncobj: dont crash compositor on protocol errors (#9627)
dont call a member on null pointer if client misbehaves.
as in the weak pointer being expired.
2025-03-15 19:57:52 +01:00
Blackilykat
4c471218c9 renderer: fix window offset for dragged windows (#9629) 2025-03-15 19:15:09 +01:00
phonetic112
e59680481d input: Fix clicking through groupbar tabs (#9606) 2025-03-15 01:22:39 +01:00
63 changed files with 761 additions and 608 deletions

View File

@@ -21,7 +21,7 @@ jobs:
- name: Build Hyprland
run: |
make all
CFLAGS=-Werror CXXFLAGS=-Werror make all
- name: Compress and package artifacts
run: |

View File

@@ -102,7 +102,7 @@ else()
endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.5)
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0)
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.1)
@@ -444,4 +444,5 @@ install(
DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING
PATTERN "*.h*")
PATTERN "*.h*"
PATTERN "*.frag")

View File

@@ -1 +1 @@
0.47.0
0.48.1

View File

@@ -139,10 +139,10 @@ animations {
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrule = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
@@ -276,14 +276,11 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# Example windowrule
# windowrule = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
windowrule = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0

24
flake.lock generated
View File

@@ -16,11 +16,11 @@
]
},
"locked": {
"lastModified": 1741934125,
"narHash": "sha256-qwI47l3aKXRpDvmCKDbLV70iVfAqhpuKqT7qYHA4KJk=",
"lastModified": 1742213273,
"narHash": "sha256-0l0vDb4anfsBu1rOs94bC73Hub+xEivgBAo6QXl2MmU=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "bea48d0bbe15fb3d758a8b6be865836c97056575",
"rev": "484b732195cc53f4536ce4bd59a5c6402b1e7ccf",
"type": "github"
},
"original": {
@@ -79,11 +79,11 @@
]
},
"locked": {
"lastModified": 1738664950,
"narHash": "sha256-xIeGNM+iivwVHkv9tHwOqoUP5dDrtees34bbFKKMZYs=",
"lastModified": 1742215578,
"narHash": "sha256-zfs71PXVVPEe56WEyNi2TJQPs0wabU4WAlq0XV7GcdE=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "7c6d165e1eb9045a996551eb9f121b6d1b30adc3",
"rev": "2fd36421c21aa87e2fe3bee11067540ae612f719",
"type": "github"
},
"original": {
@@ -276,11 +276,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1741851582,
"narHash": "sha256-cPfs8qMccim2RBgtKGF+x9IBCduRvd/N5F4nYpU0TVE=",
"lastModified": 1742069588,
"narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6607cf789e541e7873d40d3a8f7815ea92204f32",
"rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5",
"type": "github"
},
"original": {
@@ -299,11 +299,11 @@
]
},
"locked": {
"lastModified": 1741379162,
"narHash": "sha256-srpAbmJapkaqGRE3ytf3bj4XshspVR5964OX5LfjDWc=",
"lastModified": 1742058297,
"narHash": "sha256-b4SZc6TkKw8WQQssbN5O2DaCEzmFfvSTPYHlx/SFW9Y=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "b5a62751225b2f62ff3147d0a334055ebadcd5cc",
"rev": "59f17850021620cd348ad2e9c0c64f4e6325ce2a",
"type": "github"
},
"original": {

View File

@@ -165,15 +165,18 @@ int main(int argc, char** argv, char** envp) {
} else if (command[0] == "reload") {
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
if (ret != LOADSTATE_OK && notify) {
switch (ret) {
case LOADSTATE_FAIL:
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
case LOADSTATE_HEADERS_OUTDATED:
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
break;
default: break;
if (ret != LOADSTATE_OK) {
if (notify) {
switch (ret) {
case LOADSTATE_FAIL:
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
case LOADSTATE_HEADERS_OUTDATED:
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
break;
default: break;
}
}
return 1;
} else if (notify && !notifyFail) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");

View File

@@ -31,7 +31,7 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
aquamarine = dependency('aquamarine', version: '>=0.4.5')
aquamarine = dependency('aquamarine', version: '>=0.8.0')
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
@@ -89,7 +89,7 @@ endif
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
# Install headers
globber = run_command('find', 'src', '-name', '*.h*', check: true)
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.frag', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
install_headers(file, subdir: 'hyprland', preserve_path: true)

View File

@@ -2627,7 +2627,13 @@ PHLWORKSPACE CCompositor::createNewWorkspace(const WORKSPACEID& id, const MONITO
const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2;
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, getMonitorFromID(monID), NAME, SPECIAL, isEmpty));
const auto PMONITOR = getMonitorFromID(monID);
if (!PMONITOR) {
Debug::log(ERR, "BUG THIS: No pMonitor for new workspace in createNewWorkspace");
return nullptr;
}
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, PMONITOR, NAME, SPECIAL, isEmpty));
PWORKSPACE->m_fAlpha->setValueAndWarp(0);
@@ -3061,6 +3067,8 @@ bool CCompositor::shouldChangePreferredImageDescription() {
}
void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace) {
if (!m_pLastMonitor)
return;
for (const auto& rule : rules) {
if (!rule.isPersistent)
@@ -3076,6 +3084,9 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
const auto PMONITOR = getMonitorFromString(rule.monitor);
if (!rule.monitor.empty() && !PMONITOR)
continue; // don't do anything yet, as the monitor is not yet present.
if (!PWORKSPACE) {
WORKSPACEID id = rule.workspaceId;
std::string wsname = rule.workspaceName;
@@ -3092,7 +3103,7 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
}
PWORKSPACE = getWorkspaceByID(id);
if (!PWORKSPACE)
createNewWorkspace(id, PMONITOR ? PMONITOR : m_pLastMonitor.lock(), wsname, false);
createNewWorkspace(id, PMONITOR ? PMONITOR->ID : m_pLastMonitor->ID, wsname, false);
}
if (PWORKSPACE)

View File

@@ -1054,9 +1054,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
},
SConfigOptionDescription{
.value = "misc:vrr",
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only [0/1/2]",
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only, 3 - fullscreen with game or video content type [0/1/2/3]",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{0, 0, 2},
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 3},
},
SConfigOptionDescription{
.value = "misc:mouse_move_enables_dpms",
@@ -1319,6 +1319,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "xwayland:create_abstract_socket",
.description = "Create the abstract Unix domain socket for XWayland",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/*
* opengl:
@@ -1375,6 +1381,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
SConfigOptionDescription{
.value = "render:cm_fs_passthrough",
.description = "Passthrough color settings for fullscreen apps when possible",
.type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
},
SConfigOptionDescription{
.value = "render:cm_enabled",
.description = "Enable Color Management pipelines (requires restart to fully take effect)",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true},
},

View File

@@ -49,6 +49,7 @@
#include <memory>
using namespace Hyprutils::String;
using namespace Hyprutils::Animation;
using enum NContentType::eContentType;
//NOLINTNEXTLINE
extern "C" char** environ;
@@ -293,7 +294,7 @@ static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleWindowRuleV2(COMMAND, VALUE);
const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
@@ -652,6 +653,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("xwayland:enabled", Hyprlang::INT{1});
registerConfigVar("xwayland:use_nearest_neighbor", Hyprlang::INT{1});
registerConfigVar("xwayland:force_zero_scaling", Hyprlang::INT{0});
registerConfigVar("xwayland:create_abstract_socket", Hyprlang::INT{0});
registerConfigVar("opengl:nvidia_anti_flicker", Hyprlang::INT{1});
@@ -691,7 +693,8 @@ CConfigManager::CConfigManager() {
registerConfigVar("render:expand_undersized_textures", Hyprlang::INT{1});
registerConfigVar("render:xp_mode", Hyprlang::INT{0});
registerConfigVar("render:ctm_animation", Hyprlang::INT{2});
registerConfigVar("render:cm_fs_passthrough", Hyprlang::INT{1});
registerConfigVar("render:cm_fs_passthrough", Hyprlang::INT{2});
registerConfigVar("render:cm_enabled", Hyprlang::INT{1});
registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0});
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
@@ -1650,37 +1653,34 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) {
}
m->vrrActive = true;
return;
} else if (USEVRR == 2) {
} else if (USEVRR == 2 || USEVRR == 3) {
const auto PWORKSPACE = m->activeWorkspace;
if (!PWORKSPACE)
return; // ???
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
bool wantVRR = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
if (wantVRR && USEVRR == 3) {
const auto contentType = PWORKSPACE->getFullscreenWindow()->getContentType();
wantVRR = contentType == CONTENT_TYPE_GAME || contentType == CONTENT_TYPE_VIDEO;
}
if (WORKSPACEFULL) {
if (wantVRR) {
/* fullscreen */
m->vrrActive = true;
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(true);
if (!m->output->state->state().adaptiveSync) {
m->output->state->setAdaptiveSync(true);
if (!m->state.test()) {
Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name);
m->output->state->setAdaptiveSync(false);
if (!m->state.test()) {
Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name);
m->output->state->setAdaptiveSync(false);
}
}
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
} else if (!WORKSPACEFULL) {
} else {
m->vrrActive = false;
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(false);
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name);
}
}
};
@@ -2347,69 +2347,6 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma
}
std::optional<std::string> CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value
if (RULE.empty() || VALUE.empty())
return "empty rule?";
if (RULE == "unset") {
std::erase_if(m_vWindowRules, [&](const auto& other) { return other->szValue == VALUE; });
return {};
}
auto newRule = makeShared<CWindowRule>(RULE, VALUE, false);
// verify we support a rule
if (newRule->ruleType == CWindowRule::RULE_INVALID) {
Debug::log(ERR, "Invalid rule found: {}", RULE);
return "Invalid rule: " + RULE;
}
newRule->rV1Regex = {VALUE.starts_with("title:") ? VALUE.substr(6) : VALUE};
if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize"))
m_vWindowRules.insert(m_vWindowRules.begin(), newRule);
else
m_vWindowRules.emplace_back(newRule);
return {};
}
std::optional<std::string> CConfigManager::handleLayerRule(const std::string& command, const std::string& value) {
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value
if (RULE.empty() || VALUE.empty())
return "empty rule?";
if (RULE == "unset") {
std::erase_if(m_vLayerRules, [&](const auto& other) { return other->targetNamespace == VALUE; });
return {};
}
auto rule = makeShared<CLayerRule>(RULE, VALUE);
if (rule->ruleType == CLayerRule::RULE_INVALID) {
Debug::log(ERR, "Invalid rule found: {}", RULE);
return "Invalid rule found: " + RULE;
}
rule->targetNamespaceRegex = {VALUE};
m_vLayerRules.emplace_back(rule);
for (auto const& m : g_pCompositor->m_vMonitors)
for (auto const& lsl : m->m_aLayerSurfaceLayers)
for (auto const& ls : lsl)
ls->applyRules();
return {};
}
std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string& command, const std::string& value) {
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = value.substr(value.find_first_of(',') + 1);
@@ -2608,6 +2545,38 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
return {};
}
std::optional<std::string> CConfigManager::handleLayerRule(const std::string& command, const std::string& value) {
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value
if (RULE.empty() || VALUE.empty())
return "empty rule?";
if (RULE == "unset") {
std::erase_if(m_vLayerRules, [&](const auto& other) { return other->targetNamespace == VALUE; });
return {};
}
auto rule = makeShared<CLayerRule>(RULE, VALUE);
if (rule->ruleType == CLayerRule::RULE_INVALID) {
Debug::log(ERR, "Invalid rule found: {}", RULE);
return "Invalid rule found: " + RULE;
}
rule->targetNamespaceRegex = {VALUE};
m_vLayerRules.emplace_back(rule);
for (auto const& m : g_pCompositor->m_vMonitors)
for (auto const& lsl : m->m_aLayerSurfaceLayers)
for (auto const& ls : lsl)
ls->applyRules();
return {};
}
void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) {
const bool BYADDRESS = name.starts_with("address:");
std::string matchName = name;

View File

@@ -235,7 +235,6 @@ class CConfigManager {
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
std::optional<std::string> handleWindowRuleV2(const std::string&, const std::string&);
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(const std::string&, const std::string&);
std::optional<std::string> handleAnimation(const std::string&, const std::string&);

View File

@@ -152,10 +152,10 @@ animations {
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrule = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
@@ -269,7 +269,7 @@ bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
# Laptop multimedia keys for volume and LCD brightness
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
@@ -289,15 +289,12 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# Example windowrule
# windowrule = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
windowrule = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
)#";

View File

@@ -98,7 +98,7 @@ CRegion CWLSurface::computeDamage() const {
if (!m_pResource->current.texture)
return {};
CRegion damage = m_pResource->accumulateCurrentBufferDamage();
CRegion damage = m_pResource->current.accumulateBufferDamage();
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y);
const auto BUFSIZE = m_pResource->current.bufferSize;

View File

@@ -1258,7 +1258,7 @@ void CWindow::setAnimationsToMove() {
void CWindow::onWorkspaceAnimUpdate() {
// clip box for animated offsets
if (!m_bIsFloating || m_bPinned || isFullscreen()) {
if (!m_bIsFloating || m_bPinned || isFullscreen() || m_bDraggingTiled) {
m_vFloatingOffset = Vector2D(0, 0);
return;
}

View File

@@ -880,7 +880,7 @@ void Events::listener_commitWindow(void* owner, void* data) {
// tearing: if solitary, redraw it. This still might be a single surface window
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) {
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()};
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->current.accumulateBufferDamage()};
if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) {

View File

@@ -57,8 +57,7 @@ void CMonitor::onConnect(bool noRule) {
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
}
listeners.frame = output->events.frame.registerListener([this](std::any d) { onMonitorFrame(); });
@@ -1240,7 +1239,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
}
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName});
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", pWorkspace->m_iID + "," + pWorkspace->m_szName + "," + szName});
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", std::to_string(pWorkspace->m_iID) + "," + pWorkspace->m_szName + "," + szName});
g_pHyprRenderer->damageMonitor(self.lock());
@@ -1342,7 +1341,7 @@ bool CMonitor::attemptDirectScanout() {
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE);
if (!PSURFACE || !PSURFACE->current.texture || !PSURFACE->current.buffer || PSURFACE->current.buffer->buffer.expired())
if (!PSURFACE || !PSURFACE->current.texture || !PSURFACE->current.buffer)
return false;
if (PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform)
@@ -1355,9 +1354,26 @@ bool CMonitor::attemptDirectScanout() {
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer->buffer.get());
auto PBUFFER = PSURFACE->current.buffer->buffer.lock();
if (PBUFFER == output->state->state().buffer)
auto PBUFFER = PSURFACE->current.buffer->buffer;
if (PBUFFER == output->state->state().buffer) {
if (scanoutNeedsCursorUpdate) {
if (!state.test()) {
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
return false;
}
if (!output->commit()) {
Debug::log(TRACE, "attemptDirectScanout: failed to commit cursor update");
lastScanout.reset();
return false;
}
scanoutNeedsCursorUpdate = false;
}
return true;
}
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
@@ -1384,7 +1400,7 @@ bool CMonitor::attemptDirectScanout() {
clock_gettime(CLOCK_MONOTONIC, &now);
PSURFACE->presentFeedback(&now, self.lock());
output->state->addDamage(CBox{{}, vecPixelSize});
output->state->addDamage(PSURFACE->current.accumulateBufferDamage());
output->state->resetExplicitFences();
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
@@ -1404,8 +1420,6 @@ bool CMonitor::attemptDirectScanout() {
}
}
commitSeq++;
bool ok = output->commit();
if (!ok && DOEXPLICIT) {
@@ -1426,6 +1440,8 @@ bool CMonitor::attemptDirectScanout() {
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
}
scanoutNeedsCursorUpdate = false;
if (!PBUFFER->lockedByBackend || PBUFFER->hlEvents.backendRelease)
return true;

View File

@@ -57,6 +57,7 @@ struct SMonitorRule {
class CMonitor;
class CSyncTimeline;
class CEGLSync;
class CMonitorState {
public:
@@ -139,9 +140,9 @@ class CMonitor {
// explicit sync
SP<CSyncTimeline> inTimeline;
SP<CSyncTimeline> outTimeline;
Hyprutils::OS::CFileDescriptor inFence;
uint64_t commitSeq = 0;
SP<CEGLSync> eglSync;
uint64_t inTimelinePoint = 0;
PHLMONITORREF self;
@@ -158,6 +159,7 @@ class CMonitor {
// for direct scanout
PHLWINDOWREF lastScanout;
bool scanoutNeedsCursorUpdate = false;
struct {
bool canTear = false;

View File

@@ -58,7 +58,7 @@ namespace NSplashes {
"Thanks ThatOneCalculator!",
"The AUR packages always work, except for the times they don't.",
"Funny animation compositor woo",
"2 years!",
"3 years!",
// music reference / quote section
"J'remue le ciel, le jour, la nuit.",
"aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!",

View File

@@ -19,12 +19,13 @@ SP<CSyncTimeline> CSyncTimeline::create(int drmFD_) {
return timeline;
}
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, int drmSyncobjFD) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
timeline->drmFD = drmFD_;
timeline->self = timeline;
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobjFD) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
timeline->drmFD = drmFD_;
timeline->syncobjFd = std::move(drmSyncobjFD);
timeline->self = timeline;
if (drmSyncobjFDToHandle(drmFD_, drmSyncobjFD, &timeline->handle)) {
if (drmSyncobjFDToHandle(drmFD_, timeline->syncobjFd.get(), &timeline->handle)) {
Debug::log(ERR, "CSyncTimeline: failed to create a drm syncobj from fd??");
return nullptr;
}

View File

@@ -17,7 +17,7 @@ struct wl_event_source;
class CSyncTimeline {
public:
static SP<CSyncTimeline> create(int drmFD_);
static SP<CSyncTimeline> create(int drmFD_, int drmSyncobjFD);
static SP<CSyncTimeline> create(int drmFD_, Hyprutils::OS::CFileDescriptor&& drmSyncobjFD);
~CSyncTimeline();
struct SWaiter {
@@ -40,7 +40,8 @@ class CSyncTimeline {
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
void signal(uint64_t point);
int drmFD = -1;
int drmFD = -1;
Hyprutils::OS::CFileDescriptor syncobjFd;
uint32_t handle = 0;
WP<CSyncTimeline> self;

View File

@@ -89,6 +89,7 @@
{ \
Debug::log(CRIT, "\n\nMEMORY CORRUPTED: Unreachable failed! (Reached an unreachable position, memory corruption!!!)"); \
raise(SIGABRT); \
std::unreachable(); \
}
#else
#define UNREACHABLE() std::unreachable();

View File

@@ -323,6 +323,8 @@ void CPointerManager::onCursorMoved() {
const auto CURSORPOS = getCursorPosForMonitor(m);
m->output->moveCursor(CURSORPOS, m->shouldSkipScheduleFrameOnMouseEvent());
state->monitor->scanoutNeedsCursorUpdate = true;
}
if (recalc)
@@ -382,6 +384,8 @@ bool CPointerManager::setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquam
if (!state->monitor->shouldSkipScheduleFrameOnMouseEvent())
g_pCompositor->scheduleFrameForMonitor(state->monitor.lock(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE);
state->monitor->scanoutNeedsCursorUpdate = true;
return true;
}
@@ -419,7 +423,9 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
}
}
state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(allocator, state->monitor->output->getBackend());
auto backend = state->monitor->output->getBackend();
auto primary = backend->getPrimary();
state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(allocator, primary ? primary.lock() : backend);
}
auto options = state->monitor->cursorSwapchain->currentOptions();
@@ -615,14 +621,10 @@ void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, timespec* no
box.y = std::round(box.y);
CTexPassElement::SRenderData data;
data.tex = texture;
data.box = box.round();
data.syncTimeline = currentCursorImage.waitTimeline;
data.syncPoint = currentCursorImage.waitPoint;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
data.tex = texture;
data.box = box.round();
currentCursorImage.waitTimeline.reset();
currentCursorImage.waitPoint = 0;
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
if (currentCursorImage.surface)
currentCursorImage.surface->resource()->frame(now);

View File

@@ -148,8 +148,6 @@ class CPointerManager {
CHyprSignalListener destroySurface;
CHyprSignalListener commitSurface;
SP<CSyncTimeline> waitTimeline = nullptr;
uint64_t waitPoint = 0;
} currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors
Vector2D pointerPos = {0, 0};

View File

@@ -98,6 +98,7 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) {
CProtocolManager::CProtocolManager() {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
static const auto PENABLEXXCM = CConfigValue<Hyprlang::INT>("experimental:xx_color_management_v4");
static const auto PDEBUGCM = CConfigValue<Hyprlang::INT>("debug:full_cm_proto");
@@ -181,14 +182,17 @@ CProtocolManager::CProtocolManager() {
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");
PROTO::colorManagement = makeUnique<CColorManagementProtocol>(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM);
// please read the top of this file before adding another protocol
if (*PENABLEXXCM) {
if (*PENABLECM)
PROTO::colorManagement = makeUnique<CColorManagementProtocol>(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM);
if (*PENABLEXXCM && *PENABLECM) {
PROTO::xxColorManagement = makeUnique<CXXColorManagementProtocol>(&xx_color_manager_v4_interface, 1, "XXColorManagement");
PROTO::frogColorManagement = makeUnique<CFrogColorManagementProtocol>(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement");
}
// ! please read the top of this file before adding another protocol
for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
continue;

View File

@@ -606,11 +606,11 @@ void CSeatManager::setGrab(SP<CSeatGrab> grab) {
if (!refocus) {
surf = CWLSurface::fromResource(currentFocus);
layer = surf->getLayer();
layer = surf ? surf->getLayer() : nullptr;
}
if (!refocus && !layer) {
auto popup = surf->getPopup();
auto popup = surf ? surf->getPopup() : nullptr;
if (popup) {
auto parent = popup->getT1Owner();
layer = parent->getLayer();

View File

@@ -293,9 +293,10 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) {
const auto BOX = HLSurface->getSurfaceBoxGlobal();
if (BOX) {
const auto PWINDOW = HLSurface->getWindow();
surfacePos = BOX->pos();
pFoundLayerSurface = HLSurface->getLayer();
pFoundWindow = HLSurface->getWindow();
pFoundWindow = !PWINDOW || PWINDOW->isHidden() ? g_pCompositor->m_pLastWindow.lock() : PWINDOW;
} else // reset foundSurface, find one normally
foundSurface = nullptr;
} else // reset foundSurface, find one normally

View File

@@ -59,6 +59,13 @@ struct SVersionInfo {
#define OPTIONAL
#define HANDLE void*
// C ABI is needed to prevent symbol mangling, but we don't actually need C compatibility,
// so we ignore this warning about return types that are potentially incompatible with C.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
#endif
class IHyprLayout;
class CWindow;
class IHyprWindowDecoration;
@@ -315,3 +322,7 @@ APICALL inline EXPORT const char* __hyprland_api_get_client_hash() {
return GIT_COMMIT_HASH;
}
// NOLINTEND
#ifdef __clang__
#pragma clang diagnostic pop
#endif

View File

@@ -9,51 +9,27 @@
#include <fcntl.h>
using namespace Hyprutils::OS;
CDRMSyncPointState::CDRMSyncPointState(WP<CDRMSyncobjTimelineResource> resource_, uint64_t point_, bool acquirePoint) :
m_resource(resource_), m_point(point_), m_acquirePoint(acquirePoint) {}
CDRMSyncPointState::CDRMSyncPointState(SP<CSyncTimeline> timeline_, uint64_t point_) : m_timeline(timeline_), m_point(point_) {}
const uint64_t& CDRMSyncPointState::point() {
return m_point;
}
WP<CDRMSyncobjTimelineResource> CDRMSyncPointState::resource() {
return m_resource;
}
WP<CSyncTimeline> CDRMSyncPointState::timeline() {
if (expired()) {
Debug::log(ERR, "CDRMSyncPointState: getting a timeline on a expired point");
return {};
}
return m_resource->timeline;
}
bool CDRMSyncPointState::expired() {
return !m_resource || !m_resource->timeline;
return m_timeline;
}
UP<CSyncReleaser> CDRMSyncPointState::createSyncRelease() {
if (expired()) {
Debug::log(ERR, "CDRMSyncPointState: creating a sync releaser on an expired point");
return nullptr;
}
if (m_releaseTaken)
Debug::log(ERR, "CDRMSyncPointState: creating a sync releaser on an already created SyncRelease");
m_releaseTaken = true;
return makeUnique<CSyncReleaser>(m_resource->timeline, m_point);
return makeUnique<CSyncReleaser>(m_timeline, m_point);
}
bool CDRMSyncPointState::addWaiter(const std::function<void()>& waiter) {
if (expired()) {
Debug::log(ERR, "CDRMSyncPointState: adding a waiter on an expired point");
return false;
}
m_acquireCommitted = true;
return m_resource->timeline->addWaiter(waiter, m_point, 0u);
return m_timeline->addWaiter(waiter, m_point, 0u);
}
bool CDRMSyncPointState::comitted() {
@@ -61,21 +37,11 @@ bool CDRMSyncPointState::comitted() {
}
CFileDescriptor CDRMSyncPointState::exportAsFD() {
if (expired()) {
Debug::log(ERR, "CDRMSyncPointState: exporting a FD on an expired point");
return {};
}
return m_resource->timeline->exportAsSyncFileFD(m_point);
return m_timeline->exportAsSyncFileFD(m_point);
}
void CDRMSyncPointState::signal() {
if (expired()) {
Debug::log(ERR, "CDRMSyncPointState: signaling on an expired point");
return;
}
m_resource->timeline->signal(m_point);
m_timeline->signal(m_point);
}
CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurfaceV1>&& resource_, SP<CWLSurfaceResource> surface_) :
@@ -95,7 +61,7 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
}
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
pendingAcquire = {timeline, ((uint64_t)hi << 32) | (uint64_t)lo, true};
pendingAcquire = {timeline->timeline, ((uint64_t)hi << 32) | (uint64_t)lo};
});
resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
@@ -105,34 +71,36 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
}
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
pendingRelease = {timeline, ((uint64_t)hi << 32) | (uint64_t)lo, false};
pendingRelease = {timeline->timeline, ((uint64_t)hi << 32) | (uint64_t)lo};
});
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!surface->pending.buffer && surface->pending.newBuffer && !surface->pending.texture) {
const bool PENDING_HAS_NEW_BUFFER = surface->pending.updated & SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER;
if (!surface->pending.buffer && PENDING_HAS_NEW_BUFFER && !surface->pending.texture) {
removeAllWaiters();
surface->commitPendingState(surface->pending);
return; // null buffer attached.
}
if (!surface->pending.buffer && !surface->pending.newBuffer && surface->current.buffer) {
if (!surface->pending.buffer && !PENDING_HAS_NEW_BUFFER && surface->current.buffer) {
surface->current.bufferDamage.clear();
surface->current.damage.clear();
surface->commitPendingState(surface->current);
return; // no new buffer, but we still have current around and a commit happend, commit current again.
}
if (!surface->pending.buffer && !surface->pending.newBuffer && !surface->current.buffer) {
if (!surface->pending.buffer && !PENDING_HAS_NEW_BUFFER && !surface->current.buffer) {
surface->commitPendingState(surface->pending); // no pending buffer, no current buffer. probably first commit
return;
}
if (!pendingAcquire.expired()) {
if (pendingAcquire.timeline()) {
surface->pending.buffer->acquire = makeUnique<CDRMSyncPointState>(std::move(pendingAcquire));
pendingAcquire = {};
}
if (!pendingRelease.expired()) {
if (pendingRelease.timeline()) {
surface->pending.buffer->release = makeUnique<CDRMSyncPointState>(std::move(pendingRelease));
pendingRelease = {};
}
@@ -143,7 +111,8 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
const auto& state = pendingStates.emplace_back(makeShared<SSurfaceState>(surface->pending));
surface->pending.damage.clear();
surface->pending.bufferDamage.clear();
surface->pending.newBuffer = false;
surface->pending.updated &= ~SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER;
surface->pending.updated &= ~SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
surface->pending.buffer.reset();
state->buffer->buffer->syncReleaser = state->buffer->release->createSyncRelease();
@@ -159,8 +128,8 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP<CWpLinuxDrmSyncobjSurf
void CDRMSyncobjSurfaceResource::removeAllWaiters() {
for (auto& s : pendingStates) {
if (s && s->buffer && s->buffer->acquire && !s->buffer->acquire->expired())
s->buffer->acquire->resource()->timeline->removeAllWaiters();
if (s && s->buffer && s->buffer->acquire)
s->buffer->acquire->timeline()->removeAllWaiters();
}
pendingStates.clear();
@@ -171,15 +140,20 @@ CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() {
}
bool CDRMSyncobjSurfaceResource::protocolError() {
if (!surface->pending.texture) {
if (!surface->pending.buffer) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
surface->pending.rejected = true;
return true;
}
if (!!surface->pending.buffer->acquire != !!surface->pending.buffer->release) {
resource->error(surface->pending.buffer->acquire ? WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT : WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT,
"Missing timeline");
if (!surface->pending.buffer->acquire || !surface->pending.buffer->acquire->timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline");
surface->pending.rejected = true;
return true;
}
if (!surface->pending.buffer->release || !surface->pending.buffer->release->timeline()) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline");
surface->pending.rejected = true;
return true;
}
@@ -208,7 +182,7 @@ CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(UP<CWpLinuxDrmSyncobjTi
resource->setOnDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); });
resource->setDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); });
timeline = CSyncTimeline::create(PROTO::sync->drmFD, fd.get());
timeline = CSyncTimeline::create(PROTO::sync->drmFD, std::move(fd));
if (!timeline) {
resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Timeline failed importing");

View File

@@ -17,13 +17,11 @@ struct SSurfaceState;
class CDRMSyncPointState {
public:
CDRMSyncPointState() = default;
CDRMSyncPointState(WP<CDRMSyncobjTimelineResource> resource_, uint64_t point_, bool acquirePoint);
CDRMSyncPointState(SP<CSyncTimeline> timeline_, uint64_t point_);
~CDRMSyncPointState() = default;
const uint64_t& point();
WP<CDRMSyncobjTimelineResource> resource();
WP<CSyncTimeline> timeline();
bool expired();
Hyprutils::Memory::CUniquePointer<CSyncReleaser> createSyncRelease();
bool addWaiter(const std::function<void()>& waiter);
bool comitted();
@@ -31,12 +29,10 @@ class CDRMSyncPointState {
void signal();
private:
WP<CDRMSyncobjTimelineResource> m_resource = {};
uint64_t m_point = 0;
WP<CSyncTimeline> m_timeline = {};
bool m_acquirePoint = false;
bool m_acquireCommitted = false;
bool m_releaseTaken = false;
SP<CSyncTimeline> m_timeline = {};
uint64_t m_point = 0;
bool m_acquireCommitted = false;
bool m_releaseTaken = false;
};
class CDRMSyncobjSurfaceResource {

View File

@@ -1,5 +1,6 @@
#include "FrogColorManagement.hpp"
#include "color-management-v1.hpp"
#include "macros.hpp"
#include "protocols/ColorManagement.hpp"
#include "protocols/core/Subcompositor.hpp"
#include "protocols/types/ColorManagement.hpp"
@@ -13,6 +14,7 @@ static wpColorManagerV1TransferFunction getWPTransferFunction(frogColorManagedSu
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ;
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
default: UNREACHABLE();
}
}
@@ -103,9 +105,10 @@ CFrogColorManagementSurface::CFrogColorManagementSurface(SP<CFrogColorManagedSur
surface->colorManagement->m_imageDescription.transferFunction =
convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ));
break;
}; // intended fall through
};
[[fallthrough]];
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED:
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: LOGM(TRACE, "FIXME: add tf support for {}", (uint32_t)tf); // intended fall through
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: LOGM(TRACE, "FIXME: add tf support for {}", (uint32_t)tf); [[fallthrough]];
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB:
surface->colorManagement->m_imageDescription.transferFunction = convertTransferFunction(getWPTransferFunction(tf));

View File

@@ -221,6 +221,7 @@ void CLinuxDMABUFParamsResource::create(uint32_t id) {
if UNLIKELY (!buf->good() || !buf->buffer->success) {
resource->sendFailed();
PROTO::linuxDma->m_vBuffers.pop_back();
return;
}

View File

@@ -33,7 +33,6 @@ class CPointerConstraint {
private:
SP<CZwpLockedPointerV1> resourceL;
SP<CZwpConfinedPointerV1> resourceC;
wl_client* pClient = nullptr;
WP<CWLSurface> pHLSurface;

View File

@@ -12,6 +12,7 @@
#include "../helpers/Format.hpp"
#include <algorithm>
#include <functional>
CScreencopyFrame::~CScreencopyFrame() {
if (buffer && buffer->locked())
@@ -174,39 +175,42 @@ void CScreencopyFrame::share() {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (bufferDMA) {
if (!copyDmabuf()) {
LOGM(ERR, "Dmabuf copy failed in {:x}", (uintptr_t)this);
auto callback = [this, now, weak = self](bool success) {
if (weak.expired())
return;
if (!success) {
LOGM(ERR, "{} copy failed in {:x}", bufferDMA ? "Dmabuf" : "Shm", (uintptr_t)this);
resource->sendFailed();
return;
}
} else {
if (!copyShm()) {
LOGM(ERR, "Shm copy failed in {:x}", (uintptr_t)this);
resource->sendFailed();
return;
resource->sendFlags((zwlrScreencopyFrameV1Flags)0);
if (withDamage) {
// TODO: add a damage ring for this.
resource->sendDamage(0, 0, buffer->size.x, buffer->size.y);
}
}
resource->sendFlags((zwlrScreencopyFrameV1Flags)0);
if (withDamage) {
// TODO: add a damage ring for this.
resource->sendDamage(0, 0, buffer->size.x, buffer->size.y);
}
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec);
};
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec);
if (bufferDMA)
copyDmabuf(callback);
else
callback(copyShm());
}
bool CScreencopyFrame::copyDmabuf() {
void CScreencopyFrame::copyDmabuf(std::function<void(bool)> callback) {
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock(), nullptr, true)) {
LOGM(ERR, "Can't copy: failed to begin rendering to dma frame");
return false;
callback(false);
return;
}
CBox monbox = CBox{0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y}
@@ -221,9 +225,18 @@ bool CScreencopyFrame::copyDmabuf() {
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
g_pHyprRenderer->endRender();
LOGM(TRACE, "Copied frame via dma");
return true;
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(pMonitor->output);
if (pMonitor->inTimeline && explicitOptions.explicitEnabled) {
pMonitor->inTimeline->addWaiter(
[callback]() {
LOGM(TRACE, "Copied frame via dma with explicit sync");
callback(true);
},
pMonitor->inTimelinePoint, 0);
} else {
LOGM(TRACE, "Copied frame via dma");
callback(true);
}
}
bool CScreencopyFrame::copyShm() {
@@ -278,6 +291,8 @@ bool CScreencopyFrame::copyShm() {
const auto drmFmt = NFormatUtils::getPixelFormatFromDRM(shm.format);
uint32_t packStride = NFormatUtils::minStride(drmFmt, box.w);
// This could be optimized by using a pixel buffer object to make this async,
// but really clients should just use a dma buffer anyways.
if (packStride == (uint32_t)shm.stride) {
glReadPixels(0, 0, box.w, box.h, glFormat, PFORMAT->glType, pixelData);
} else {

View File

@@ -73,7 +73,7 @@ class CScreencopyFrame {
CBox box = {};
void copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer);
bool copyDmabuf();
void copyDmabuf(std::function<void(bool)> callback);
bool copyShm();
void share();

View File

@@ -52,7 +52,7 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
});
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!surface || !surface->pending.texture)
if (!surface || !surface->pending.buffer)
return;
if (surface->pending.viewport.hasSource) {

View File

@@ -18,7 +18,6 @@ class CXDGActivationToken {
uint32_t serial = 0;
std::string appID = "";
bool committed = false;
bool used = false;
std::string token = "";

View File

@@ -319,8 +319,11 @@ uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
void CXDGToplevelResource::applyState() {
wl_array arr;
wl_array_init(&arr);
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
if (!pendingApply.states.empty()) {
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
}
resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr);
@@ -387,7 +390,7 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
if (toplevel)
toplevel->current = toplevel->pending;
if UNLIKELY (initialCommit && surface->pending.texture) {
if UNLIKELY (initialCommit && surface->pending.buffer) {
resource->error(-1, "Buffer attached before initial commit");
return;
}

View File

@@ -23,6 +23,7 @@ static wpColorManagerV1TransferFunction getWPTransferFunction(xxColorManagerV4Tr
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST428: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG;
default: UNREACHABLE();
}
}

View File

@@ -12,6 +12,8 @@
#include "../DRMSyncobj.hpp"
#include "../../render/Renderer.hpp"
#include "config/ConfigValue.hpp"
#include "protocols/types/SurfaceRole.hpp"
#include "render/Texture.hpp"
#include <cstring>
class CDefaultSurfaceRole : public ISurfaceRole {
@@ -69,12 +71,13 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
resource->setOnDestroy([this](CWlSurface* r) { destroy(); });
resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) {
pending.offset = {x, y};
pending.newBuffer = true;
pending.offset = {x, y};
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET;
if (!buffer) {
pending.buffer.reset();
pending.texture.reset();
pending.bufferSize = Vector2D{};
} else {
auto res = CWLBufferResource::fromResource(buffer);
pending.buffer = res && res->buffer ? makeShared<CHLBufferReference>(res->buffer.lock(), self.lock()) : nullptr;
@@ -83,18 +86,15 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{};
}
Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{};
Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{};
if (oldBufSize != newBufSize || current.buffer != pending.buffer)
if (pending.bufferSize != current.bufferSize)
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
resource->setCommit([this](CWlSurface* r) {
if (pending.texture)
if (pending.buffer)
pending.bufferDamage.intersect(CBox{{}, pending.bufferSize});
if (!pending.texture)
if (!pending.buffer)
pending.size = {};
else if (pending.viewport.hasDestination)
pending.size = pending.viewport.destination;
@@ -117,11 +117,29 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
commitPendingState(pending);
});
resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.damage.add(CBox{x, y, w, h}); });
resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.bufferDamage.add(CBox{x, y, w, h}); });
resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.damage.add(CBox{x, y, w, h});
});
resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.bufferDamage.add(CBox{x, y, w, h});
});
resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { pending.scale = scale; });
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { pending.transform = (wl_output_transform)tr; });
resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) {
if (scale == pending.scale)
return;
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_SCALE | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.scale = scale;
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) {
if (tr == pending.transform)
return;
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_TRANSFORM | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_DAMAGE;
pending.transform = (wl_output_transform)tr;
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
});
resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) {
if (!region) {
@@ -129,6 +147,8 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return;
}
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_INPUT;
auto RG = CWLRegionResource::fromResource(region);
pending.input = RG->region;
});
@@ -139,13 +159,18 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return;
}
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OPAQUE;
auto RG = CWLRegionResource::fromResource(region);
pending.opaque = RG->region;
});
resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared<CWLCallbackResource>(makeShared<CWlCallback>(pClient, 1, id))); });
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { pending.offset = {x, y}; });
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) {
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET;
pending.offset = {x, y};
});
}
CWLSurfaceResource::~CWLSurfaceResource() {
@@ -399,58 +424,24 @@ CBox CWLSurfaceResource::extends() {
return full.getExtents();
}
Vector2D CWLSurfaceResource::sourceSize() {
if UNLIKELY (!current.texture)
return {};
if UNLIKELY (current.viewport.hasSource)
return current.viewport.source.size();
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize;
return trc / current.scale;
}
CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
if UNLIKELY (!current.texture)
return {};
CRegion surfaceDamage = current.damage;
if (current.viewport.hasDestination) {
Vector2D scale = sourceSize() / current.viewport.destination;
surfaceDamage.scale(scale);
}
if (current.viewport.hasSource)
surfaceDamage.translate(current.viewport.source.pos());
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize;
return surfaceDamage.scale(current.scale).transform(wlTransformToHyprutils(invertTransform(current.transform)), trc.x, trc.y).add(current.bufferDamage);
}
void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
if (state.newBuffer) {
state.newBuffer = false;
current = state;
state.damage.clear();
state.bufferDamage.clear();
state.buffer.reset();
auto lastTexture = current.texture;
current.updateFrom(state);
state.updated = 0;
if (current.buffer) {
if (current.buffer->buffer->isSynchronous())
current.updateSynchronousTexture(lastTexture);
// if the surface is a cursor, update the shm buffer
// TODO: don't update the entire texture
if (role->role() == SURFACE_ROLE_CURSOR)
updateCursorShm(current.accumulateBufferDamage());
}
if (current.texture)
current.texture->m_eTransform = wlTransformToHyprutils(current.transform);
if (current.buffer && current.buffer->buffer) {
const auto DAMAGE = accumulateCurrentBufferDamage();
current.buffer->buffer->update(DAMAGE);
// if the surface is a cursor, update the shm buffer
// TODO: don't update the entire texture
if (role->role() == SURFACE_ROLE_CURSOR && !DAMAGE.empty())
updateCursorShm(DAMAGE);
}
// TODO: we should _accumulate_ and not replace above if sync
if (role->role() == SURFACE_ROLE_SUBSURFACE) {
auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock();
if (subsurface->sync)
@@ -471,16 +462,18 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
nullptr);
}
// release the buffer if it's synchronous as update() has done everything thats needed
// release the buffer if it's synchronous (SHM) as update() has done everything thats needed
// so we can let the app know we're done.
// if (!syncobj && current.buffer && current.buffer->buffer && current.buffer->buffer->isSynchronous()) {
// dropCurrentBuffer(); // lets not drop it at all, it will get dropped on next commit if a new buffer arrives.
// solves flickering on nonsyncobj apps on explicit sync.
// }
// if it doesn't have a role, we can't release it yet, in case it gets turned into a cursor.
if (current.buffer && current.buffer->buffer && current.buffer->buffer->isSynchronous() && role->role() != SURFACE_ROLE_UNASSIGNED)
dropCurrentBuffer();
}
void CWLSurfaceResource::updateCursorShm(CRegion damage) {
auto buf = current.buffer ? current.buffer->buffer : WP<IHLBuffer>{};
if (damage.empty())
return;
auto buf = current.buffer ? current.buffer->buffer : SP<IHLBuffer>{};
if UNLIKELY (!buf)
return;
@@ -524,7 +517,7 @@ void CWLSurfaceResource::presentFeedback(timespec* when, PHLMONITOR pMonitor, bo
FEEDBACK->presented();
PROTO::presentation->queueData(FEEDBACK);
if (!pMonitor || !pMonitor->outTimeline || !syncobj)
if (!pMonitor || !pMonitor->inTimeline || !syncobj)
return;
// attach explicit sync

View File

@@ -11,6 +11,7 @@
#include <vector>
#include <cstdint>
#include "../WaylandProtocol.hpp"
#include "../../render/Texture.hpp"
#include "wayland.hpp"
#include "../../helpers/signal/Signal.hpp"
#include "../../helpers/math/Math.hpp"
@@ -75,7 +76,6 @@ class CWLSurfaceResource {
SP<CWlSurface> getResource();
CBox extends();
void resetRole();
Vector2D sourceSize();
struct {
CSignal precommit; // before commit
@@ -102,7 +102,6 @@ class CWLSurfaceResource {
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
SP<CWLSurfaceResource> findFirstPreorder(std::function<bool(SP<CWLSurfaceResource>)> fn);
CRegion accumulateCurrentBufferDamage();
void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false);
void commitPendingState(SSurfaceState& state);

View File

@@ -50,7 +50,6 @@ class CWLDataOfferResource : public IDataOffer {
private:
SP<CWlDataOffer> resource;
wl_client* pClient = nullptr;
friend class CWLDataDeviceResource;
};
@@ -89,7 +88,6 @@ class CWLDataSourceResource : public IDataSource {
private:
SP<CWlDataSource> resource;
wl_client* pClient = nullptr;
friend class CWLDataDeviceProtocol;
};

View File

@@ -22,7 +22,7 @@ bool CWLTouchResource::good() {
}
void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner)
if (!owner || !surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
@@ -124,7 +124,7 @@ CWLPointerResource::CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResou
return;
}
if (surfResource) {
if (surfResource && surfResource->role->role() != SURFACE_ROLE_CURSOR) {
surfResource->role = makeShared<CCursorSurfaceRole>();
surfResource->updateCursorShm();
}
@@ -145,7 +145,7 @@ bool CWLPointerResource::good() {
}
void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local) {
if (!owner || currentSurface == surface)
if (!owner || currentSurface == surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
@@ -165,7 +165,7 @@ void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2
}
void CWLPointerResource::sendLeave() {
if (!owner || !currentSurface)
if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
@@ -336,7 +336,7 @@ void CWLKeyboardResource::sendKeymap(SP<IKeyboard> keyboard) {
}
void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
if (!owner || currentSurface == surface)
if (!owner || currentSurface == surface || !surface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
@@ -361,7 +361,7 @@ void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
}
void CWLKeyboardResource::sendLeave() {
if (!owner || !currentSurface)
if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))

View File

@@ -22,19 +22,12 @@ CWLSHMBuffer::CWLSHMBuffer(SP<CWLSHMPoolResource> pool_, uint32_t id, int32_t of
offset = offset_;
opaque = NFormatUtils::isFormatOpaque(NFormatUtils::shmToDRM(fmt_));
texture = makeShared<CTexture>(NFormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, size_);
resource = CWLBufferResource::create(makeShared<CWlBuffer>(pool_->resource->client(), 1, id));
listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) {
listeners.bufferResourceDestroy.reset();
PROTO::shm->destroyResource(this);
});
success = texture->m_iTexID;
if UNLIKELY (!success)
Debug::log(ERR, "Failed creating a shm texture: null texture id");
}
CWLSHMBuffer::~CWLSHMBuffer() {
@@ -74,11 +67,11 @@ void CWLSHMBuffer::endDataPtr() {
}
bool CWLSHMBuffer::good() {
return success;
return true;
}
void CWLSHMBuffer::update(const CRegion& damage) {
texture->update(NFormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, damage);
;
}
CSHMPool::CSHMPool(CFileDescriptor fd_, size_t size_) : fd(std::move(fd_)), size(size_), data(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)) {

View File

@@ -50,8 +50,6 @@ class CWLSHMBuffer : public IHLBuffer {
SP<CSHMPool> pool;
private:
bool success = false;
struct {
CHyprSignalListener bufferResourceDestroy;
} listeners;

View File

@@ -45,7 +45,7 @@ class CHLBufferReference {
CHLBufferReference(SP<IHLBuffer> buffer, SP<CWLSurfaceResource> surface);
~CHLBufferReference();
WP<IHLBuffer> buffer;
SP<IHLBuffer> buffer;
UP<CDRMSyncPointState> acquire;
UP<CDRMSyncPointState> release;

View File

@@ -0,0 +1,93 @@
#include "SurfaceState.hpp"
#include "helpers/Format.hpp"
#include "protocols/types/Buffer.hpp"
#include "render/Texture.hpp"
Vector2D SSurfaceState::sourceSize() {
if UNLIKELY (!texture)
return {};
if UNLIKELY (viewport.hasSource)
return viewport.source.size();
Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize;
return trc / scale;
}
CRegion SSurfaceState::accumulateBufferDamage() {
if (damage.empty())
return bufferDamage;
CRegion surfaceDamage = damage;
if (viewport.hasDestination) {
Vector2D scale = sourceSize() / viewport.destination;
surfaceDamage.scale(scale);
}
if (viewport.hasSource)
surfaceDamage.translate(viewport.source.pos());
Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize;
bufferDamage = surfaceDamage.scale(scale).transform(wlTransformToHyprutils(invertTransform(transform)), trc.x, trc.y).add(bufferDamage);
damage.clear();
return bufferDamage;
}
void SSurfaceState::updateSynchronousTexture(SP<CTexture> lastTexture) {
auto [dataPtr, fmt, size] = buffer->buffer->beginDataPtr(0);
if (dataPtr) {
auto drmFmt = NFormatUtils::shmToDRM(fmt);
auto stride = bufferSize.y ? size / bufferSize.y : 0;
if (lastTexture && lastTexture->m_isSynchronous && lastTexture->m_vSize == bufferSize) {
texture = lastTexture;
texture->update(drmFmt, dataPtr, stride, accumulateBufferDamage());
} else
texture = makeShared<CTexture>(drmFmt, dataPtr, stride, bufferSize);
}
buffer->buffer->endDataPtr();
}
void SSurfaceState::reset() {
damage.clear();
bufferDamage.clear();
transform = WL_OUTPUT_TRANSFORM_NORMAL;
scale = 1;
offset = {};
size = {};
}
void SSurfaceState::updateFrom(SSurfaceState& ref) {
updated = ref.updated;
if (ref.updated & SURFACE_UPDATED_BUFFER) {
ref.updated &= ~SURFACE_UPDATED_BUFFER;
*this = ref;
ref.damage.clear();
ref.bufferDamage.clear();
ref.buffer.reset();
} else {
if (ref.updated & SURFACE_UPDATED_DAMAGE) {
damage = ref.damage;
bufferDamage = ref.bufferDamage;
}
if (ref.updated & SURFACE_UPDATED_INPUT)
input = ref.input;
if (ref.updated & SURFACE_UPDATED_OPAQUE)
opaque = ref.opaque;
if (ref.updated & SURFACE_UPDATED_OFFSET)
offset = ref.offset;
if (ref.updated & SURFACE_UPDATED_SCALE)
scale = ref.scale;
if (ref.updated & SURFACE_UPDATED_VIEWPORT)
viewport = ref.viewport;
if (ref.updated & SURFACE_UPDATED_TRANSFORM)
transform = ref.transform;
}
}

View File

@@ -7,6 +7,17 @@ class CHLBufferReference;
class CTexture;
struct SSurfaceState {
enum eUpdatedProperties : uint8_t {
SURFACE_UPDATED_OPAQUE = 1 << 0,
SURFACE_UPDATED_INPUT = 1 << 1,
SURFACE_UPDATED_DAMAGE = 1 << 2,
SURFACE_UPDATED_SCALE = 1 << 3,
SURFACE_UPDATED_BUFFER = 1 << 4,
SURFACE_UPDATED_OFFSET = 1 << 5,
SURFACE_UPDATED_VIEWPORT = 1 << 6,
SURFACE_UPDATED_TRANSFORM = 1 << 7,
};
CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
int scale = 1;
@@ -20,16 +31,15 @@ struct SSurfaceState {
Vector2D destination;
CBox source;
} viewport;
bool rejected = false;
bool newBuffer = false;
bool rejected = false;
uint8_t updated = 0; // eUpdatedProperties. Stores what the last update changed
//
void reset() {
damage.clear();
bufferDamage.clear();
transform = WL_OUTPUT_TRANSFORM_NORMAL;
scale = 1;
offset = {};
size = {};
}
Vector2D sourceSize();
// Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage
CRegion accumulateBufferDamage();
void updateSynchronousTexture(SP<CTexture> lastTexture);
void reset();
// updates this state from a reference state. Mutates the reference state. If a new buffer is committed,
// reference state gets its damage and buffer cleared.
void updateFrom(SSurfaceState& ref);
};

View File

@@ -158,6 +158,7 @@ void CHyprOpenGLImpl::initEGL(bool gbm) {
#else
attrs.push_back(EGL_CONTEXT_CLIENT_VERSION);
attrs.push_back(2);
m_eglContextVersion = EGL_CONTEXT_GLES_2_0;
#endif
attrs.push_back(EGL_NONE);
@@ -176,7 +177,8 @@ void CHyprOpenGLImpl::initEGL(bool gbm) {
attrs.push_back(0);
attrs.push_back(EGL_NONE);
m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data());
m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data());
m_eglContextVersion = EGL_CONTEXT_GLES_3_0;
if (m_pEglContext == EGL_NO_CONTEXT)
RASSERT(false, "EGL: failed to create a context with either GLES3.2 or 3.0");
@@ -564,7 +566,7 @@ EGLImageKHR CHyprOpenGLImpl::createEGLImage(const Aquamarine::SDMABUFAttrs& attr
return image;
}
void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program) {
void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program, bool silent) {
GLint maxLength = 0;
if (program)
glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
@@ -582,18 +584,19 @@ void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program) {
Debug::log(ERR, "Failed to link shader: {}", FULLERROR);
g_pConfigManager->addParseError(FULLERROR);
if (!silent)
g_pConfigManager->addParseError(FULLERROR);
}
GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag, bool dynamic) {
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic);
GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag, bool dynamic, bool silent) {
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic, silent);
if (dynamic) {
if (vertCompiled == 0)
return 0;
} else
RASSERT(vertCompiled, "Compiling shader failed. VERTEX nullptr! Shader source:\n\n{}", vert);
auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag, dynamic);
auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag, dynamic, silent);
if (dynamic) {
if (fragCompiled == 0)
return 0;
@@ -614,7 +617,7 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
if (dynamic) {
if (ok == GL_FALSE) {
logShaderError(prog, true);
logShaderError(prog, true, silent);
return 0;
}
} else {
@@ -626,7 +629,7 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string
return prog;
}
GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool dynamic) {
GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool dynamic, bool silent) {
auto shader = glCreateShader(type);
auto shaderSource = src.c_str();
@@ -639,7 +642,7 @@ GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool
if (dynamic) {
if (ok == GL_FALSE) {
logShaderError(shader, false);
logShaderError(shader, false, silent);
return 0;
}
} else {
@@ -868,42 +871,43 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shQUAD.roundingPower = glGetUniformLocation(prog, "roundingPower");
#ifndef GLES2
prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM, true);
m_bCMSupported = prog > 0;
if (m_bCMSupported) {
m_RenderData.pCurrentMonData->m_shCM.program = prog;
m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj");
m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex");
m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType");
m_RenderData.pCurrentMonData->m_shCM.skipCM = glGetUniformLocation(prog, "skipCM");
m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF");
m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF");
m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries");
m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries");
m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance");
m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation");
m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier");
m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte");
m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha");
m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte");
m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha");
m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius");
m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower");
m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint");
m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint");
m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte");
} else {
Debug::log(
ERR,
"WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports about this!");
if (m_eglContextVersion == EGL_CONTEXT_GLES_3_2 /* GLES2 and GLES3.0 can't compile the CM shader */) {
prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM, true, true);
m_bCMSupported = prog > 0;
if (m_bCMSupported) {
m_RenderData.pCurrentMonData->m_shCM.program = prog;
m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj");
m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex");
m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType");
m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF");
m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF");
m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries");
m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries");
m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance");
m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation");
m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier");
m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte");
m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha");
m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte");
m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha");
m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius");
m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower");
m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint");
m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint");
m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte");
} else {
Debug::log(
ERR,
"WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports about this!");
}
}
#endif
@@ -1321,8 +1325,9 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
CBox newBox = box;
m_RenderData.renderModif.applyToBox(newBox);
static auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
static const auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
static const auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
// get the needed transform for this texture
const bool TRANSFORMS_MATCH = wlTransformToHyprutils(m_RenderData.pMonitor->transform) == tex->m_eTransform; // FIXME: combine them properly!!!
@@ -1352,27 +1357,18 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
shader = &m_RenderData.pCurrentMonData->m_shPASSTHRURGBA;
usingFinalShader = true;
} else {
#ifndef GLES2
if (m_bCMSupported)
shader = &m_RenderData.pCurrentMonData->m_shCM;
else
#endif
switch (tex->m_iType) {
case TEXTURE_RGBA: shader = &m_RenderData.pCurrentMonData->m_shRGBA; break;
case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shRGBX; break;
switch (tex->m_iType) {
case TEXTURE_RGBA: shader = &m_RenderData.pCurrentMonData->m_shRGBA; break;
case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shRGBX; break;
case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; // might be unused
default: RASSERT(false, "tex->m_iTarget unsupported!");
}
case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; // might be unused
default: RASSERT(false, "tex->m_iTarget unsupported!");
}
}
}
if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault()) {
#ifdef GLES2
shader = &m_RenderData.pCurrentMonData->m_shRGBX;
#endif
if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault())
texType = TEXTURE_RGBX;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(tex->m_iTarget, tex->m_iTexID);
@@ -1388,8 +1384,60 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
glTexParameteri(tex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
const auto imageDescription =
m_RenderData.surface.valid() && m_RenderData.surface->colorManagement.valid() ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{};
const bool skipCM = !*PENABLECM /* CM disabled by the user */
|| !m_RenderData.surface /* FIXME unknown texture settings should be treated as sRGB and go through CM if monitor isn't in sRGB mode */
|| !m_bCMSupported /* CM unsupported - hw failed to compile the shader probably */
|| (imageDescription == m_RenderData.pMonitor->imageDescription) /* Source and target have the same image description */
|| ((*PPASS == 1 || (*PPASS == 2 && imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) && m_RenderData.pMonitor->activeWorkspace &&
m_RenderData.pMonitor->activeWorkspace->m_bHasFullscreenWindow &&
m_RenderData.pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) /* Fullscreen window with pass cm enabled */;
glUseProgram(shader->program);
#ifndef GLES2
if (!skipCM && !usingFinalShader && (texType == TEXTURE_RGBA || texType == TEXTURE_RGBX)) {
shader = &m_RenderData.pCurrentMonData->m_shCM;
glUseProgram(shader->program);
glUniform1i(shader->texType, texType);
const auto imageDescription =
m_RenderData.surface && m_RenderData.surface->colorManagement ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{};
glUniform1i(shader->sourceTF, imageDescription.transferFunction);
glUniform1i(shader->targetTF, m_RenderData.pMonitor->imageDescription.transferFunction);
const auto sourcePrimaries =
imageDescription.primariesNameSet || imageDescription.primaries == SPCPRimaries{} ? getPrimaries(imageDescription.primariesNamed) : imageDescription.primaries;
const auto targetPrimaries = m_RenderData.pMonitor->imageDescription.primariesNameSet || m_RenderData.pMonitor->imageDescription.primaries == SPCPRimaries{} ?
getPrimaries(m_RenderData.pMonitor->imageDescription.primariesNamed) :
m_RenderData.pMonitor->imageDescription.primaries;
const GLfloat glSourcePrimaries[8] = {
sourcePrimaries.red.x, sourcePrimaries.red.y, sourcePrimaries.green.x, sourcePrimaries.green.y,
sourcePrimaries.blue.x, sourcePrimaries.blue.y, sourcePrimaries.white.x, sourcePrimaries.white.y,
};
const GLfloat glTargetPrimaries[8] = {
targetPrimaries.red.x, targetPrimaries.red.y, targetPrimaries.green.x, targetPrimaries.green.y,
targetPrimaries.blue.x, targetPrimaries.blue.y, targetPrimaries.white.x, targetPrimaries.white.y,
};
glUniformMatrix4x2fv(shader->sourcePrimaries, 1, false, glSourcePrimaries);
glUniformMatrix4x2fv(shader->targetPrimaries, 1, false, glTargetPrimaries);
const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference;
glUniform1f(shader->maxLuminance, maxLuminance * m_RenderData.pMonitor->imageDescription.luminances.reference / imageDescription.luminances.reference);
glUniform1f(shader->dstMaxLuminance, m_RenderData.pMonitor->imageDescription.luminances.max > 0 ? m_RenderData.pMonitor->imageDescription.luminances.max : 10000);
glUniform1f(shader->dstRefLuminance, m_RenderData.pMonitor->imageDescription.luminances.reference);
glUniform1f(shader->sdrSaturation,
m_RenderData.pMonitor->sdrSaturation > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_RenderData.pMonitor->sdrSaturation :
1.0f);
glUniform1f(shader->sdrBrightness,
m_RenderData.pMonitor->sdrBrightness > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_RenderData.pMonitor->sdrBrightness :
1.0f);
}
#endif
#ifndef GLES2
glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data());
#else
@@ -1397,49 +1445,6 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data());
#endif
glUniform1i(shader->tex, 0);
#ifndef GLES2
if (shader == &m_RenderData.pCurrentMonData->m_shCM && !usingFinalShader && (texType == TEXTURE_RGBA || texType == TEXTURE_RGBX)) {
const bool skipCM = *PPASS && m_RenderData.pMonitor->activeWorkspace && m_RenderData.pMonitor->activeWorkspace->m_bHasFullscreenWindow &&
m_RenderData.pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
glUniform1i(shader->texType, texType);
glUniform1i(shader->skipCM, skipCM);
if (!skipCM) {
const auto imageDescription =
m_RenderData.surface.valid() && m_RenderData.surface->colorManagement.valid() ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{};
glUniform1i(shader->sourceTF, imageDescription.transferFunction);
glUniform1i(shader->targetTF, m_RenderData.pMonitor->imageDescription.transferFunction);
const auto sourcePrimaries =
imageDescription.primariesNameSet || imageDescription.primaries == SPCPRimaries{} ? getPrimaries(imageDescription.primariesNamed) : imageDescription.primaries;
const auto targetPrimaries = m_RenderData.pMonitor->imageDescription.primariesNameSet || m_RenderData.pMonitor->imageDescription.primaries == SPCPRimaries{} ?
getPrimaries(m_RenderData.pMonitor->imageDescription.primariesNamed) :
m_RenderData.pMonitor->imageDescription.primaries;
const GLfloat glSourcePrimaries[8] = {
sourcePrimaries.red.x, sourcePrimaries.red.y, sourcePrimaries.green.x, sourcePrimaries.green.y,
sourcePrimaries.blue.x, sourcePrimaries.blue.y, sourcePrimaries.white.x, sourcePrimaries.white.y,
};
const GLfloat glTargetPrimaries[8] = {
targetPrimaries.red.x, targetPrimaries.red.y, targetPrimaries.green.x, targetPrimaries.green.y,
targetPrimaries.blue.x, targetPrimaries.blue.y, targetPrimaries.white.x, targetPrimaries.white.y,
};
glUniformMatrix4x2fv(shader->sourcePrimaries, 1, false, glSourcePrimaries);
glUniformMatrix4x2fv(shader->targetPrimaries, 1, false, glTargetPrimaries);
const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference;
glUniform1f(shader->maxLuminance, maxLuminance * m_RenderData.pMonitor->imageDescription.luminances.reference / imageDescription.luminances.reference);
glUniform1f(shader->dstMaxLuminance, m_RenderData.pMonitor->imageDescription.luminances.max > 0 ? m_RenderData.pMonitor->imageDescription.luminances.max : 10000);
glUniform1f(shader->dstRefLuminance, m_RenderData.pMonitor->imageDescription.luminances.reference);
glUniform1f(shader->sdrSaturation,
m_RenderData.pMonitor->sdrSaturation > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_RenderData.pMonitor->sdrSaturation :
1.0f);
glUniform1f(shader->sdrBrightness,
m_RenderData.pMonitor->sdrBrightness > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_RenderData.pMonitor->sdrBrightness :
1.0f);
}
}
#endif
if ((usingFinalShader && *PDT == 0) || CRASHING) {
glUniform1f(shader->time, m_tGlobalTimer.getSeconds() - shader->initialTime);
@@ -3016,7 +3021,7 @@ CEGLSync::~CEGLSync() {
if (sync == EGL_NO_SYNC_KHR)
return;
if (g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE)
if (g_pHyprOpenGL && g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE)
Debug::log(ERR, "eglDestroySyncKHR failed");
}

View File

@@ -276,6 +276,14 @@ class CHyprOpenGLImpl {
} m_sExts;
private:
enum eEGLContextVersion : uint8_t {
EGL_CONTEXT_GLES_2_0 = 0,
EGL_CONTEXT_GLES_3_0,
EGL_CONTEXT_GLES_3_2,
};
eEGLContextVersion m_eglContextVersion = EGL_CONTEXT_GLES_3_2;
std::list<GLuint> m_lBuffers;
std::list<GLuint> m_lTextures;
@@ -297,9 +305,9 @@ class CHyprOpenGLImpl {
SP<CTexture> m_pMissingAssetTexture, m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture; // TODO: don't always load lock
void logShaderError(const GLuint&, bool program = false);
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false);
GLuint compileShader(const GLuint&, std::string, bool dynamic = false);
void logShaderError(const GLuint&, bool program = false, bool silent = false);
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false, bool silent = false);
GLuint compileShader(const GLuint&, std::string, bool dynamic = false, bool silent = false);
void createBGTextureForMonitor(PHLMONITOR);
void initShaders();
void initDRMFormats();

View File

@@ -33,9 +33,7 @@
#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;
@@ -1216,7 +1214,8 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
pMonitor->tearingState.activelyTearing = shouldTear;
if ((*PDIRECTSCANOUT == 1 ||
(*PDIRECTSCANOUT == 2 && pMonitor->activeWorkspace->getFullscreenWindow() && pMonitor->activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) &&
(*PDIRECTSCANOUT == 2 && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow &&
pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN && pMonitor->activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) &&
!shouldTear) {
if (pMonitor->attemptDirectScanout()) {
return;
@@ -1412,11 +1411,16 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
static const hdr_output_metadata NO_HDR_METADATA = {.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}};
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
if (settings.transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ)
return NO_HDR_METADATA; // empty metadata for SDR
uint8_t eotf = 0;
switch (settings.transferFunction) {
case CM_TRANSFER_FUNCTION_SRGB: eotf = 0; break; // used to send primaries and luminances to AQ. ignored for now
case CM_TRANSFER_FUNCTION_ST2084_PQ: eotf = 2; break;
// case CM_TRANSFER_FUNCTION_HLG: eotf = 3; break; TODO check display capabilities first
default: return NO_HDR_METADATA; // empty metadata for SDR
}
const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); };
const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); };
const auto to16Bit = [](float value) { return uint16_t(std::round(value * 50000)); };
auto colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries;
auto luminances = settings.masteringLuminances.max > 0 ?
@@ -1430,7 +1434,7 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, A
.metadata_type = 0,
.hdmi_metadata_type1 =
hdr_metadata_infoframe{
.eotf = 2,
.eotf = eotf,
.metadata_type = 0,
.display_primaries =
{
@@ -1448,8 +1452,6 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, A
}
bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
pMonitor->commitSeq++;
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
const bool PHDR = pMonitor->imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ;
@@ -1457,24 +1459,42 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ);
if (pMonitor->output->parsedEDID.supportsBT2020 && SUPPORTSPQ) {
// HDR metadata determined by
// PPASS = 0 monitor settings
// PPASS = 1
// windowed: monitor settings
// fullscreen surface: surface settings FIXME: fullscreen SDR surface passthrough - pass degamma, ctm, gamma if needed
// PPASS = 2
// windowed: monitor settings
// fullscreen SDR surface: monitor settings
// fullscreen HDR surface: surface settings
bool wantHDR = PHDR;
bool hdrIsHandled = false;
if (*PPASS && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
const auto ROOT_SURF = WINDOW->m_pWLSurface->resource();
const auto SURF =
ROOT_SURF->findFirstPreorder([ROOT_SURF](SP<CWLSurfaceResource> surf) { return surf->colorManagement.valid() && surf->extends() == ROOT_SURF->extends(); });
if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription()) {
wantHDR = PHDR && *PPASS == 2;
// we have a surface with image description and it's allowed by wantHDR
if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription() &&
(!wantHDR || SURF->colorManagement->imageDescription().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) {
bool needsHdrMetadataUpdate = SURF->colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW;
if (SURF->colorManagement->needsHdrMetadataUpdate())
SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID));
if (needsHdrMetadataUpdate)
pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata());
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
hdrIsHandled = true;
}
pMonitor->m_previousFSWindow = WINDOW;
} else {
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
}
if (!hdrIsHandled) {
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != wantHDR)
pMonitor->output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
pMonitor->m_previousFSWindow.reset();
}
}
@@ -1495,13 +1515,11 @@ 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;
@@ -1530,21 +1548,19 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
return ok;
Debug::log(TRACE, "Explicit: {} presented", explicitPresented.size());
auto sync = g_pHyprOpenGL->createEGLSync(pMonitor->inFence.get());
if (!sync)
Debug::log(TRACE, "Explicit: can't add sync, EGLSync failed");
if (!pMonitor->eglSync)
Debug::log(TRACE, "Explicit: can't add sync, monitor has no EGLSync");
else {
for (auto const& e : explicitPresented) {
if (!e->current.buffer || !e->current.buffer->buffer || !e->current.buffer->buffer->syncReleaser)
if (!e->current.buffer || !e->current.buffer->buffer->syncReleaser)
continue;
e->current.buffer->buffer->syncReleaser->addReleaseSync(sync);
e->current.buffer->buffer->syncReleaser->addReleaseSync(pMonitor->eglSync);
}
}
explicitPresented.clear();
pMonitor->output->state->resetExplicitFences();
return ok;
}
@@ -2255,37 +2271,39 @@ void CHyprRenderer::endRender() {
if (m_eRenderMode == RENDER_MODE_FULL_FAKE)
return;
if (m_eRenderMode == RENDER_MODE_NORMAL) {
if (m_eRenderMode == RENDER_MODE_NORMAL)
PMONITOR->output->state->setBuffer(m_pCurrentBuffer);
auto explicitOptions = getExplicitSyncSettings(PMONITOR->output);
auto explicitOptions = getExplicitSyncSettings(PMONITOR->output);
if (PMONITOR->inTimeline && explicitOptions.explicitEnabled && explicitOptions.explicitKMSEnabled) {
auto sync = g_pHyprOpenGL->createEGLSync();
if (!sync) {
Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender");
return;
}
if (PMONITOR->inTimeline && explicitOptions.explicitEnabled) {
PMONITOR->eglSync = g_pHyprOpenGL->createEGLSync();
if (!PMONITOR->eglSync) {
Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender");
return;
}
bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->commitSeq, sync->fd());
if (!ok) {
Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender");
return;
}
PMONITOR->inTimelinePoint++;
bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->inTimelinePoint, PMONITOR->eglSync->fd());
if (!ok) {
Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender");
return;
}
PMONITOR->inFence = CFileDescriptor{PMONITOR->inTimeline->exportAsSyncFileFD(PMONITOR->commitSeq)};
if (m_eRenderMode == RENDER_MODE_NORMAL && explicitOptions.explicitKMSEnabled) {
PMONITOR->inFence = CFileDescriptor{PMONITOR->inTimeline->exportAsSyncFileFD(PMONITOR->inTimelinePoint)};
if (!PMONITOR->inFence.isValid()) {
Debug::log(ERR, "renderer: couldn't export from sync timeline in endRender");
return;
}
PMONITOR->output->state->setExplicitInFence(PMONITOR->inFence.get());
} else {
if (isNvidia() && *PNVIDIAANTIFLICKER)
glFinish();
else
glFlush();
}
} else {
if (isNvidia() && *PNVIDIAANTIFLICKER)
glFinish();
else
glFlush();
}
}

View File

@@ -12,7 +12,6 @@ class CShader {
GLint color = -1;
GLint alphaMatte = -1;
GLint texType = -1;
GLint skipCM = -1;
GLint sourceTF = -1;
GLint targetTF = -1;
GLint sourcePrimaries = -1;

View File

@@ -64,8 +64,9 @@ void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t strid
const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat);
ASSERT(format);
m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX;
m_vSize = size_;
m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX;
m_vSize = size_;
m_isSynchronous = true;
allocate();
GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID));

View File

@@ -35,14 +35,15 @@ class CTexture {
void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage);
const std::vector<uint8_t>& dataCopy();
eTextureType m_iType = TEXTURE_RGBA;
GLenum m_iTarget = GL_TEXTURE_2D;
GLuint m_iTexID = 0;
Vector2D m_vSize = {};
void* m_pEglImage = nullptr;
eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL;
bool m_bOpaque = false;
uint32_t m_iDrmFormat = 0; // for shm
eTextureType m_iType = TEXTURE_RGBA;
GLenum m_iTarget = GL_TEXTURE_2D;
GLuint m_iTexID = 0;
Vector2D m_vSize = {};
void* m_pEglImage = nullptr;
eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL;
bool m_bOpaque = false;
uint32_t m_iDrmFormat = 0; // for shm
bool m_isSynchronous = false;
private:
void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size);

View File

@@ -46,7 +46,7 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0);
info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + *POUTERGAP}, {0, 0}};
} else
info.desiredExtents = {{0, *POUTERGAP * 2 + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2}, {0, 0}};
info.desiredExtents = {{0, *POUTERGAP * 2 + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}, {0, 0}};
} else
info.desiredExtents = {{0, 0}, {0, 0}};
return info;
@@ -54,7 +54,6 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
void CHyprGroupBarDecoration::onPositioningReply(const SDecorationPositioningReply& reply) {
m_bAssignedBox = reply.assignedGeometry;
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pWindow.lock());
}
eDecorationType CHyprGroupBarDecoration::getDecorationType() {
@@ -143,7 +142,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - *PINDICATORHEIGHT - *POUTERGAP - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
*PINDICATORHEIGHT};
rect.scale(pMonitor->scale);
rect.scale(pMonitor->scale).round();
const bool GROUPLOCKED = m_pWindow->getGroupHead()->m_sGroupData.locked || g_pKeybindManager->m_bGroupsLocked;
const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE;
@@ -279,7 +278,7 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float
const CHyprColor COLOR = CHyprColor(*PTEXTCOLOR);
const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT;
tex = g_pHyprOpenGL->renderText(pWindow->m_szTitle, COLOR, *PTITLEFONTSIZE, false, FONTFAMILY, bufferSize.x - 2 /* some padding yk */);
tex = g_pHyprOpenGL->renderText(pWindow->m_szTitle, COLOR, *PTITLEFONTSIZE * monitorScale, false, FONTFAMILY, bufferSize.x - 2 /* some padding yk */);
if (tex)
texSize = tex->m_vSize;
@@ -574,5 +573,5 @@ CBox CHyprGroupBarDecoration::assignedBoxGlobal() {
if (PWORKSPACE && !m_pWindow->m_bPinned)
box.translate(PWORKSPACE->m_vRenderOffset->value());
return box;
return box.round();
}

View File

@@ -33,5 +33,13 @@ std::optional<CBox> CRectPassElement::boundingBox() {
}
CRegion CRectPassElement::opaqueRegion() {
return data.color.a >= 1.F ? boundingBox()->expand(-data.round) : CRegion{};
if (data.color.a < 1.F)
return CRegion{};
CRegion rg = boundingBox()->expand(-data.round);
if (!data.clipBox.empty())
rg.intersect(data.clipBox);
return rg;
}

View File

@@ -22,8 +22,7 @@ void CTexPassElement::draw(const CRegion& damage) {
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = *data.replaceProjection;
g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower, data.syncTimeline,
data.syncPoint);
g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower);
if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix;
}

View File

@@ -16,8 +16,6 @@ class CTexPassElement : public IPassElement {
int round = 0;
float roundingPower = 2.0f;
bool flipEndFrame = false;
SP<CSyncTimeline> syncTimeline;
int64_t syncPoint = 0;
std::optional<Mat3x3> replaceProjection;
CBox clipBox;
};

View File

@@ -8,7 +8,6 @@ uniform sampler2D tex;
//uniform samplerExternalOES texture0;
uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext
uniform int skipCM;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries;
@@ -408,26 +407,28 @@ void main() {
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
discard;
if (skipCM == 0) {
pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF);
mat3 srcxyz = primaries2xyz(sourcePrimaries);
mat3 dstxyz;
if (sourcePrimaries == targetPrimaries)
dstxyz = srcxyz;
else {
dstxyz = primaries2xyz(targetPrimaries);
pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]);
}
pixColor = toNit(pixColor, sourceTF);
pixColor.rgb *= pixColor.a;
pixColor = tonemap(pixColor, dstxyz);
if (sourceTF == CM_TRANSFER_FUNCTION_SRGB && targetTF == CM_TRANSFER_FUNCTION_ST2084_PQ)
pixColor = saturate(pixColor, srcxyz, sdrSaturation);
pixColor *= sdrBrightnessMultiplier;
pixColor = fromLinearNit(pixColor, targetTF);
pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF);
mat3 srcxyz = primaries2xyz(sourcePrimaries);
mat3 dstxyz;
if (sourcePrimaries == targetPrimaries)
dstxyz = srcxyz;
else {
dstxyz = primaries2xyz(targetPrimaries);
pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]);
}
pixColor = toNit(pixColor, sourceTF);
pixColor.rgb *= pixColor.a;
pixColor = tonemap(pixColor, dstxyz);
if (sourceTF == CM_TRANSFER_FUNCTION_SRGB && targetTF == CM_TRANSFER_FUNCTION_ST2084_PQ)
pixColor = saturate(pixColor, srcxyz, sdrSaturation);
pixColor *= sdrBrightnessMultiplier;
pixColor = fromLinearNit(pixColor, targetTF);
if (applyTint == 1)
pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]);

View File

@@ -129,7 +129,7 @@ void CX11DataDevice::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, con
xcb_window_t targetWindow = getProxyWindow(XSURF->xID);
xcb_client_message_data_t data = {0};
xcb_client_message_data_t data = {{0}};
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
data.data32[1] = XDND_VERSION << 24;
data.data32[1] |= 1;
@@ -164,7 +164,7 @@ void CX11DataDevice::sendLeave() {
xcb_window_t targetWindow = getProxyWindow(lastSurface->xID);
xcb_client_message_data_t data = {0};
xcb_client_message_data_t data = {{0}};
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
sendDndEvent(targetWindow, HYPRATOMS["XdndLeave"], data);
@@ -183,7 +183,7 @@ void CX11DataDevice::sendMotion(uint32_t timeMs, const Vector2D& local) {
const auto XCOORDS = g_pXWaylandManager->waylandToXWaylandCoords(lastSurfaceCoords + local);
const uint32_t coords = ((uint32_t)XCOORDS.x << 16) | (uint32_t)XCOORDS.y;
xcb_client_message_data_t data = {0};
xcb_client_message_data_t data = {{0}};
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
data.data32[2] = coords;
data.data32[3] = timeMs;
@@ -204,7 +204,7 @@ void CX11DataDevice::sendDrop() {
xcb_window_t targetWindow = getProxyWindow(lastSurface->xID);
xcb_client_message_data_t data = {0};
xcb_client_message_data_t data = {{0}};
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
data.data32[2] = lastTime;

View File

@@ -20,6 +20,7 @@
#include "Server.hpp"
#include "XWayland.hpp"
#include "config/ConfigValue.hpp"
#include "debug/Log.hpp"
#include "../defines.hpp"
#include "../Compositor.hpp"
@@ -32,31 +33,45 @@ constexpr int SOCKET_BACKLOG = 1;
constexpr int MAX_SOCKET_RETRIES = 32;
constexpr int LOCK_FILE_MODE = 0444;
static CFileDescriptor createSocket(struct sockaddr_un* addr, size_t path_size) {
socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1;
CFileDescriptor fd{socket(AF_UNIX, SOCK_STREAM, 0)};
static CFileDescriptor createSocket(struct sockaddr_un* addr, size_t pathSize) {
const bool isRegularSocket(addr->sun_path[0]);
const char dbgSocketPathPrefix = isRegularSocket ? addr->sun_path[0] : '@';
const char* const dbgSocketPathRem = addr->sun_path + 1;
socklen_t size = offsetof(struct sockaddr_un, sun_path) + pathSize + 1;
CFileDescriptor fd{socket(AF_UNIX, SOCK_STREAM, 0)};
if (!fd.isValid()) {
Debug::log(ERR, "Failed to create socket {}{}", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1);
Debug::log(ERR, "Failed to create socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
return {};
}
if (!fd.setFlags(fd.getFlags() | FD_CLOEXEC)) {
Debug::log(ERR, "Failed to set flags for socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
return {};
}
if (addr->sun_path[0])
if (isRegularSocket)
unlink(addr->sun_path);
if (bind(fd.get(), (struct sockaddr*)addr, size) < 0) {
Debug::log(ERR, "Failed to bind socket {}{}", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1);
if (addr->sun_path[0])
Debug::log(ERR, "Failed to bind socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
if (isRegularSocket)
unlink(addr->sun_path);
return {};
}
// Required for the correct functioning of `xhost` #9574
// The operation is safe because XWayland controls socket access by itself
// and rejects connections not matched by the `xhost` ACL
if (isRegularSocket && chmod(addr->sun_path, 0666) < 0) {
// We are only extending the default permissions,
// and I don't see the reason to make a full stop in case of a failed operation.
Debug::log(ERR, "Failed to set permission mode for socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
}
if (listen(fd.get(), SOCKET_BACKLOG) < 0) {
Debug::log(ERR, "Failed to listen to socket {}{}", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1);
if (addr->sun_path[0])
Debug::log(ERR, "Failed to listen to socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
if (isRegularSocket)
unlink(addr->sun_path);
return {};
}
@@ -113,14 +128,32 @@ static std::string getSocketPath(int display, bool isLinux) {
}
static bool openSockets(std::array<CFileDescriptor, 2>& sockets, int display) {
static auto CREATEABSTRACTSOCKET = CConfigValue<Hyprlang::INT>("xwayland:create_abstract_socket");
if (!ensureSocketDirExists())
return false;
sockaddr_un addr = {.sun_family = AF_UNIX};
std::string path;
#ifdef __linux__
if (*CREATEABSTRACTSOCKET) {
// cursed...
// but is kept as an option for better compatibility
addr.sun_path[0] = 0;
path = getSocketPath(display, true);
strncpy(addr.sun_path + 1, path.c_str(), path.length() + 1);
} else {
path = getSocketPath(display, false);
strncpy(addr.sun_path, path.c_str(), path.length() + 1);
}
#else
if (*CREATEABSTRACTSOCKET) {
Debug::log(WARN, "The abstract XWayland Unix domain socket might be used only on Linux systems. A regular one'll be created insted.");
}
path = getSocketPath(display, false);
strncpy(addr.sun_path, path.c_str(), path.length() + 1);
#endif
sockets[0] = CFileDescriptor{createSocket(&addr, path.length())};
if (!sockets[0].isValid())
@@ -144,8 +177,7 @@ static void startServer(void* data) {
}
static int xwaylandReady(int fd, uint32_t mask, void* data) {
CFileDescriptor xwlFd{fd};
return g_pXWayland->pServer->ready(std::move(xwlFd), mask);
return g_pXWayland->pServer->ready(fd, mask);
}
static bool safeRemove(const std::string& path) {
@@ -220,12 +252,10 @@ CXWaylandServer::~CXWaylandServer() {
safeRemove(lockPath);
std::string path;
#ifdef __linux__
path = getSocketPath(display, true);
#else
path = getSocketPath(display, false);
#endif
safeRemove(path);
for (bool isLinux : {true, false}) {
path = getSocketPath(display, isLinux);
safeRemove(path);
}
}
void CXWaylandServer::die() {
@@ -322,7 +352,7 @@ bool CXWaylandServer::start() {
return false;
}
waylandFDs[0].take(); // does this leak?
waylandFDs[0].take(); // wl_client owns this fd now
int notify[2] = {-1, -1};
if (pipe(notify) < 0) {
@@ -342,30 +372,24 @@ bool CXWaylandServer::start() {
pipeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, notifyFds[0].get(), WL_EVENT_READABLE, ::xwaylandReady, nullptr);
pipeFd = std::move(notifyFds[0]);
serverPID = fork();
auto serverPID = fork();
if (serverPID < 0) {
Debug::log(ERR, "fork failed");
die();
return false;
} else if (serverPID == 0) {
pid_t pid = fork();
if (pid < 0) {
Debug::log(ERR, "second fork failed");
_exit(1);
} else if (pid == 0)
runXWayland(notifyFds[1]);
runXWayland(notifyFds[1]);
_exit(0);
}
return true;
}
int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) {
int CXWaylandServer::ready(int fd, uint32_t mask) {
if (mask & WL_EVENT_READABLE) {
// xwayland writes twice
char buf[64];
ssize_t n = read(fd.get(), buf, sizeof(buf));
ssize_t n = read(fd, buf, sizeof(buf));
if (n < 0 && errno != EINTR) {
Debug::log(ERR, "Xwayland: read from displayFd failed");
mask = 0;
@@ -373,14 +397,6 @@ int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) {
return 1;
}
while (waitpid(serverPID, nullptr, 0) < 0) {
if (errno == EINTR)
continue;
Debug::log(ERR, "Xwayland: waitpid for fork failed");
g_pXWayland->pServer.reset();
return 1;
}
// if we don't have readable here, it failed
if (!(mask & WL_EVENT_READABLE)) {
Debug::log(ERR, "Xwayland: startup failed, not setting up xwm");
@@ -391,6 +407,7 @@ int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) {
Debug::log(LOG, "XWayland is ready");
wl_event_source_remove(pipeSource);
pipeFd.reset();
pipeSource = nullptr;
// start the wm

View File

@@ -20,7 +20,7 @@ class CXWaylandServer {
bool start();
// called on ready
int ready(Hyprutils::OS::CFileDescriptor fd, uint32_t mask);
int ready(int fd, uint32_t mask);
void die();
@@ -34,8 +34,6 @@ class CXWaylandServer {
bool tryOpenSockets();
void runXWayland(Hyprutils::OS::CFileDescriptor& notifyFD);
pid_t serverPID = 0;
std::string displayName;
int display = -1;
std::array<Hyprutils::OS::CFileDescriptor, 2> xFDs;

View File

@@ -62,12 +62,12 @@ void CXWaylandSurface::ensureListeners() {
});
listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) {
if (surface->pending.texture && !mapped) {
if (surface->current.texture && !mapped) {
map();
return;
}
if (!surface->pending.texture && mapped) {
if (!surface->current.texture && mapped) {
unmap();
return;
}
@@ -131,7 +131,7 @@ void CXWaylandSurface::considerMap() {
return;
}
if (surface->pending.texture) {
if (surface->current.texture) {
Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer");
map();
return;

View File

@@ -494,7 +494,7 @@ void CXWM::focusWindow(SP<CXWaylandSurface> surf) {
if (surf->overrideRedirect)
return;
xcb_client_message_data_t msg = {0};
xcb_client_message_data_t msg = {{0}};
msg.data32[0] = HYPRATOMS["WM_TAKE_FOCUS"];
msg.data32[1] = XCB_TIME_CURRENT_TIME;