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 - name: Build Hyprland
run: | run: |
make all CFLAGS=-Werror CXXFLAGS=-Werror make all
- name: Compress and package artifacts - name: Compress and package artifacts
run: | run: |

View File

@@ -102,7 +102,7 @@ else()
endif() endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) 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(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7) pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.1) pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.1)
@@ -444,4 +444,5 @@ install(
DIRECTORY ${HEADERS_SRC} DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING 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. # uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0 # workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0 # workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1] # windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1] # windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1] # windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1] # windowrule = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle { 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/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules # See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1 # Example windowrule
# windowrule = float, ^(kitty)$ # windowrule = float,class:^(kitty)$,title:^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this. # Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.* windowrule = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland # 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": { "locked": {
"lastModified": 1741934125, "lastModified": 1742213273,
"narHash": "sha256-qwI47l3aKXRpDvmCKDbLV70iVfAqhpuKqT7qYHA4KJk=", "narHash": "sha256-0l0vDb4anfsBu1rOs94bC73Hub+xEivgBAo6QXl2MmU=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "aquamarine", "repo": "aquamarine",
"rev": "bea48d0bbe15fb3d758a8b6be865836c97056575", "rev": "484b732195cc53f4536ce4bd59a5c6402b1e7ccf",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -79,11 +79,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1738664950, "lastModified": 1742215578,
"narHash": "sha256-xIeGNM+iivwVHkv9tHwOqoUP5dDrtees34bbFKKMZYs=", "narHash": "sha256-zfs71PXVVPEe56WEyNi2TJQPs0wabU4WAlq0XV7GcdE=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprcursor", "repo": "hyprcursor",
"rev": "7c6d165e1eb9045a996551eb9f121b6d1b30adc3", "rev": "2fd36421c21aa87e2fe3bee11067540ae612f719",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -276,11 +276,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1741851582, "lastModified": 1742069588,
"narHash": "sha256-cPfs8qMccim2RBgtKGF+x9IBCduRvd/N5F4nYpU0TVE=", "narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "6607cf789e541e7873d40d3a8f7815ea92204f32", "rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -299,11 +299,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1741379162, "lastModified": 1742058297,
"narHash": "sha256-srpAbmJapkaqGRE3ytf3bj4XshspVR5964OX5LfjDWc=", "narHash": "sha256-b4SZc6TkKw8WQQssbN5O2DaCEzmFfvSTPYHlx/SFW9Y=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "b5a62751225b2f62ff3147d0a334055ebadcd5cc", "rev": "59f17850021620cd348ad2e9c0c64f4e6325ce2a",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -165,15 +165,18 @@ int main(int argc, char** argv, char** envp) {
} else if (command[0] == "reload") { } else if (command[0] == "reload") {
auto ret = g_pPluginManager->ensurePluginsLoadState(force); auto ret = g_pPluginManager->ensurePluginsLoadState(force);
if (ret != LOADSTATE_OK && notify) { if (ret != LOADSTATE_OK) {
switch (ret) { if (notify) {
case LOADSTATE_FAIL: switch (ret) {
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break; case LOADSTATE_FAIL:
case LOADSTATE_HEADERS_OUTDATED: case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually."); case LOADSTATE_HEADERS_OUTDATED:
break; g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
default: break; break;
default: break;
}
} }
return 1; return 1;
} else if (notify && !notifyFail) { } else if (notify && !notifyFail) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins"); 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') add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif endif
aquamarine = dependency('aquamarine', version: '>=0.4.5') aquamarine = dependency('aquamarine', version: '>=0.8.0')
hyprcursor = dependency('hyprcursor', version: '>=0.1.7') hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1') hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
hyprlang = dependency('hyprlang', version: '>= 0.3.2') hyprlang = dependency('hyprlang', version: '>= 0.3.2')
@@ -89,7 +89,7 @@ endif
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true) run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
# Install headers # 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') headers = globber.stdout().strip().split('\n')
foreach file : headers foreach file : headers
install_headers(file, subdir: 'hyprland', preserve_path: true) 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 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); PWORKSPACE->m_fAlpha->setValueAndWarp(0);
@@ -3061,6 +3067,8 @@ bool CCompositor::shouldChangePreferredImageDescription() {
} }
void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace) { void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace) {
if (!m_pLastMonitor)
return;
for (const auto& rule : rules) { for (const auto& rule : rules) {
if (!rule.isPersistent) if (!rule.isPersistent)
@@ -3076,6 +3084,9 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
const auto PMONITOR = getMonitorFromString(rule.monitor); 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) { if (!PWORKSPACE) {
WORKSPACEID id = rule.workspaceId; WORKSPACEID id = rule.workspaceId;
std::string wsname = rule.workspaceName; std::string wsname = rule.workspaceName;
@@ -3092,7 +3103,7 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspace
} }
PWORKSPACE = getWorkspaceByID(id); PWORKSPACE = getWorkspaceByID(id);
if (!PWORKSPACE) if (!PWORKSPACE)
createNewWorkspace(id, PMONITOR ? PMONITOR : m_pLastMonitor.lock(), wsname, false); createNewWorkspace(id, PMONITOR ? PMONITOR->ID : m_pLastMonitor->ID, wsname, false);
} }
if (PWORKSPACE) if (PWORKSPACE)

View File

@@ -1054,9 +1054,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
}, },
SConfigOptionDescription{ SConfigOptionDescription{
.value = "misc:vrr", .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, .type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{0, 0, 2}, .data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 3},
}, },
SConfigOptionDescription{ SConfigOptionDescription{
.value = "misc:mouse_move_enables_dpms", .value = "misc:mouse_move_enables_dpms",
@@ -1319,6 +1319,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL, .type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false}, .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: * opengl:
@@ -1375,6 +1381,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
SConfigOptionDescription{ SConfigOptionDescription{
.value = "render:cm_fs_passthrough", .value = "render:cm_fs_passthrough",
.description = "Passthrough color settings for fullscreen apps when possible", .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, .type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true}, .data = SConfigOptionDescription::SBoolData{true},
}, },

View File

@@ -49,6 +49,7 @@
#include <memory> #include <memory>
using namespace Hyprutils::String; using namespace Hyprutils::String;
using namespace Hyprutils::Animation; using namespace Hyprutils::Animation;
using enum NContentType::eContentType;
//NOLINTNEXTLINE //NOLINTNEXTLINE
extern "C" char** environ; 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 VALUE = v;
const std::string COMMAND = c; const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleWindowRuleV2(COMMAND, VALUE); const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE);
Hyprlang::CParseResult result; Hyprlang::CParseResult result;
if (RESULT.has_value()) if (RESULT.has_value())
@@ -652,6 +653,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("xwayland:enabled", Hyprlang::INT{1}); registerConfigVar("xwayland:enabled", Hyprlang::INT{1});
registerConfigVar("xwayland:use_nearest_neighbor", Hyprlang::INT{1}); registerConfigVar("xwayland:use_nearest_neighbor", Hyprlang::INT{1});
registerConfigVar("xwayland:force_zero_scaling", Hyprlang::INT{0}); registerConfigVar("xwayland:force_zero_scaling", Hyprlang::INT{0});
registerConfigVar("xwayland:create_abstract_socket", Hyprlang::INT{0});
registerConfigVar("opengl:nvidia_anti_flicker", Hyprlang::INT{1}); registerConfigVar("opengl:nvidia_anti_flicker", Hyprlang::INT{1});
@@ -691,7 +693,8 @@ CConfigManager::CConfigManager() {
registerConfigVar("render:expand_undersized_textures", Hyprlang::INT{1}); registerConfigVar("render:expand_undersized_textures", Hyprlang::INT{1});
registerConfigVar("render:xp_mode", Hyprlang::INT{0}); registerConfigVar("render:xp_mode", Hyprlang::INT{0});
registerConfigVar("render:ctm_animation", Hyprlang::INT{2}); 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_update_news", Hyprlang::INT{0});
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});
@@ -1650,37 +1653,34 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) {
} }
m->vrrActive = true; m->vrrActive = true;
return; return;
} else if (USEVRR == 2) { } else if (USEVRR == 2 || USEVRR == 3) {
const auto PWORKSPACE = m->activeWorkspace; const auto PWORKSPACE = m->activeWorkspace;
if (!PWORKSPACE) if (!PWORKSPACE)
return; // ??? 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 */ /* fullscreen */
m->vrrActive = true; m->vrrActive = true;
m->output->state->resetExplicitFences(); if (!m->output->state->state().adaptiveSync) {
m->output->state->setAdaptiveSync(true); m->output->state->setAdaptiveSync(true);
if (!m->state.test()) { if (!m->state.test()) {
Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name);
m->output->state->setAdaptiveSync(false); m->output->state->setAdaptiveSync(false);
}
} }
} else {
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
} else if (!WORKSPACEFULL) {
m->vrrActive = false; m->vrrActive = false;
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(false); 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) { 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 RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = value.substr(value.find_first_of(',') + 1); const auto VALUE = value.substr(value.find_first_of(',') + 1);
@@ -2608,6 +2545,38 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
return {}; 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) { void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) {
const bool BYADDRESS = name.starts_with("address:"); const bool BYADDRESS = name.starts_with("address:");
std::string matchName = name; 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> handleUnbind(const std::string&, const std::string&);
std::optional<std::string> handleWindowRule(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> 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> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(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&); 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. # uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0 # workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0 # workspace = f[1], gapsout:0, gapsin:0
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1] # windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1] # windowrule = rounding 0, floating:0, onworkspace:w[tv1]
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1] # windowrule = bordersize 0, floating:0, onworkspace:f[1]
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1] # windowrule = rounding 0, floating:0, onworkspace:f[1]
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle { dwindle {
@@ -269,7 +269,7 @@ bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow bindm = $mainMod, mouse:273, resizewindow
# Laptop multimedia keys for volume and LCD brightness # 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 = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 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/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules # See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1 # Example windowrule
# windowrule = float, ^(kitty)$ # windowrule = float,class:^(kitty)$,title:^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this. # Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.* windowrule = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland # 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) if (!m_pResource->current.texture)
return {}; 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); damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y);
const auto BUFSIZE = m_pResource->current.bufferSize; const auto BUFSIZE = m_pResource->current.bufferSize;

View File

@@ -1258,7 +1258,7 @@ void CWindow::setAnimationsToMove() {
void CWindow::onWorkspaceAnimUpdate() { void CWindow::onWorkspaceAnimUpdate() {
// clip box for animated offsets // clip box for animated offsets
if (!m_bIsFloating || m_bPinned || isFullscreen()) { if (!m_bIsFloating || m_bPinned || isFullscreen() || m_bDraggingTiled) {
m_vFloatingOffset = Vector2D(0, 0); m_vFloatingOffset = Vector2D(0, 0);
return; 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 // 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) { 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 (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) { if (PMONITOR->tearingState.busy) {

View File

@@ -57,8 +57,7 @@ void CMonitor::onConnect(bool noRule) {
g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); }); g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); });
if (output->supportsExplicit) { if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD()); inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
} }
listeners.frame = output->events.frame.registerListener([this](std::any d) { onMonitorFrame(); }); 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{"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()); g_pHyprRenderer->damageMonitor(self.lock());
@@ -1342,7 +1341,7 @@ bool CMonitor::attemptDirectScanout() {
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE); 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; return false;
if (PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform) 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()); 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(); auto PBUFFER = PSURFACE->current.buffer->buffer;
if (PBUFFER == output->state->state().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; return true;
}
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats // FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!! // and comes from the appropriate device. This may implode on multi-gpu!!
@@ -1384,7 +1400,7 @@ bool CMonitor::attemptDirectScanout() {
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
PSURFACE->presentFeedback(&now, self.lock()); PSURFACE->presentFeedback(&now, self.lock());
output->state->addDamage(CBox{{}, vecPixelSize}); output->state->addDamage(PSURFACE->current.accumulateBufferDamage());
output->state->resetExplicitFences(); output->state->resetExplicitFences();
auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); }); auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); });
@@ -1404,8 +1420,6 @@ bool CMonitor::attemptDirectScanout() {
} }
} }
commitSeq++;
bool ok = output->commit(); bool ok = output->commit();
if (!ok && DOEXPLICIT) { 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); Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
} }
scanoutNeedsCursorUpdate = false;
if (!PBUFFER->lockedByBackend || PBUFFER->hlEvents.backendRelease) if (!PBUFFER->lockedByBackend || PBUFFER->hlEvents.backendRelease)
return true; return true;

View File

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

View File

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

View File

@@ -19,12 +19,13 @@ SP<CSyncTimeline> CSyncTimeline::create(int drmFD_) {
return timeline; return timeline;
} }
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, int drmSyncobjFD) { SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobjFD) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline); auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
timeline->drmFD = drmFD_; timeline->drmFD = drmFD_;
timeline->self = timeline; 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??"); Debug::log(ERR, "CSyncTimeline: failed to create a drm syncobj from fd??");
return nullptr; return nullptr;
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -98,6 +98,7 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) {
CProtocolManager::CProtocolManager() { CProtocolManager::CProtocolManager() {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync"); 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 PENABLEXXCM = CConfigValue<Hyprlang::INT>("experimental:xx_color_management_v4");
static const auto PDEBUGCM = CConfigValue<Hyprlang::INT>("debug:full_cm_proto"); 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::ctm = makeUnique<CHyprlandCTMControlProtocol>(&hyprland_ctm_control_manager_v1_interface, 2, "CTMControl");
PROTO::hyprlandSurface = makeUnique<CHyprlandSurfaceProtocol>(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface"); PROTO::hyprlandSurface = makeUnique<CHyprlandSurfaceProtocol>(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface");
PROTO::contentType = makeUnique<CContentTypeProtocol>(&wp_content_type_manager_v1_interface, 1, "ContentType"); 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::xxColorManagement = makeUnique<CXXColorManagementProtocol>(&xx_color_manager_v4_interface, 1, "XXColorManagement");
PROTO::frogColorManagement = makeUnique<CFrogColorManagementProtocol>(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement"); 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()) { for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM) if (b->type() != Aquamarine::AQ_BACKEND_DRM)
continue; continue;

View File

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

View File

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

View File

@@ -59,6 +59,13 @@ struct SVersionInfo {
#define OPTIONAL #define OPTIONAL
#define HANDLE void* #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 IHyprLayout;
class CWindow; class CWindow;
class IHyprWindowDecoration; class IHyprWindowDecoration;
@@ -70,7 +77,7 @@ class CWindow;
Methods marked with REQUIRED are required. Methods marked with REQUIRED are required.
*/ */
/* /*
called pre-plugin init. called pre-plugin init.
In case of a version mismatch, will eject the .so. In case of a version mismatch, will eject the .so.
@@ -315,3 +322,7 @@ APICALL inline EXPORT const char* __hyprland_api_get_client_hash() {
return GIT_COMMIT_HASH; return GIT_COMMIT_HASH;
} }
// NOLINTEND // NOLINTEND
#ifdef __clang__
#pragma clang diagnostic pop
#endif

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
#include "FrogColorManagement.hpp" #include "FrogColorManagement.hpp"
#include "color-management-v1.hpp" #include "color-management-v1.hpp"
#include "macros.hpp"
#include "protocols/ColorManagement.hpp" #include "protocols/ColorManagement.hpp"
#include "protocols/core/Subcompositor.hpp" #include "protocols/core/Subcompositor.hpp"
#include "protocols/types/ColorManagement.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_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_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; 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 = surface->colorManagement->m_imageDescription.transferFunction =
convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ)); convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ));
break; break;
}; // intended fall through };
[[fallthrough]];
case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED: 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: case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB:
surface->colorManagement->m_imageDescription.transferFunction = convertTransferFunction(getWPTransferFunction(tf)); 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) { if UNLIKELY (!buf->good() || !buf->buffer->success) {
resource->sendFailed(); resource->sendFailed();
PROTO::linuxDma->m_vBuffers.pop_back();
return; return;
} }

View File

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

View File

@@ -12,6 +12,7 @@
#include "../helpers/Format.hpp" #include "../helpers/Format.hpp"
#include <algorithm> #include <algorithm>
#include <functional>
CScreencopyFrame::~CScreencopyFrame() { CScreencopyFrame::~CScreencopyFrame() {
if (buffer && buffer->locked()) if (buffer && buffer->locked())
@@ -174,39 +175,42 @@ void CScreencopyFrame::share() {
timespec now; timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
if (bufferDMA) { auto callback = [this, now, weak = self](bool success) {
if (!copyDmabuf()) { if (weak.expired())
LOGM(ERR, "Dmabuf copy failed in {:x}", (uintptr_t)this); return;
if (!success) {
LOGM(ERR, "{} copy failed in {:x}", bufferDMA ? "Dmabuf" : "Shm", (uintptr_t)this);
resource->sendFailed(); resource->sendFailed();
return; return;
} }
} else {
if (!copyShm()) { resource->sendFlags((zwlrScreencopyFrameV1Flags)0);
LOGM(ERR, "Shm copy failed in {:x}", (uintptr_t)this); if (withDamage) {
resource->sendFailed(); // TODO: add a damage ring for this.
return; resource->sendDamage(0, 0, buffer->size.x, buffer->size.y);
} }
}
resource->sendFlags((zwlrScreencopyFrameV1Flags)0); uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
if (withDamage) { uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
// TODO: add a damage ring for this. resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec);
resource->sendDamage(0, 0, buffer->size.x, buffer->size.y); };
}
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0; if (bufferDMA)
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF; copyDmabuf(callback);
resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec); else
callback(copyShm());
} }
bool CScreencopyFrame::copyDmabuf() { void CScreencopyFrame::copyDmabuf(std::function<void(bool)> callback) {
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer); auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock(), nullptr, true)) { 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"); 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} 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_pHyprOpenGL->m_RenderData.blockScreenShader = true;
g_pHyprRenderer->endRender(); g_pHyprRenderer->endRender();
LOGM(TRACE, "Copied frame via dma"); auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(pMonitor->output);
if (pMonitor->inTimeline && explicitOptions.explicitEnabled) {
return true; 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() { bool CScreencopyFrame::copyShm() {
@@ -278,6 +291,8 @@ bool CScreencopyFrame::copyShm() {
const auto drmFmt = NFormatUtils::getPixelFormatFromDRM(shm.format); const auto drmFmt = NFormatUtils::getPixelFormatFromDRM(shm.format);
uint32_t packStride = NFormatUtils::minStride(drmFmt, box.w); 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) { if (packStride == (uint32_t)shm.stride) {
glReadPixels(0, 0, box.w, box.h, glFormat, PFORMAT->glType, pixelData); glReadPixels(0, 0, box.w, box.h, glFormat, PFORMAT->glType, pixelData);
} else { } else {

View File

@@ -73,7 +73,7 @@ class CScreencopyFrame {
CBox box = {}; CBox box = {};
void copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer); void copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer);
bool copyDmabuf(); void copyDmabuf(std::function<void(bool)> callback);
bool copyShm(); bool copyShm();
void share(); 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) { listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!surface || !surface->pending.texture) if (!surface || !surface->pending.buffer)
return; return;
if (surface->pending.viewport.hasSource) { if (surface->pending.viewport.hasSource) {

View File

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

View File

@@ -319,8 +319,11 @@ uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
void CXDGToplevelResource::applyState() { void CXDGToplevelResource::applyState() {
wl_array arr; wl_array arr;
wl_array_init(&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); resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr);
@@ -387,7 +390,7 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
if (toplevel) if (toplevel)
toplevel->current = toplevel->pending; toplevel->current = toplevel->pending;
if UNLIKELY (initialCommit && surface->pending.texture) { if UNLIKELY (initialCommit && surface->pending.buffer) {
resource->error(-1, "Buffer attached before initial commit"); resource->error(-1, "Buffer attached before initial commit");
return; 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_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_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; 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 "../DRMSyncobj.hpp"
#include "../../render/Renderer.hpp" #include "../../render/Renderer.hpp"
#include "config/ConfigValue.hpp" #include "config/ConfigValue.hpp"
#include "protocols/types/SurfaceRole.hpp"
#include "render/Texture.hpp"
#include <cstring> #include <cstring>
class CDefaultSurfaceRole : public ISurfaceRole { class CDefaultSurfaceRole : public ISurfaceRole {
@@ -69,12 +71,13 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
resource->setOnDestroy([this](CWlSurface* r) { destroy(); }); resource->setOnDestroy([this](CWlSurface* r) { destroy(); });
resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) { resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) {
pending.offset = {x, y}; pending.offset = {x, y};
pending.newBuffer = true; pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_BUFFER | SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OFFSET;
if (!buffer) { if (!buffer) {
pending.buffer.reset(); pending.buffer.reset();
pending.texture.reset(); pending.texture.reset();
pending.bufferSize = Vector2D{};
} else { } else {
auto res = CWLBufferResource::fromResource(buffer); auto res = CWLBufferResource::fromResource(buffer);
pending.buffer = res && res->buffer ? makeShared<CHLBufferReference>(res->buffer.lock(), self.lock()) : nullptr; 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{}; pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{};
} }
Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{}; if (pending.bufferSize != current.bufferSize)
Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{};
if (oldBufSize != newBufSize || current.buffer != pending.buffer)
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
}); });
resource->setCommit([this](CWlSurface* r) { resource->setCommit([this](CWlSurface* r) {
if (pending.texture) if (pending.buffer)
pending.bufferDamage.intersect(CBox{{}, pending.bufferSize}); pending.bufferDamage.intersect(CBox{{}, pending.bufferSize});
if (!pending.texture) if (!pending.buffer)
pending.size = {}; pending.size = {};
else if (pending.viewport.hasDestination) else if (pending.viewport.hasDestination)
pending.size = pending.viewport.destination; pending.size = pending.viewport.destination;
@@ -117,11 +117,29 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
commitPendingState(pending); 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->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t 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}); }); 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->setSetBufferScale([this](CWlSurface* r, int32_t scale) {
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { pending.transform = (wl_output_transform)tr; }); 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) { resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) {
if (!region) { if (!region) {
@@ -129,6 +147,8 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return; return;
} }
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_INPUT;
auto RG = CWLRegionResource::fromResource(region); auto RG = CWLRegionResource::fromResource(region);
pending.input = RG->region; pending.input = RG->region;
}); });
@@ -139,13 +159,18 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
return; return;
} }
pending.updated |= SSurfaceState::eUpdatedProperties::SURFACE_UPDATED_OPAQUE;
auto RG = CWLRegionResource::fromResource(region); auto RG = CWLRegionResource::fromResource(region);
pending.opaque = RG->region; pending.opaque = RG->region;
}); });
resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared<CWLCallbackResource>(makeShared<CWlCallback>(pClient, 1, id))); }); 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() { CWLSurfaceResource::~CWLSurfaceResource() {
@@ -399,58 +424,24 @@ CBox CWLSurfaceResource::extends() {
return full.getExtents(); 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) { void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
if (state.newBuffer) { auto lastTexture = current.texture;
state.newBuffer = false; current.updateFrom(state);
current = state; state.updated = 0;
state.damage.clear();
state.bufferDamage.clear(); if (current.buffer) {
state.buffer.reset(); 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) if (current.texture)
current.texture->m_eTransform = wlTransformToHyprutils(current.transform); 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) { if (role->role() == SURFACE_ROLE_SUBSURFACE) {
auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock(); auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock();
if (subsurface->sync) if (subsurface->sync)
@@ -471,16 +462,18 @@ void CWLSurfaceResource::commitPendingState(SSurfaceState& state) {
nullptr); 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. // so we can let the app know we're done.
// if (!syncobj && current.buffer && current.buffer->buffer && current.buffer->buffer->isSynchronous()) { // if it doesn't have a role, we can't release it yet, in case it gets turned into a cursor.
// dropCurrentBuffer(); // lets not drop it at all, it will get dropped on next commit if a new buffer arrives. if (current.buffer && current.buffer->buffer && current.buffer->buffer->isSynchronous() && role->role() != SURFACE_ROLE_UNASSIGNED)
// solves flickering on nonsyncobj apps on explicit sync. dropCurrentBuffer();
// }
} }
void CWLSurfaceResource::updateCursorShm(CRegion damage) { 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) if UNLIKELY (!buf)
return; return;
@@ -524,7 +517,7 @@ void CWLSurfaceResource::presentFeedback(timespec* when, PHLMONITOR pMonitor, bo
FEEDBACK->presented(); FEEDBACK->presented();
PROTO::presentation->queueData(FEEDBACK); PROTO::presentation->queueData(FEEDBACK);
if (!pMonitor || !pMonitor->outTimeline || !syncobj) if (!pMonitor || !pMonitor->inTimeline || !syncobj)
return; return;
// attach explicit sync // attach explicit sync

View File

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

View File

@@ -50,7 +50,6 @@ class CWLDataOfferResource : public IDataOffer {
private: private:
SP<CWlDataOffer> resource; SP<CWlDataOffer> resource;
wl_client* pClient = nullptr;
friend class CWLDataDeviceResource; friend class CWLDataDeviceResource;
}; };
@@ -89,7 +88,6 @@ class CWLDataSourceResource : public IDataSource {
private: private:
SP<CWlDataSource> resource; SP<CWlDataSource> resource;
wl_client* pClient = nullptr;
friend class CWLDataDeviceProtocol; 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) { void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner) if (!owner || !surface || !surface->getResource()->resource())
return; return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH))
@@ -124,7 +124,7 @@ CWLPointerResource::CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResou
return; return;
} }
if (surfResource) { if (surfResource && surfResource->role->role() != SURFACE_ROLE_CURSOR) {
surfResource->role = makeShared<CCursorSurfaceRole>(); surfResource->role = makeShared<CCursorSurfaceRole>();
surfResource->updateCursorShm(); surfResource->updateCursorShm();
} }
@@ -145,7 +145,7 @@ bool CWLPointerResource::good() {
} }
void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local) { void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local) {
if (!owner || currentSurface == surface) if (!owner || currentSurface == surface || !surface->getResource()->resource())
return; return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER))
@@ -165,7 +165,7 @@ void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2
} }
void CWLPointerResource::sendLeave() { void CWLPointerResource::sendLeave() {
if (!owner || !currentSurface) if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return; return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) 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) { void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
if (!owner || currentSurface == surface) if (!owner || currentSurface == surface || !surface->getResource()->resource())
return; return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD))
@@ -361,7 +361,7 @@ void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
} }
void CWLKeyboardResource::sendLeave() { void CWLKeyboardResource::sendLeave() {
if (!owner || !currentSurface) if (!owner || !currentSurface || !currentSurface->getResource()->resource())
return; return;
if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) 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_; offset = offset_;
opaque = NFormatUtils::isFormatOpaque(NFormatUtils::shmToDRM(fmt_)); 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)); resource = CWLBufferResource::create(makeShared<CWlBuffer>(pool_->resource->client(), 1, id));
listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) { listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) {
listeners.bufferResourceDestroy.reset(); listeners.bufferResourceDestroy.reset();
PROTO::shm->destroyResource(this); PROTO::shm->destroyResource(this);
}); });
success = texture->m_iTexID;
if UNLIKELY (!success)
Debug::log(ERR, "Failed creating a shm texture: null texture id");
} }
CWLSHMBuffer::~CWLSHMBuffer() { CWLSHMBuffer::~CWLSHMBuffer() {
@@ -74,11 +67,11 @@ void CWLSHMBuffer::endDataPtr() {
} }
bool CWLSHMBuffer::good() { bool CWLSHMBuffer::good() {
return success; return true;
} }
void CWLSHMBuffer::update(const CRegion& damage) { 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)) { 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; SP<CSHMPool> pool;
private: private:
bool success = false;
struct { struct {
CHyprSignalListener bufferResourceDestroy; CHyprSignalListener bufferResourceDestroy;
} listeners; } listeners;

View File

@@ -45,7 +45,7 @@ class CHLBufferReference {
CHLBufferReference(SP<IHLBuffer> buffer, SP<CWLSurfaceResource> surface); CHLBufferReference(SP<IHLBuffer> buffer, SP<CWLSurfaceResource> surface);
~CHLBufferReference(); ~CHLBufferReference();
WP<IHLBuffer> buffer; SP<IHLBuffer> buffer;
UP<CDRMSyncPointState> acquire; UP<CDRMSyncPointState> acquire;
UP<CDRMSyncPointState> release; 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; class CTexture;
struct SSurfaceState { 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 */; 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; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
int scale = 1; int scale = 1;
@@ -20,16 +31,15 @@ struct SSurfaceState {
Vector2D destination; Vector2D destination;
CBox source; CBox source;
} viewport; } viewport;
bool rejected = false; bool rejected = false;
bool newBuffer = false; uint8_t updated = 0; // eUpdatedProperties. Stores what the last update changed
// Vector2D sourceSize();
void reset() { // Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage
damage.clear(); CRegion accumulateBufferDamage();
bufferDamage.clear(); void updateSynchronousTexture(SP<CTexture> lastTexture);
transform = WL_OUTPUT_TRANSFORM_NORMAL; void reset();
scale = 1; // updates this state from a reference state. Mutates the reference state. If a new buffer is committed,
offset = {}; // reference state gets its damage and buffer cleared.
size = {}; void updateFrom(SSurfaceState& ref);
}
}; };

View File

@@ -158,6 +158,7 @@ void CHyprOpenGLImpl::initEGL(bool gbm) {
#else #else
attrs.push_back(EGL_CONTEXT_CLIENT_VERSION); attrs.push_back(EGL_CONTEXT_CLIENT_VERSION);
attrs.push_back(2); attrs.push_back(2);
m_eglContextVersion = EGL_CONTEXT_GLES_2_0;
#endif #endif
attrs.push_back(EGL_NONE); attrs.push_back(EGL_NONE);
@@ -176,7 +177,8 @@ void CHyprOpenGLImpl::initEGL(bool gbm) {
attrs.push_back(0); attrs.push_back(0);
attrs.push_back(EGL_NONE); 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) if (m_pEglContext == EGL_NO_CONTEXT)
RASSERT(false, "EGL: failed to create a context with either GLES3.2 or 3.0"); 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; return image;
} }
void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program) { void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program, bool silent) {
GLint maxLength = 0; GLint maxLength = 0;
if (program) if (program)
glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &maxLength); 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); 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) { GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag, bool dynamic, bool silent) {
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic); auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic, silent);
if (dynamic) { if (dynamic) {
if (vertCompiled == 0) if (vertCompiled == 0)
return 0; return 0;
} else } else
RASSERT(vertCompiled, "Compiling shader failed. VERTEX nullptr! Shader source:\n\n{}", vert); 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 (dynamic) {
if (fragCompiled == 0) if (fragCompiled == 0)
return 0; return 0;
@@ -614,7 +617,7 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string
glGetProgramiv(prog, GL_LINK_STATUS, &ok); glGetProgramiv(prog, GL_LINK_STATUS, &ok);
if (dynamic) { if (dynamic) {
if (ok == GL_FALSE) { if (ok == GL_FALSE) {
logShaderError(prog, true); logShaderError(prog, true, silent);
return 0; return 0;
} }
} else { } else {
@@ -626,7 +629,7 @@ GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string
return prog; 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 shader = glCreateShader(type);
auto shaderSource = src.c_str(); auto shaderSource = src.c_str();
@@ -639,7 +642,7 @@ GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool
if (dynamic) { if (dynamic) {
if (ok == GL_FALSE) { if (ok == GL_FALSE) {
logShaderError(shader, false); logShaderError(shader, false, silent);
return 0; return 0;
} }
} else { } else {
@@ -868,42 +871,43 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shQUAD.roundingPower = glGetUniformLocation(prog, "roundingPower"); m_RenderData.pCurrentMonData->m_shQUAD.roundingPower = glGetUniformLocation(prog, "roundingPower");
#ifndef GLES2 #ifndef GLES2
prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM, true); if (m_eglContextVersion == EGL_CONTEXT_GLES_3_2 /* GLES2 and GLES3.0 can't compile the CM shader */) {
m_bCMSupported = prog > 0; prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM, true, true);
if (m_bCMSupported) { m_bCMSupported = prog > 0;
m_RenderData.pCurrentMonData->m_shCM.program = prog; if (m_bCMSupported) {
m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj"); m_RenderData.pCurrentMonData->m_shCM.program = prog;
m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex"); m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj");
m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType"); m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex");
m_RenderData.pCurrentMonData->m_shCM.skipCM = glGetUniformLocation(prog, "skipCM"); m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType");
m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF"); m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF");
m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF"); m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF");
m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries"); m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries");
m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries"); m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries");
m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance"); m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance"); m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance");
m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance"); m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance");
m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation"); m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation");
m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier"); m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier");
m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte"); m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte");
m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha"); m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha");
m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord"); m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte"); m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte");
m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos"); m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque"); m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha"); m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha");
m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue"); m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft"); m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize"); m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius"); m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius");
m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower"); m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower");
m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint"); m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint");
m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint"); m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint");
m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte"); m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte");
} else { } else {
Debug::log( Debug::log(
ERR, 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!"); "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 #endif
@@ -1321,8 +1325,9 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
CBox newBox = box; CBox newBox = box;
m_RenderData.renderModif.applyToBox(newBox); m_RenderData.renderModif.applyToBox(newBox);
static auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking"); static const auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough"); 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 // get the needed transform for this texture
const bool TRANSFORMS_MATCH = wlTransformToHyprutils(m_RenderData.pMonitor->transform) == tex->m_eTransform; // FIXME: combine them properly!!! 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; shader = &m_RenderData.pCurrentMonData->m_shPASSTHRURGBA;
usingFinalShader = true; usingFinalShader = true;
} else { } else {
#ifndef GLES2 switch (tex->m_iType) {
if (m_bCMSupported) case TEXTURE_RGBA: shader = &m_RenderData.pCurrentMonData->m_shRGBA; break;
shader = &m_RenderData.pCurrentMonData->m_shCM; case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shRGBX; break;
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;
case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; // might be unused case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; // might be unused
default: RASSERT(false, "tex->m_iTarget unsupported!"); default: RASSERT(false, "tex->m_iTarget unsupported!");
} }
} }
} }
if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault()) { if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault())
#ifdef GLES2
shader = &m_RenderData.pCurrentMonData->m_shRGBX;
#endif
texType = TEXTURE_RGBX; texType = TEXTURE_RGBX;
}
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(tex->m_iTarget, tex->m_iTexID); 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); 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); 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 #ifndef GLES2
glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data()); glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data());
#else #else
@@ -1397,49 +1445,6 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data()); glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data());
#endif #endif
glUniform1i(shader->tex, 0); 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) { if ((usingFinalShader && *PDT == 0) || CRASHING) {
glUniform1f(shader->time, m_tGlobalTimer.getSeconds() - shader->initialTime); glUniform1f(shader->time, m_tGlobalTimer.getSeconds() - shader->initialTime);
@@ -3016,7 +3021,7 @@ CEGLSync::~CEGLSync() {
if (sync == EGL_NO_SYNC_KHR) if (sync == EGL_NO_SYNC_KHR)
return; 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"); Debug::log(ERR, "eglDestroySyncKHR failed");
} }

View File

@@ -276,6 +276,14 @@ class CHyprOpenGLImpl {
} m_sExts; } m_sExts;
private: 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_lBuffers;
std::list<GLuint> m_lTextures; 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 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); void logShaderError(const GLuint&, bool program = false, bool silent = false);
GLuint createProgram(const std::string&, const std::string&, bool dynamic = false); GLuint createProgram(const std::string&, const std::string&, bool dynamic = false, bool silent = false);
GLuint compileShader(const GLuint&, std::string, bool dynamic = false); GLuint compileShader(const GLuint&, std::string, bool dynamic = false, bool silent = false);
void createBGTextureForMonitor(PHLMONITOR); void createBGTextureForMonitor(PHLMONITOR);
void initShaders(); void initShaders();
void initDRMFormats(); void initDRMFormats();

View File

@@ -33,9 +33,7 @@
#include "pass/SurfacePassElement.hpp" #include "pass/SurfacePassElement.hpp"
#include "debug/Log.hpp" #include "debug/Log.hpp"
#include "../protocols/ColorManagement.hpp" #include "../protocols/ColorManagement.hpp"
#if AQUAMARINE_VERSION_NUMBER > 702 // >0.7.2
#include "../protocols/types/ContentType.hpp" #include "../protocols/types/ContentType.hpp"
#endif
#include <hyprutils/utils/ScopeGuard.hpp> #include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::Utils; using namespace Hyprutils::Utils;
@@ -1216,7 +1214,8 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
pMonitor->tearingState.activelyTearing = shouldTear; pMonitor->tearingState.activelyTearing = shouldTear;
if ((*PDIRECTSCANOUT == 1 || 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) { !shouldTear) {
if (pMonitor->attemptDirectScanout()) { if (pMonitor->attemptDirectScanout()) {
return; 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 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) { static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
if (settings.transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ) uint8_t eotf = 0;
return NO_HDR_METADATA; // empty metadata for SDR 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 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 colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries;
auto luminances = settings.masteringLuminances.max > 0 ? auto luminances = settings.masteringLuminances.max > 0 ?
@@ -1430,7 +1434,7 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, A
.metadata_type = 0, .metadata_type = 0,
.hdmi_metadata_type1 = .hdmi_metadata_type1 =
hdr_metadata_infoframe{ hdr_metadata_infoframe{
.eotf = 2, .eotf = eotf,
.metadata_type = 0, .metadata_type = 0,
.display_primaries = .display_primaries =
{ {
@@ -1448,8 +1452,6 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, A
} }
bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
pMonitor->commitSeq++;
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough"); static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
const bool PHDR = pMonitor->imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ; 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); Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ);
if (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) { if (*PPASS && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow(); const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
const auto ROOT_SURF = WINDOW->m_pWLSurface->resource(); const auto ROOT_SURF = WINDOW->m_pWLSurface->resource();
const auto SURF = const auto SURF =
ROOT_SURF->findFirstPreorder([ROOT_SURF](SP<CWLSurfaceResource> surf) { return surf->colorManagement.valid() && surf->extends() == ROOT_SURF->extends(); }); 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; bool needsHdrMetadataUpdate = SURF->colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW;
if (SURF->colorManagement->needsHdrMetadataUpdate()) if (SURF->colorManagement->needsHdrMetadataUpdate())
SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID)); SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID));
if (needsHdrMetadataUpdate) if (needsHdrMetadataUpdate)
pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata()); pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata());
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR) hdrIsHandled = true;
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA); }
pMonitor->m_previousFSWindow = WINDOW; pMonitor->m_previousFSWindow = WINDOW;
} else { }
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR) if (!hdrIsHandled) {
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA); 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(); 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) { if (pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow(); const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
pMonitor->output->state->setContentType(NContentType::toDRM(WINDOW->getContentType())); pMonitor->output->state->setContentType(NContentType::toDRM(WINDOW->getContentType()));
} else } else
pMonitor->output->state->setContentType(NContentType::toDRM(CONTENT_TYPE_NONE)); pMonitor->output->state->setContentType(NContentType::toDRM(CONTENT_TYPE_NONE));
#endif
if (pMonitor->ctmUpdated) { if (pMonitor->ctmUpdated) {
pMonitor->ctmUpdated = false; pMonitor->ctmUpdated = false;
@@ -1530,21 +1548,19 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
return ok; return ok;
Debug::log(TRACE, "Explicit: {} presented", explicitPresented.size()); Debug::log(TRACE, "Explicit: {} presented", explicitPresented.size());
auto sync = g_pHyprOpenGL->createEGLSync(pMonitor->inFence.get());
if (!sync) if (!pMonitor->eglSync)
Debug::log(TRACE, "Explicit: can't add sync, EGLSync failed"); Debug::log(TRACE, "Explicit: can't add sync, monitor has no EGLSync");
else { else {
for (auto const& e : explicitPresented) { 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; continue;
e->current.buffer->buffer->syncReleaser->addReleaseSync(sync); e->current.buffer->buffer->syncReleaser->addReleaseSync(pMonitor->eglSync);
} }
} }
explicitPresented.clear(); explicitPresented.clear();
pMonitor->output->state->resetExplicitFences();
return ok; return ok;
} }
@@ -2255,37 +2271,39 @@ void CHyprRenderer::endRender() {
if (m_eRenderMode == RENDER_MODE_FULL_FAKE) if (m_eRenderMode == RENDER_MODE_FULL_FAKE)
return; return;
if (m_eRenderMode == RENDER_MODE_NORMAL) { if (m_eRenderMode == RENDER_MODE_NORMAL)
PMONITOR->output->state->setBuffer(m_pCurrentBuffer); PMONITOR->output->state->setBuffer(m_pCurrentBuffer);
auto explicitOptions = getExplicitSyncSettings(PMONITOR->output); auto explicitOptions = getExplicitSyncSettings(PMONITOR->output);
if (PMONITOR->inTimeline && explicitOptions.explicitEnabled && explicitOptions.explicitKMSEnabled) { if (PMONITOR->inTimeline && explicitOptions.explicitEnabled) {
auto sync = g_pHyprOpenGL->createEGLSync(); PMONITOR->eglSync = g_pHyprOpenGL->createEGLSync();
if (!sync) { if (!PMONITOR->eglSync) {
Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender"); Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender");
return; return;
} }
bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->commitSeq, sync->fd()); PMONITOR->inTimelinePoint++;
if (!ok) { bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->inTimelinePoint, PMONITOR->eglSync->fd());
Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender"); if (!ok) {
return; 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()) { if (!PMONITOR->inFence.isValid()) {
Debug::log(ERR, "renderer: couldn't export from sync timeline in endRender"); Debug::log(ERR, "renderer: couldn't export from sync timeline in endRender");
return; return;
} }
PMONITOR->output->state->setExplicitInFence(PMONITOR->inFence.get()); 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 color = -1;
GLint alphaMatte = -1; GLint alphaMatte = -1;
GLint texType = -1; GLint texType = -1;
GLint skipCM = -1;
GLint sourceTF = -1; GLint sourceTF = -1;
GLint targetTF = -1; GLint targetTF = -1;
GLint sourcePrimaries = -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); const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat);
ASSERT(format); ASSERT(format);
m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX;
m_vSize = size_; m_vSize = size_;
m_isSynchronous = true;
allocate(); allocate();
GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID)); 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); void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage);
const std::vector<uint8_t>& dataCopy(); const std::vector<uint8_t>& dataCopy();
eTextureType m_iType = TEXTURE_RGBA; eTextureType m_iType = TEXTURE_RGBA;
GLenum m_iTarget = GL_TEXTURE_2D; GLenum m_iTarget = GL_TEXTURE_2D;
GLuint m_iTexID = 0; GLuint m_iTexID = 0;
Vector2D m_vSize = {}; Vector2D m_vSize = {};
void* m_pEglImage = nullptr; void* m_pEglImage = nullptr;
eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL;
bool m_bOpaque = false; bool m_bOpaque = false;
uint32_t m_iDrmFormat = 0; // for shm uint32_t m_iDrmFormat = 0; // for shm
bool m_isSynchronous = false;
private: private:
void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); 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); const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0);
info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + *POUTERGAP}, {0, 0}}; info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + *POUTERGAP}, {0, 0}};
} else } 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 } else
info.desiredExtents = {{0, 0}, {0, 0}}; info.desiredExtents = {{0, 0}, {0, 0}};
return info; return info;
@@ -54,7 +54,6 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
void CHyprGroupBarDecoration::onPositioningReply(const SDecorationPositioningReply& reply) { void CHyprGroupBarDecoration::onPositioningReply(const SDecorationPositioningReply& reply) {
m_bAssignedBox = reply.assignedGeometry; m_bAssignedBox = reply.assignedGeometry;
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pWindow.lock());
} }
eDecorationType CHyprGroupBarDecoration::getDecorationType() { 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, ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - *PINDICATORHEIGHT - *POUTERGAP - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
*PINDICATORHEIGHT}; *PINDICATORHEIGHT};
rect.scale(pMonitor->scale); rect.scale(pMonitor->scale).round();
const bool GROUPLOCKED = m_pWindow->getGroupHead()->m_sGroupData.locked || g_pKeybindManager->m_bGroupsLocked; const bool GROUPLOCKED = m_pWindow->getGroupHead()->m_sGroupData.locked || g_pKeybindManager->m_bGroupsLocked;
const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE; 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 CHyprColor COLOR = CHyprColor(*PTEXTCOLOR);
const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT; 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) if (tex)
texSize = tex->m_vSize; texSize = tex->m_vSize;
@@ -574,5 +573,5 @@ CBox CHyprGroupBarDecoration::assignedBoxGlobal() {
if (PWORKSPACE && !m_pWindow->m_bPinned) if (PWORKSPACE && !m_pWindow->m_bPinned)
box.translate(PWORKSPACE->m_vRenderOffset->value()); 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() { 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) if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = *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, g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower);
data.syncPoint);
if (data.replaceProjection) if (data.replaceProjection)
g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix; g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix;
} }

View File

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

View File

@@ -8,7 +8,6 @@ uniform sampler2D tex;
//uniform samplerExternalOES texture0; //uniform samplerExternalOES texture0;
uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext
uniform int skipCM;
uniform int sourceTF; // eTransferFunction uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries; uniform mat4x2 sourcePrimaries;
@@ -408,26 +407,28 @@ void main() {
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue) if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
discard; discard;
if (skipCM == 0) { pixColor.rgb /= max(pixColor.a, 0.001);
pixColor.rgb /= max(pixColor.a, 0.001); pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF);
pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF); mat3 srcxyz = primaries2xyz(sourcePrimaries);
mat3 srcxyz = primaries2xyz(sourcePrimaries); mat3 dstxyz;
mat3 dstxyz;
if (sourcePrimaries == targetPrimaries) if (sourcePrimaries == targetPrimaries)
dstxyz = srcxyz; dstxyz = srcxyz;
else { else {
dstxyz = primaries2xyz(targetPrimaries); dstxyz = primaries2xyz(targetPrimaries);
pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]); 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 = 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) if (applyTint == 1)
pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]); 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_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[0] = g_pXWayland->pWM->dndSelection.window;
data.data32[1] = XDND_VERSION << 24; data.data32[1] = XDND_VERSION << 24;
data.data32[1] |= 1; data.data32[1] |= 1;
@@ -164,7 +164,7 @@ void CX11DataDevice::sendLeave() {
xcb_window_t targetWindow = getProxyWindow(lastSurface->xID); 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[0] = g_pXWayland->pWM->dndSelection.window;
sendDndEvent(targetWindow, HYPRATOMS["XdndLeave"], data); 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 auto XCOORDS = g_pXWaylandManager->waylandToXWaylandCoords(lastSurfaceCoords + local);
const uint32_t coords = ((uint32_t)XCOORDS.x << 16) | (uint32_t)XCOORDS.y; 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[0] = g_pXWayland->pWM->dndSelection.window;
data.data32[2] = coords; data.data32[2] = coords;
data.data32[3] = timeMs; data.data32[3] = timeMs;
@@ -204,7 +204,7 @@ void CX11DataDevice::sendDrop() {
xcb_window_t targetWindow = getProxyWindow(lastSurface->xID); 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[0] = g_pXWayland->pWM->dndSelection.window;
data.data32[2] = lastTime; data.data32[2] = lastTime;

View File

@@ -20,6 +20,7 @@
#include "Server.hpp" #include "Server.hpp"
#include "XWayland.hpp" #include "XWayland.hpp"
#include "config/ConfigValue.hpp"
#include "debug/Log.hpp" #include "debug/Log.hpp"
#include "../defines.hpp" #include "../defines.hpp"
#include "../Compositor.hpp" #include "../Compositor.hpp"
@@ -32,31 +33,45 @@ constexpr int SOCKET_BACKLOG = 1;
constexpr int MAX_SOCKET_RETRIES = 32; constexpr int MAX_SOCKET_RETRIES = 32;
constexpr int LOCK_FILE_MODE = 0444; constexpr int LOCK_FILE_MODE = 0444;
static CFileDescriptor createSocket(struct sockaddr_un* addr, size_t path_size) { static CFileDescriptor createSocket(struct sockaddr_un* addr, size_t pathSize) {
socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1; const bool isRegularSocket(addr->sun_path[0]);
CFileDescriptor fd{socket(AF_UNIX, SOCK_STREAM, 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()) { 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 {}; return {};
} }
if (!fd.setFlags(fd.getFlags() | FD_CLOEXEC)) { if (!fd.setFlags(fd.getFlags() | FD_CLOEXEC)) {
Debug::log(ERR, "Failed to set flags for socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
return {}; return {};
} }
if (addr->sun_path[0]) if (isRegularSocket)
unlink(addr->sun_path); unlink(addr->sun_path);
if (bind(fd.get(), (struct sockaddr*)addr, size) < 0) { 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); Debug::log(ERR, "Failed to bind socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
if (addr->sun_path[0]) if (isRegularSocket)
unlink(addr->sun_path); unlink(addr->sun_path);
return {}; 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) { 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); Debug::log(ERR, "Failed to listen to socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem);
if (addr->sun_path[0]) if (isRegularSocket)
unlink(addr->sun_path); unlink(addr->sun_path);
return {}; return {};
} }
@@ -113,14 +128,32 @@ static std::string getSocketPath(int display, bool isLinux) {
} }
static bool openSockets(std::array<CFileDescriptor, 2>& sockets, int display) { static bool openSockets(std::array<CFileDescriptor, 2>& sockets, int display) {
static auto CREATEABSTRACTSOCKET = CConfigValue<Hyprlang::INT>("xwayland:create_abstract_socket");
if (!ensureSocketDirExists()) if (!ensureSocketDirExists())
return false; return false;
sockaddr_un addr = {.sun_family = AF_UNIX}; sockaddr_un addr = {.sun_family = AF_UNIX};
std::string path; 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); path = getSocketPath(display, false);
strncpy(addr.sun_path, path.c_str(), path.length() + 1); strncpy(addr.sun_path, path.c_str(), path.length() + 1);
#endif
sockets[0] = CFileDescriptor{createSocket(&addr, path.length())}; sockets[0] = CFileDescriptor{createSocket(&addr, path.length())};
if (!sockets[0].isValid()) if (!sockets[0].isValid())
@@ -144,8 +177,7 @@ static void startServer(void* data) {
} }
static int xwaylandReady(int fd, uint32_t mask, void* data) { static int xwaylandReady(int fd, uint32_t mask, void* data) {
CFileDescriptor xwlFd{fd}; return g_pXWayland->pServer->ready(fd, mask);
return g_pXWayland->pServer->ready(std::move(xwlFd), mask);
} }
static bool safeRemove(const std::string& path) { static bool safeRemove(const std::string& path) {
@@ -220,12 +252,10 @@ CXWaylandServer::~CXWaylandServer() {
safeRemove(lockPath); safeRemove(lockPath);
std::string path; std::string path;
#ifdef __linux__ for (bool isLinux : {true, false}) {
path = getSocketPath(display, true); path = getSocketPath(display, isLinux);
#else safeRemove(path);
path = getSocketPath(display, false); }
#endif
safeRemove(path);
} }
void CXWaylandServer::die() { void CXWaylandServer::die() {
@@ -322,7 +352,7 @@ bool CXWaylandServer::start() {
return false; return false;
} }
waylandFDs[0].take(); // does this leak? waylandFDs[0].take(); // wl_client owns this fd now
int notify[2] = {-1, -1}; int notify[2] = {-1, -1};
if (pipe(notify) < 0) { 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); pipeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, notifyFds[0].get(), WL_EVENT_READABLE, ::xwaylandReady, nullptr);
pipeFd = std::move(notifyFds[0]); pipeFd = std::move(notifyFds[0]);
serverPID = fork(); auto serverPID = fork();
if (serverPID < 0) { if (serverPID < 0) {
Debug::log(ERR, "fork failed"); Debug::log(ERR, "fork failed");
die(); die();
return false; return false;
} else if (serverPID == 0) { } else if (serverPID == 0) {
pid_t pid = fork(); runXWayland(notifyFds[1]);
if (pid < 0) {
Debug::log(ERR, "second fork failed");
_exit(1);
} else if (pid == 0)
runXWayland(notifyFds[1]);
_exit(0); _exit(0);
} }
return true; return true;
} }
int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) { int CXWaylandServer::ready(int fd, uint32_t mask) {
if (mask & WL_EVENT_READABLE) { if (mask & WL_EVENT_READABLE) {
// xwayland writes twice // xwayland writes twice
char buf[64]; 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) { if (n < 0 && errno != EINTR) {
Debug::log(ERR, "Xwayland: read from displayFd failed"); Debug::log(ERR, "Xwayland: read from displayFd failed");
mask = 0; mask = 0;
@@ -373,14 +397,6 @@ int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) {
return 1; 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 we don't have readable here, it failed
if (!(mask & WL_EVENT_READABLE)) { if (!(mask & WL_EVENT_READABLE)) {
Debug::log(ERR, "Xwayland: startup failed, not setting up xwm"); 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"); Debug::log(LOG, "XWayland is ready");
wl_event_source_remove(pipeSource); wl_event_source_remove(pipeSource);
pipeFd.reset();
pipeSource = nullptr; pipeSource = nullptr;
// start the wm // start the wm

View File

@@ -20,7 +20,7 @@ class CXWaylandServer {
bool start(); bool start();
// called on ready // called on ready
int ready(Hyprutils::OS::CFileDescriptor fd, uint32_t mask); int ready(int fd, uint32_t mask);
void die(); void die();
@@ -34,8 +34,6 @@ class CXWaylandServer {
bool tryOpenSockets(); bool tryOpenSockets();
void runXWayland(Hyprutils::OS::CFileDescriptor& notifyFD); void runXWayland(Hyprutils::OS::CFileDescriptor& notifyFD);
pid_t serverPID = 0;
std::string displayName; std::string displayName;
int display = -1; int display = -1;
std::array<Hyprutils::OS::CFileDescriptor, 2> xFDs; 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) { listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) {
if (surface->pending.texture && !mapped) { if (surface->current.texture && !mapped) {
map(); map();
return; return;
} }
if (!surface->pending.texture && mapped) { if (!surface->current.texture && mapped) {
unmap(); unmap();
return; return;
} }
@@ -131,7 +131,7 @@ void CXWaylandSurface::considerMap() {
return; return;
} }
if (surface->pending.texture) { if (surface->current.texture) {
Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer"); Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer");
map(); map();
return; return;

View File

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