mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-23 06:05:59 +01:00
Merge branch 'main' into main
This commit is contained in:
commit
b16d2e2c51
121 changed files with 3175 additions and 2490 deletions
5
.github/workflows/ci.yaml
vendored
5
.github/workflows/ci.yaml
vendored
|
@ -3,6 +3,7 @@ name: Build Hyprland
|
|||
on: [push, pull_request, workflow_dispatch]
|
||||
jobs:
|
||||
gcc:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Build Hyprland (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
|
@ -44,6 +45,7 @@ jobs:
|
|||
path: Hyprland.tar.xz
|
||||
|
||||
meson:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Build Hyprland with Meson (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
|
@ -64,6 +66,7 @@ jobs:
|
|||
run: ninja -C build
|
||||
|
||||
no-pch:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Build Hyprland without precompiled headers (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
|
@ -83,6 +86,7 @@ jobs:
|
|||
run: make nopch
|
||||
|
||||
noxwayland:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Build Hyprland in pure Wayland (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
|
@ -103,6 +107,7 @@ jobs:
|
|||
run: make release
|
||||
|
||||
clang-format:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Code Style (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
|
|
6
.github/workflows/nix-build.yml
vendored
6
.github/workflows/nix-build.yml
vendored
|
@ -15,14 +15,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
submodules: recursive
|
||||
|
||||
- uses: cachix/install-nix-action@v26
|
||||
- uses: cachix/install-nix-action@v27
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: hyprland
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
|
|
2
.github/workflows/nix-ci.yml
vendored
2
.github/workflows/nix-ci.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
secrets: inherit
|
||||
|
||||
build:
|
||||
if: always() && !cancelled() && !contains(needs.*.result, 'failure')
|
||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) && !contains(needs.*.result, 'failure')
|
||||
needs: update-inputs
|
||||
uses: ./.github/workflows/nix-build.yml
|
||||
secrets: inherit
|
||||
|
|
1
.github/workflows/security-checks.yml
vendored
1
.github/workflows/security-checks.yml
vendored
|
@ -4,6 +4,7 @@ on: [push, pull_request]
|
|||
|
||||
jobs:
|
||||
flawfinder:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: Flawfinder Checks
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.27)
|
||||
|
||||
include(CheckIncludeFile)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Get version
|
||||
file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
|
||||
string(STRIP ${VER_RAW} VER)
|
||||
|
@ -12,6 +9,9 @@ project(
|
|||
DESCRIPTION "A Modern C++ Wayland Compositor"
|
||||
VERSION ${VER})
|
||||
|
||||
include(CheckIncludeFile)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(HYPRLAND_VERSION ${VER})
|
||||
set(PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
@ -49,8 +49,6 @@ endif()
|
|||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_get_variable(WaylandScanner wayland-scanner wayland_scanner)
|
||||
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
||||
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
|
||||
pkg_get_variable(WAYLAND_SERVER_DIR wayland-server pkgdatadir)
|
||||
|
@ -115,7 +113,7 @@ pkg_check_modules(
|
|||
gbm
|
||||
hyprlang>=0.3.2
|
||||
hyprcursor>=0.1.7
|
||||
hyprutils>=0.2.0)
|
||||
hyprutils>=0.2.1)
|
||||
|
||||
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
|
||||
|
||||
|
@ -232,30 +230,6 @@ target_link_libraries(Hyprland rt PkgConfig::deps)
|
|||
# used by `make installheaders`, to ensure the headers are generated
|
||||
add_custom_target(generate-protocol-headers)
|
||||
|
||||
function(protocol protoPath protoName external)
|
||||
if(external)
|
||||
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
|
||||
else()
|
||||
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
|
||||
COMMAND ${WaylandScanner} server-header ${path}
|
||||
protocols/${protoName}-protocol.h
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c
|
||||
COMMAND ${WaylandScanner} private-code ${path}
|
||||
protocols/${protoName}-protocol.c
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
target_sources(
|
||||
Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
|
||||
${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c)
|
||||
target_sources(generate-protocol-headers
|
||||
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h)
|
||||
endfunction()
|
||||
|
||||
function(protocolnew protoPath protoName external)
|
||||
if(external)
|
||||
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
|
||||
|
@ -288,17 +262,12 @@ endfunction()
|
|||
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads
|
||||
libudis86 uuid)
|
||||
|
||||
protocol("protocols/wlr-screencopy-unstable-v1.xml"
|
||||
"wlr-screencopy-unstable-v1" true)
|
||||
protocol(
|
||||
"subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml"
|
||||
"hyprland-global-shortcuts-v1" true)
|
||||
protocol(
|
||||
"subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml"
|
||||
"hyprland-toplevel-export-v1" true)
|
||||
protocol("unstable/text-input/text-input-unstable-v1.xml"
|
||||
"text-input-unstable-v1" false)
|
||||
|
||||
protocolnew("subprojects/hyprland-protocols/protocols"
|
||||
"hyprland-global-shortcuts-v1" true)
|
||||
protocolnew("unstable/text-input" "text-input-unstable-v1" false)
|
||||
protocolnew("subprojects/hyprland-protocols/protocols"
|
||||
"hyprland-toplevel-export-v1" true)
|
||||
protocolnew("protocols" "wlr-screencopy-unstable-v1" true)
|
||||
protocolnew("protocols" "wlr-gamma-control-unstable-v1" true)
|
||||
protocolnew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true)
|
||||
protocolnew("protocols" "wlr-output-power-management-unstable-v1" true)
|
||||
|
@ -359,19 +328,22 @@ install(
|
|||
|
||||
# session file
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/wayland-sessions)
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
|
||||
|
||||
# allow Hyprland to find wallpapers
|
||||
add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}")
|
||||
|
||||
# wallpapers
|
||||
file(GLOB_RECURSE WALLPAPERS "assets/wall*")
|
||||
install(FILES ${WALLPAPERS} DESTINATION ${CMAKE_INSTALL_DATADIR}/hyprland)
|
||||
install(FILES ${WALLPAPERS} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
|
||||
|
||||
# default config
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.conf
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/hyprland)
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
|
||||
|
||||
# portal config
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/assets/hyprland-portals.conf
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/xdg-desktop-portal)
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xdg-desktop-portal)
|
||||
|
||||
# man pages
|
||||
file(GLOB_RECURSE MANPAGES "docs/*.1")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
wallpapers = ['0', '1', '2']
|
||||
|
||||
foreach type : wallpapers
|
||||
install_data(f'wall@type@.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime')
|
||||
install_data(f'wall@type@.png', install_dir: join_paths(get_option('datadir'), 'hypr'), install_tag: 'runtime')
|
||||
endforeach
|
||||
|
||||
install_data('hyprland-portals.conf', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'), install_tag: 'runtime')
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
install_data('hyprland.conf', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime')
|
||||
install_data('hyprland.conf', install_dir: join_paths(get_option('datadir'), 'hypr'), install_tag: 'runtime')
|
||||
install_data('hyprland.desktop', install_dir: join_paths(get_option('datadir'), 'wayland-sessions'), install_tag: 'runtime')
|
||||
|
|
30
flake.lock
30
flake.lock
|
@ -16,11 +16,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1721571743,
|
||||
"narHash": "sha256-hat7wggtDISBJD8kTo5MTrT+IsY/Ha2MwgjmqqijoCA=",
|
||||
"lastModified": 1722347739,
|
||||
"narHash": "sha256-rAoh+K6KG+b1DwSWtqRVocdojnH6nGk6q07mNltoUSM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"rev": "601f6cf95cbe4fef02dc7faf34bba58566c914e9",
|
||||
"rev": "7c3565f9bedc7cb601cc0baa14792247e4dc1d5a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -67,11 +67,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1718746314,
|
||||
"narHash": "sha256-HUklK5u86w2Yh9dOkk4FdsL8eehcOZ95jPhLixGDRQY=",
|
||||
"lastModified": 1721326555,
|
||||
"narHash": "sha256-zCu4R0CSHEactW9JqYki26gy8h9f6rHmSwj4XJmlHgg=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "1b61f0093afff20ab44d88ad707aed8bf2215290",
|
||||
"rev": "5a11232266bf1a1f5952d5b179c3f4b2facaaa84",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -116,11 +116,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1721324102,
|
||||
"narHash": "sha256-WAZ0X6yJW1hFG6otkHBfyJDKRpNP5stsRqdEuHrFRpk=",
|
||||
"lastModified": 1722098849,
|
||||
"narHash": "sha256-D3wIZlBNh7LuZ0NaoCpY/Pvu+xHxIVtSN+KkWZYvvVs=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "962582a090bc233c4de9d9897f46794280288989",
|
||||
"rev": "5dcbbc1e3de40b2cecfd2007434d86e924468f1f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -154,11 +154,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1721379653,
|
||||
"narHash": "sha256-8MUgifkJ7lkZs3u99UDZMB4kbOxvMEXQZ31FO3SopZ0=",
|
||||
"lastModified": 1722185531,
|
||||
"narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1d9c2c9b3e71b9ee663d11c5d298727dace8d374",
|
||||
"rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -209,11 +209,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720194466,
|
||||
"narHash": "sha256-Rizg9efi6ue95zOp0MeIV2ZedNo+5U9G2l6yirgBUnA=",
|
||||
"lastModified": 1722365976,
|
||||
"narHash": "sha256-Khdm+mDzYA//XaU0M+hftod+rKr5q9SSHSEuiQ0/9ow=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"rev": "b9b97e5ba23fe7bd5fa4df54696102e8aa863cf6",
|
||||
"rev": "7f2a77ddf60390248e2a3de2261d7102a13e5341",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -141,7 +141,7 @@ int rollingRead(const int socket) {
|
|||
int request(std::string arg, int minArgs = 0, bool needRoll = false) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
auto t = timeval{.tv_sec = 1, .tv_usec = 0};
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
|
||||
|
||||
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
|
||||
|
|
|
@ -464,12 +464,22 @@ bool CPluginManager::updateHeaders(bool force) {
|
|||
progress.m_szCurrentMessage = "Checking out sources";
|
||||
progress.print();
|
||||
|
||||
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.branch + " 2>&1");
|
||||
if (m_bVerbose)
|
||||
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will run: " + "cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
|
||||
|
||||
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
|
||||
|
||||
if (ret.contains("fatal: unable to read tree")) {
|
||||
std::cerr << "\n"
|
||||
<< Colors::RED << "✖" << Colors::RESET
|
||||
<< " Could not checkout the running Hyprland commit. If you are on -git, try updating.\nYou can also try re-running hyprpm update with --no-shallow.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_bVerbose)
|
||||
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (co): " + ret);
|
||||
|
||||
ret = execAndGet("cd " + WORKINGDIR + " && git rm subprojects/tracy && git submodule update --init 2>&1 && git reset --hard --recurse-submodules " + HLVER.hash);
|
||||
ret = execAndGet("cd " + WORKINGDIR + " ; git rm subprojects/tracy ; git submodule update --init 2>&1 ; git reset --hard --recurse-submodules " + HLVER.hash);
|
||||
|
||||
if (m_bVerbose)
|
||||
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (rs): " + ret);
|
||||
|
@ -529,7 +539,8 @@ bool CPluginManager::updateHeaders(bool force) {
|
|||
|
||||
std::cout << "\n";
|
||||
} else {
|
||||
progress.printMessageAbove(std::string{Colors::RED} + "✖" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID));
|
||||
progress.printMessageAbove(std::string{Colors::RED} + "✖" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID) + " (" +
|
||||
headerErrorShort(HEADERSVALID) + ")");
|
||||
progress.m_iSteps = 5;
|
||||
progress.m_szCurrentMessage = "Failed";
|
||||
progress.print();
|
||||
|
@ -880,6 +891,18 @@ std::string CPluginManager::headerError(const eHeadersErrors err) {
|
|||
return std::string{Colors::RED} + "✖" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n";
|
||||
}
|
||||
|
||||
std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
|
||||
switch (err) {
|
||||
case HEADERS_CORRUPTED: return "Headers corrupted";
|
||||
case HEADERS_MISMATCHED: return "Headers version mismatched";
|
||||
case HEADERS_NOT_HYPRLAND: return "Not running on Hyprland";
|
||||
case HEADERS_MISSING: return "Headers missing";
|
||||
case HEADERS_DUPLICATED: return "Headers duplicated";
|
||||
default: break;
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
bool CPluginManager::hasDeps() {
|
||||
std::vector<std::string> deps = {"meson", "cpio", "cmake"};
|
||||
for (auto& d : deps) {
|
||||
|
|
|
@ -66,6 +66,7 @@ class CPluginManager {
|
|||
|
||||
private:
|
||||
std::string headerError(const eHeadersErrors err);
|
||||
std::string headerErrorShort(const eHeadersErrors err);
|
||||
|
||||
std::string m_szWorkingPluginDirectory = "";
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ project('Hyprland', 'cpp', 'c',
|
|||
'cpp_std=c++23',
|
||||
])
|
||||
|
||||
datarootdir = '-DDATAROOTDIR="' + get_option('prefix') / get_option('datadir') + '"'
|
||||
add_project_arguments(
|
||||
[
|
||||
'-Wno-unused-parameter',
|
||||
|
@ -16,6 +17,7 @@ add_project_arguments(
|
|||
'-Wno-missing-field-initializers',
|
||||
'-Wno-narrowing',
|
||||
'-Wno-pointer-arith',
|
||||
datarootdir,
|
||||
],
|
||||
language: 'cpp')
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
libuuid,
|
||||
libxkbcommon,
|
||||
mesa,
|
||||
meson,
|
||||
pango,
|
||||
pciutils,
|
||||
pcre2,
|
||||
|
@ -90,7 +89,6 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
|
|||
jq
|
||||
makeWrapper
|
||||
cmake
|
||||
meson # for wlroots
|
||||
ninja
|
||||
pkg-config
|
||||
python3 # for udis86
|
||||
|
@ -114,10 +112,12 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
|
|||
hyprcursor
|
||||
hyprlang
|
||||
hyprutils
|
||||
libGL
|
||||
libdrm
|
||||
libdatrie
|
||||
libdisplay-info
|
||||
libdrm
|
||||
libGL
|
||||
libinput
|
||||
libliftoff
|
||||
libselinux
|
||||
libsepol
|
||||
libthai
|
||||
|
@ -127,13 +127,10 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
|
|||
pango
|
||||
pciutils
|
||||
pcre2
|
||||
seatd
|
||||
tomlplusplus
|
||||
wayland
|
||||
wayland-protocols
|
||||
# for wlroots
|
||||
seatd
|
||||
libdisplay-info
|
||||
libliftoff
|
||||
]
|
||||
(lib.optionals stdenv.hostPlatform.isMusl [libexecinfo])
|
||||
(lib.optionals enableXWayland [
|
||||
|
|
|
@ -12,24 +12,12 @@ hyprland_protos = dependency('hyprland-protocols',
|
|||
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
|
||||
hl_protocol_dir = hyprland_protos.get_variable('pkgdatadir')
|
||||
|
||||
wayland_scanner_dep = dependency('wayland-scanner', native: true)
|
||||
wayland_scanner = find_program(
|
||||
wayland_scanner_dep.get_variable('wayland_scanner'),
|
||||
native: true,
|
||||
)
|
||||
hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.8', native: true)
|
||||
hyprwayland_scanner = find_program(
|
||||
hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'),
|
||||
native: true,
|
||||
)
|
||||
|
||||
protocols = [
|
||||
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
|
||||
['wlr-screencopy-unstable-v1.xml'],
|
||||
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
|
||||
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml']
|
||||
]
|
||||
|
||||
new_protocols = [
|
||||
['wlr-gamma-control-unstable-v1.xml'],
|
||||
['wlr-foreign-toplevel-management-unstable-v1.xml'],
|
||||
|
@ -42,6 +30,9 @@ new_protocols = [
|
|||
['wlr-layer-shell-unstable-v1.xml'],
|
||||
['wayland-drm.xml'],
|
||||
['wlr-data-control-unstable-v1.xml'],
|
||||
['wlr-screencopy-unstable-v1.xml'],
|
||||
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml'],
|
||||
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
|
||||
[hl_protocol_dir, 'protocols/hyprland-focus-grab-v1.xml'],
|
||||
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
|
||||
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
|
||||
|
@ -55,6 +46,7 @@ new_protocols = [
|
|||
[wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v3.xml'],
|
||||
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'],
|
||||
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'],
|
||||
|
@ -73,24 +65,6 @@ new_protocols = [
|
|||
wl_protos_src = []
|
||||
wl_protos_headers = []
|
||||
|
||||
foreach p : protocols
|
||||
xml = join_paths(p)
|
||||
wl_protos_src += custom_target(
|
||||
xml.underscorify() + '_server_c',
|
||||
input: xml,
|
||||
output: '@BASENAME@-protocol.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
wl_protos_headers += custom_target(
|
||||
xml.underscorify() + '_server_h',
|
||||
input: xml,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('includedir'), 'hyprland/protocols'),
|
||||
output: '@BASENAME@-protocol.h',
|
||||
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
endforeach
|
||||
|
||||
new_wl_protos = []
|
||||
foreach p : new_protocols
|
||||
xml = join_paths(p)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "managers/SeatManager.hpp"
|
||||
#include "managers/eventLoop/EventLoopManager.hpp"
|
||||
#include <aquamarine/output/Output.hpp>
|
||||
#include <bit>
|
||||
#include <random>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
@ -43,7 +44,7 @@ int handleCritSignal(int signo, void* data) {
|
|||
Debug::log(LOG, "Hyprland received signal {}", signo);
|
||||
|
||||
if (signo == SIGTERM || signo == SIGINT || signo == SIGKILL)
|
||||
g_pCompositor->cleanup();
|
||||
g_pCompositor->stopCompositor();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -193,7 +194,8 @@ CCompositor::CCompositor() {
|
|||
}
|
||||
|
||||
CCompositor::~CCompositor() {
|
||||
cleanup();
|
||||
if (!m_bIsShuttingDown)
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void CCompositor::setRandomSplash() {
|
||||
|
@ -207,7 +209,7 @@ void CCompositor::setRandomSplash() {
|
|||
static std::vector<SP<Aquamarine::IOutput>> pendingOutputs;
|
||||
|
||||
//
|
||||
void CCompositor::initServer() {
|
||||
void CCompositor::initServer(std::string socketName, int socketFd) {
|
||||
|
||||
m_sWLDisplay = wl_display_create();
|
||||
|
||||
|
@ -246,7 +248,7 @@ void CCompositor::initServer() {
|
|||
|
||||
if (!m_pAqBackend) {
|
||||
Debug::log(CRIT,
|
||||
"m_pAqBackend was null! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a Wayland "
|
||||
"m_pAqBackend was null! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a Wayland "
|
||||
"session, NOT an X11 one.");
|
||||
throwError("CBackend::create() failed!");
|
||||
}
|
||||
|
@ -257,7 +259,7 @@ void CCompositor::initServer() {
|
|||
|
||||
if (!m_pAqBackend->start()) {
|
||||
Debug::log(CRIT,
|
||||
"m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a "
|
||||
"m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a "
|
||||
"Wayland session, NOT an X11 one.");
|
||||
throwError("CBackend::create() failed!");
|
||||
}
|
||||
|
@ -267,16 +269,25 @@ void CCompositor::initServer() {
|
|||
m_iDRMFD = m_pAqBackend->drmFD();
|
||||
Debug::log(LOG, "Running on DRMFD: {}", m_iDRMFD);
|
||||
|
||||
// get socket, avoid using 0
|
||||
for (int candidate = 1; candidate <= 32; candidate++) {
|
||||
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
|
||||
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
|
||||
if (!socketName.empty() && socketFd != -1) {
|
||||
fcntl(socketFd, F_SETFD, FD_CLOEXEC);
|
||||
const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd);
|
||||
if (RETVAL >= 0) {
|
||||
m_szWLDisplaySocket = CANDIDATESTR;
|
||||
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
|
||||
break;
|
||||
} else {
|
||||
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
|
||||
m_szWLDisplaySocket = socketName;
|
||||
Debug::log(LOG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL);
|
||||
} else
|
||||
Debug::log(WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL);
|
||||
} else {
|
||||
// get socket, avoid using 0
|
||||
for (int candidate = 1; candidate <= 32; candidate++) {
|
||||
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
|
||||
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
|
||||
if (RETVAL >= 0) {
|
||||
m_szWLDisplaySocket = CANDIDATESTR;
|
||||
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
|
||||
break;
|
||||
} else
|
||||
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +303,6 @@ void CCompositor::initServer() {
|
|||
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
|
||||
}
|
||||
|
||||
Debug::log(LOG, "Setting WAYLAND_DISPLAY to {}", m_szWLDisplaySocket);
|
||||
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
|
||||
setenv("XDG_SESSION_TYPE", "wayland", 1);
|
||||
|
||||
|
@ -424,8 +434,16 @@ void CCompositor::cleanEnvironment() {
|
|||
}
|
||||
}
|
||||
|
||||
void CCompositor::stopCompositor() {
|
||||
Debug::log(LOG, "Hyprland is stopping!");
|
||||
|
||||
// this stops the wayland loop, wl_display_run
|
||||
wl_display_terminate(m_sWLDisplay);
|
||||
m_bIsShuttingDown = true;
|
||||
}
|
||||
|
||||
void CCompositor::cleanup() {
|
||||
if (!m_sWLDisplay || m_bIsShuttingDown)
|
||||
if (!m_sWLDisplay)
|
||||
return;
|
||||
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
|
@ -499,13 +517,10 @@ void CCompositor::cleanup() {
|
|||
if (m_critSigSource)
|
||||
wl_event_source_remove(m_critSigSource);
|
||||
|
||||
wl_event_loop_destroy(m_sWLEventLoop);
|
||||
wl_display_terminate(m_sWLDisplay);
|
||||
m_sWLDisplay = nullptr;
|
||||
// this frees all wayland resources, including sockets
|
||||
wl_display_destroy(m_sWLDisplay);
|
||||
|
||||
std::string waylandSocket = std::string{getenv("XDG_RUNTIME_DIR")} + "/" + m_szWLDisplaySocket;
|
||||
std::filesystem::remove(waylandSocket);
|
||||
std::filesystem::remove(waylandSocket + ".lock");
|
||||
Debug::close();
|
||||
}
|
||||
|
||||
void CCompositor::initManagers(eManagersInitStage stage) {
|
||||
|
@ -631,44 +646,7 @@ void CCompositor::prepareFallbackOutput() {
|
|||
headless->createOutput();
|
||||
}
|
||||
|
||||
void CCompositor::startCompositor(std::string socketName, int socketFd) {
|
||||
if (!socketName.empty() && socketFd != -1) {
|
||||
fcntl(socketFd, F_SETFD, FD_CLOEXEC);
|
||||
const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd);
|
||||
if (RETVAL >= 0) {
|
||||
m_szWLDisplaySocket = socketName;
|
||||
Debug::log(LOG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL);
|
||||
} else
|
||||
Debug::log(WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL);
|
||||
} else {
|
||||
// get socket, avoid using 0
|
||||
for (int candidate = 1; candidate <= 32; candidate++) {
|
||||
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
|
||||
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
|
||||
if (RETVAL >= 0) {
|
||||
m_szWLDisplaySocket = CANDIDATESTR;
|
||||
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
|
||||
break;
|
||||
} else
|
||||
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_szWLDisplaySocket.empty()) {
|
||||
Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto");
|
||||
const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay);
|
||||
if (SOCKETSTR)
|
||||
m_szWLDisplaySocket = SOCKETSTR;
|
||||
}
|
||||
|
||||
if (m_szWLDisplaySocket.empty()) {
|
||||
Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
|
||||
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
|
||||
}
|
||||
|
||||
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
|
||||
setenv("XDG_SESSION_TYPE", "wayland", 1);
|
||||
|
||||
void CCompositor::startCompositor() {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (m_pAqBackend->hasSession() /* Session-less Hyprland usually means a nest, don't update the env in that case */) {
|
||||
|
@ -1129,7 +1107,7 @@ void CCompositor::focusSurface(SP<CWLSurfaceResource> pSurface, PHLWINDOW pWindo
|
|||
return;
|
||||
|
||||
if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(pSurface)) {
|
||||
Debug::log(LOG, "surface {:x} won't receive kb focus becuase grab rejected it", (uintptr_t)pSurface);
|
||||
Debug::log(LOG, "surface {:x} won't receive kb focus becuase grab rejected it", (uintptr_t)pSurface.get());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1152,9 +1130,9 @@ void CCompositor::focusSurface(SP<CWLSurfaceResource> pSurface, PHLWINDOW pWindo
|
|||
g_pSeatManager->setKeyboardFocus(pSurface);
|
||||
|
||||
if (pWindowOwner)
|
||||
Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface, pWindowOwner);
|
||||
Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface.get(), pWindowOwner);
|
||||
else
|
||||
Debug::log(LOG, "Set keyboard focus to surface {:x}", (uintptr_t)pSurface);
|
||||
Debug::log(LOG, "Set keyboard focus to surface {:x}", (uintptr_t)pSurface.get());
|
||||
|
||||
g_pXWaylandManager->activateSurface(pSurface, true);
|
||||
m_pLastFocus = pSurface;
|
||||
|
@ -1213,15 +1191,10 @@ SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, st
|
|||
}
|
||||
|
||||
PHLWINDOW CCompositor::getWindowFromSurface(SP<CWLSurfaceResource> pSurface) {
|
||||
for (auto& w : m_vWindows) {
|
||||
if (!w->m_bIsMapped || w->m_bFadingOut)
|
||||
continue;
|
||||
if (!pSurface || !pSurface->hlSurface)
|
||||
return nullptr;
|
||||
|
||||
if (w->m_pWLSurface->resource() == pSurface)
|
||||
return w;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return pSurface->hlSurface->getWindow();
|
||||
}
|
||||
|
||||
PHLWINDOW CCompositor::getWindowFromHandle(uint32_t handle) {
|
||||
|
@ -1236,7 +1209,7 @@ PHLWINDOW CCompositor::getWindowFromHandle(uint32_t handle) {
|
|||
|
||||
PHLWINDOW CCompositor::getFullscreenWindowOnWorkspace(const int& ID) {
|
||||
for (auto& w : m_vWindows) {
|
||||
if (w->workspaceID() == ID && w->m_bIsFullscreen)
|
||||
if (w->workspaceID() == ID && w->isFullscreen())
|
||||
return w;
|
||||
}
|
||||
|
||||
|
@ -1524,7 +1497,7 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
|
|||
if (!PMONITOR)
|
||||
return nullptr; // ??
|
||||
|
||||
const auto WINDOWIDEALBB = pWindow->m_bIsFullscreen ? CBox{PMONITOR->vecPosition, PMONITOR->vecSize} : pWindow->getWindowIdealBoundingBoxIgnoreReserved();
|
||||
const auto WINDOWIDEALBB = pWindow->isFullscreen() ? CBox{PMONITOR->vecPosition, PMONITOR->vecSize} : pWindow->getWindowIdealBoundingBoxIgnoreReserved();
|
||||
|
||||
const auto POSA = Vector2D(WINDOWIDEALBB.x, WINDOWIDEALBB.y);
|
||||
const auto SIZEA = Vector2D(WINDOWIDEALBB.width, WINDOWIDEALBB.height);
|
||||
|
@ -1537,13 +1510,13 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
|
|||
|
||||
// for tiled windows, we calc edges
|
||||
for (auto& w : m_vWindows) {
|
||||
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->m_bIsFullscreen && w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
|
||||
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->isFullscreen() && w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
|
||||
continue;
|
||||
|
||||
if (pWindow->m_iMonitorID == w->m_iMonitorID && pWindow->m_pWorkspace != w->m_pWorkspace)
|
||||
continue;
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen)
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !w->isFullscreen() && !w->m_bCreatedOverFullscreen)
|
||||
continue;
|
||||
|
||||
if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID)
|
||||
|
@ -1629,13 +1602,13 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
|
|||
constexpr float THRESHOLD = 0.3 * M_PI;
|
||||
|
||||
for (auto& w : m_vWindows) {
|
||||
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->m_bIsFullscreen && !w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
|
||||
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->isFullscreen() && !w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
|
||||
continue;
|
||||
|
||||
if (pWindow->m_iMonitorID == w->m_iMonitorID && pWindow->m_pWorkspace != w->m_pWorkspace)
|
||||
continue;
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen)
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !w->isFullscreen() && !w->m_bCreatedOverFullscreen)
|
||||
continue;
|
||||
|
||||
if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID)
|
||||
|
@ -1917,7 +1890,7 @@ void CCompositor::updateWindowAnimatedDecorationValues(PHLWINDOW pWindow) {
|
|||
|
||||
// opacity
|
||||
const auto PWORKSPACE = pWindow->m_pWorkspace;
|
||||
if (pWindow->m_bIsFullscreen && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
|
||||
if (pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) {
|
||||
pWindow->m_fActiveInactiveAlpha = pWindow->m_sWindowData.alphaFullscreen.valueOrDefault().applyAlpha(*PFULLSCREENALPHA);
|
||||
} else {
|
||||
if (pWindow == m_pLastWindow)
|
||||
|
@ -1987,7 +1960,7 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
|
|||
if (w->m_bIsFloating)
|
||||
w->m_vRealPosition = w->m_vRealPosition.goal() - pMonitorA->vecPosition + pMonitorB->vecPosition;
|
||||
|
||||
if (w->m_bIsFullscreen) {
|
||||
if (w->isFullscreen()) {
|
||||
w->m_vRealPosition = pMonitorB->vecPosition;
|
||||
w->m_vRealSize = pMonitorB->vecSize;
|
||||
}
|
||||
|
@ -2012,7 +1985,7 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
|
|||
if (w->m_bIsFloating)
|
||||
w->m_vRealPosition = w->m_vRealPosition.goal() - pMonitorB->vecPosition + pMonitorA->vecPosition;
|
||||
|
||||
if (w->m_bIsFullscreen) {
|
||||
if (w->isFullscreen()) {
|
||||
w->m_vRealPosition = pMonitorA->vecPosition;
|
||||
w->m_vRealSize = pMonitorA->vecSize;
|
||||
}
|
||||
|
@ -2189,7 +2162,7 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, CMonitor* pMon
|
|||
if (w->m_bIsFloating)
|
||||
w->m_vRealPosition = w->m_vRealPosition.goal() - POLDMON->vecPosition + pMonitor->vecPosition;
|
||||
|
||||
if (w->m_bIsFullscreen) {
|
||||
if (w->isFullscreen()) {
|
||||
w->m_vRealPosition = pMonitor->vecPosition;
|
||||
w->m_vRealSize = pMonitor->vecSize;
|
||||
}
|
||||
|
@ -2264,12 +2237,12 @@ void CCompositor::updateFullscreenFadeOnWorkspace(PHLWORKSPACE pWorkspace) {
|
|||
for (auto& w : g_pCompositor->m_vWindows) {
|
||||
if (w->m_pWorkspace == pWorkspace) {
|
||||
|
||||
if (w->m_bFadingOut || w->m_bPinned || w->m_bIsFullscreen)
|
||||
if (w->m_bFadingOut || w->m_bPinned || w->isFullscreen())
|
||||
continue;
|
||||
|
||||
if (!FULLSCREEN)
|
||||
w->m_fAlpha = 1.f;
|
||||
else if (!w->m_bIsFullscreen)
|
||||
else if (!w->isFullscreen())
|
||||
w->m_fAlpha = !w->m_bCreatedOverFullscreen ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
|
@ -2279,52 +2252,87 @@ void CCompositor::updateFullscreenFadeOnWorkspace(PHLWORKSPACE pWorkspace) {
|
|||
if (pWorkspace->m_iID == PMONITOR->activeWorkspaceID() || pWorkspace->m_iID == PMONITOR->activeSpecialWorkspaceID()) {
|
||||
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
|
||||
if (!ls->fadingOut)
|
||||
ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL ? 0.f : 1.f;
|
||||
ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMode mode) {
|
||||
if (!validMapped(pWindow) || g_pCompositor->m_bUnsafeState)
|
||||
void CCompositor::changeWindowFullscreenModeInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON) {
|
||||
setWindowFullscreenInternal(
|
||||
PWINDOW, (eFullscreenMode)(ON ? (uint8_t)PWINDOW->m_sFullscreenState.internal | (uint8_t)MODE : ((uint8_t)PWINDOW->m_sFullscreenState.internal & (uint8_t)~MODE)));
|
||||
}
|
||||
|
||||
void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON) {
|
||||
setWindowFullscreenClient(PWINDOW,
|
||||
(eFullscreenMode)(ON ? (uint8_t)PWINDOW->m_sFullscreenState.client | (uint8_t)MODE : ((uint8_t)PWINDOW->m_sFullscreenState.client & (uint8_t)~MODE)));
|
||||
}
|
||||
|
||||
void CCompositor::setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
|
||||
if (PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
|
||||
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = MODE, .client = MODE});
|
||||
else
|
||||
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = MODE, .client = PWINDOW->m_sFullscreenState.client});
|
||||
}
|
||||
|
||||
void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
|
||||
if (PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
|
||||
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = MODE, .client = MODE});
|
||||
else
|
||||
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = PWINDOW->m_sFullscreenState.internal, .client = MODE});
|
||||
}
|
||||
|
||||
void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, sFullscreenState state) {
|
||||
static auto PNODIRECTSCANOUT = CConfigValue<Hyprlang::INT>("misc:no_direct_scanout");
|
||||
|
||||
if (!validMapped(PWINDOW) || g_pCompositor->m_bUnsafeState)
|
||||
return;
|
||||
|
||||
if (pWindow->m_bPinned) {
|
||||
Debug::log(LOG, "Pinned windows cannot be fullscreen'd");
|
||||
state.internal = std::clamp(state.internal, (eFullscreenMode)0, FSMODE_MAX);
|
||||
state.client = std::clamp(state.client, (eFullscreenMode)0, FSMODE_MAX);
|
||||
|
||||
const auto PMONITOR = getMonitorFromID(PWINDOW->m_iMonitorID);
|
||||
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
|
||||
|
||||
const eFullscreenMode CURRENT_EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)PWINDOW->m_sFullscreenState.internal);
|
||||
const eFullscreenMode EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)state.internal);
|
||||
|
||||
const bool CHANGEINTERNAL = !(PWINDOW->m_bPinned || CURRENT_EFFECTIVE_MODE == EFFECTIVE_MODE || (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen()));
|
||||
|
||||
// TODO: update the state on syncFullscreen changes
|
||||
if (!CHANGEINTERNAL && PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
|
||||
return;
|
||||
}
|
||||
|
||||
if (pWindow->m_bIsFullscreen == on) {
|
||||
Debug::log(LOG, "Window is already in the required fullscreen state");
|
||||
PWINDOW->m_sFullscreenState.client = state.client;
|
||||
g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN);
|
||||
|
||||
if (!CHANGEINTERNAL)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PMONITOR = getMonitorFromID(pWindow->m_iMonitorID);
|
||||
g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(PWINDOW, CURRENT_EFFECTIVE_MODE, EFFECTIVE_MODE);
|
||||
|
||||
const auto PWORKSPACE = pWindow->m_pWorkspace;
|
||||
PWINDOW->m_sFullscreenState.internal = state.internal;
|
||||
PWORKSPACE->m_efFullscreenMode = EFFECTIVE_MODE;
|
||||
PWORKSPACE->m_bHasFullscreenWindow = EFFECTIVE_MODE != FSMODE_NONE;
|
||||
|
||||
const auto MODE = mode == FULLSCREEN_INVALID ? PWORKSPACE->m_efFullscreenMode : mode;
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)EFFECTIVE_MODE != FSMODE_NONE)});
|
||||
EMIT_HOOK_EVENT("fullscreen", PWINDOW);
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
|
||||
Debug::log(LOG, "Rejecting fullscreen ON on a fullscreen workspace");
|
||||
return;
|
||||
}
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(pWindow, MODE, on);
|
||||
|
||||
g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->shouldSendFullscreenState());
|
||||
|
||||
updateWindowAnimatedDecorationValues(pWindow);
|
||||
PWINDOW->updateDynamicRules();
|
||||
PWINDOW->updateWindowDecos();
|
||||
updateWindowAnimatedDecorationValues(PWINDOW);
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->m_iMonitorID);
|
||||
|
||||
// make all windows on the same workspace under the fullscreen window
|
||||
for (auto& w : m_vWindows) {
|
||||
if (w->m_pWorkspace == PWORKSPACE && !w->m_bIsFullscreen && !w->m_bFadingOut && !w->m_bPinned)
|
||||
if (w->m_pWorkspace == PWORKSPACE && !w->isFullscreen() && !w->m_bFadingOut && !w->m_bPinned)
|
||||
w->m_bCreatedOverFullscreen = false;
|
||||
}
|
||||
|
||||
updateFullscreenFadeOnWorkspace(PWORKSPACE);
|
||||
|
||||
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goal(), true);
|
||||
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal(), true);
|
||||
|
||||
forceReportSizesToWindowsOnWorkspace(pWindow->workspaceID());
|
||||
forceReportSizesToWindowsOnWorkspace(PWINDOW->workspaceID());
|
||||
|
||||
g_pInputManager->recheckIdleInhibitorStatus();
|
||||
|
||||
|
@ -2333,7 +2341,9 @@ void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMod
|
|||
return;
|
||||
|
||||
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
|
||||
g_pHyprRenderer->setSurfaceScanoutMode(pWindow->m_pWLSurface->resource(), on ? PMONITOR->self.lock() : nullptr);
|
||||
// ignore if DS is disabled.
|
||||
if (!*PNODIRECTSCANOUT)
|
||||
g_pHyprRenderer->setSurfaceScanoutMode(PWINDOW->m_pWLSurface->resource(), EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->self.lock() : nullptr);
|
||||
|
||||
g_pConfigManager->ensureVRR(PMONITOR);
|
||||
}
|
||||
|
@ -2665,11 +2675,11 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
|
|||
if (pWindow->m_bPinned && pWorkspace->m_bIsSpecialWorkspace)
|
||||
return;
|
||||
|
||||
const bool FULLSCREEN = pWindow->m_bIsFullscreen;
|
||||
const auto FULLSCREENMODE = pWindow->m_pWorkspace->m_efFullscreenMode;
|
||||
const bool FULLSCREEN = pWindow->isFullscreen();
|
||||
const auto FULLSCREENMODE = pWindow->m_sFullscreenState.internal;
|
||||
|
||||
if (FULLSCREEN)
|
||||
setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
|
||||
setWindowFullscreenInternal(pWindow, FSMODE_NONE);
|
||||
|
||||
if (!pWindow->m_bIsFloating) {
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemovedTiling(pWindow);
|
||||
|
@ -2702,7 +2712,7 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
|
|||
}
|
||||
|
||||
if (FULLSCREEN)
|
||||
setWindowFullscreen(pWindow, true, FULLSCREENMODE);
|
||||
setWindowFullscreenInternal(pWindow, FULLSCREENMODE);
|
||||
|
||||
g_pCompositor->updateWorkspaceWindows(pWorkspace->m_iID);
|
||||
g_pCompositor->updateWorkspaceWindows(pWindow->workspaceID());
|
||||
|
@ -2861,7 +2871,7 @@ void CCompositor::setPreferredScaleForSurface(SP<CWLSurfaceResource> pSurface, d
|
|||
|
||||
const auto PSURFACE = CWLSurface::fromResource(pSurface);
|
||||
if (!PSURFACE) {
|
||||
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface);
|
||||
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface.get());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2874,7 +2884,7 @@ void CCompositor::setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurfac
|
|||
|
||||
const auto PSURFACE = CWLSurface::fromResource(pSurface);
|
||||
if (!PSURFACE) {
|
||||
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface);
|
||||
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface.get());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,8 +69,9 @@ class CCompositor {
|
|||
|
||||
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap;
|
||||
|
||||
void initServer();
|
||||
void startCompositor(std::string socketName, int socketFd);
|
||||
void initServer(std::string socketName, int socketFd);
|
||||
void startCompositor();
|
||||
void stopCompositor();
|
||||
void cleanup();
|
||||
void createLockFile();
|
||||
void removeLockFile();
|
||||
|
@ -145,7 +146,11 @@ class CCompositor {
|
|||
void swapActiveWorkspaces(CMonitor*, CMonitor*);
|
||||
CMonitor* getMonitorFromString(const std::string&);
|
||||
bool workspaceIDOutOfBounds(const int64_t&);
|
||||
void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID);
|
||||
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const sFullscreenState state);
|
||||
void changeWindowFullscreenModeInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
|
||||
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
|
||||
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
|
||||
PHLWINDOW getX11Parent(PHLWINDOW);
|
||||
void scheduleFrameForMonitor(CMonitor*, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
|
||||
|
|
|
@ -669,7 +669,7 @@ std::string CConfigManager::getConfigDir() {
|
|||
Debug::log(WARN, "Creating config home directory");
|
||||
try {
|
||||
std::filesystem::create_directories(parentPath);
|
||||
} catch (std::exception e) { throw e; }
|
||||
} catch (std::exception& e) { throw e; }
|
||||
}
|
||||
|
||||
Debug::log(WARN, "No config file found; attempting to generate.");
|
||||
|
@ -1141,7 +1141,7 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
|
|||
|
||||
// Since some rules will be applied later, we need to store some flags
|
||||
bool hasFloating = pWindow->m_bIsFloating;
|
||||
bool hasFullscreen = pWindow->m_bIsFullscreen;
|
||||
bool hasFullscreen = pWindow->isFullscreen();
|
||||
|
||||
// Local tags for dynamic tag rule match
|
||||
auto tags = pWindow->m_tags;
|
||||
|
@ -1340,12 +1340,14 @@ void CConfigManager::dispatchExecOnce() {
|
|||
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS");
|
||||
|
||||
firstExecDispatched = true;
|
||||
isLaunchingExecOnce = true;
|
||||
|
||||
for (auto& c : firstExecRequests) {
|
||||
handleRawExec("", c);
|
||||
}
|
||||
|
||||
firstExecRequests.clear(); // free some kb of memory :P
|
||||
isLaunchingExecOnce = false;
|
||||
|
||||
// set input, fixes some certain issues
|
||||
g_pInputManager->setKeyboardLayout();
|
||||
|
@ -1487,7 +1489,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
|
|||
if (!PWORKSPACE)
|
||||
return; // ???
|
||||
|
||||
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
|
||||
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
|
||||
|
||||
if (WORKSPACEFULL) {
|
||||
m->output->state->setAdaptiveSync(true);
|
||||
|
@ -2024,6 +2026,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||
bool ignoreMods = false;
|
||||
bool multiKey = false;
|
||||
bool hasDescription = false;
|
||||
bool dontInhibit = false;
|
||||
const auto BINDARGS = command.substr(4);
|
||||
|
||||
for (auto& arg : BINDARGS) {
|
||||
|
@ -2045,6 +2048,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||
multiKey = true;
|
||||
} else if (arg == 'd') {
|
||||
hasDescription = true;
|
||||
} else if (arg == 'p') {
|
||||
dontInhibit = true;
|
||||
} else {
|
||||
return "bind: invalid flag";
|
||||
}
|
||||
|
@ -2113,8 +2118,9 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||
return "Invalid catchall, catchall keybinds are only allowed in submaps.";
|
||||
}
|
||||
|
||||
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, DESCRIPTION,
|
||||
release, repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription});
|
||||
g_pKeybindManager->addKeybind(SKeybind{
|
||||
parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, DESCRIPTION, release,
|
||||
repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit});
|
||||
}
|
||||
|
||||
return {};
|
||||
|
@ -2134,11 +2140,11 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma
|
|||
|
||||
bool windowRuleValid(const std::string& RULE) {
|
||||
static const auto rules = std::unordered_set<std::string>{
|
||||
"fakefullscreen", "float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile",
|
||||
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile",
|
||||
};
|
||||
static const auto rulesPrefix = std::vector<std::string>{
|
||||
"animation", "bordercolor", "bordersize", "center", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move",
|
||||
"opacity", "plugin:", "pseudo", "rounding", "size", "suppressevent", "tag", "workspace", "xray",
|
||||
"animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor",
|
||||
"move", "opacity", "plugin:", "pseudo", "rounding", "size", "suppressevent", "tag", "workspace", "xray",
|
||||
};
|
||||
|
||||
const auto VALS = CVarList(RULE, 2, ' ');
|
||||
|
|
|
@ -158,9 +158,6 @@ class CConfigManager {
|
|||
void performMonitorReload();
|
||||
void appendMonitorRule(const SMonitorRule&);
|
||||
bool replaceMonitorRule(const SMonitorRule&);
|
||||
bool m_bWantsMonitorReload = false;
|
||||
bool m_bForceReload = false;
|
||||
bool m_bNoMonitorReload = false;
|
||||
void ensureMonitorStatus();
|
||||
void ensureVRR(CMonitor* pMonitor = nullptr);
|
||||
|
||||
|
@ -212,8 +209,10 @@ class CConfigManager {
|
|||
{"nomaxsize", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noMaxSize; }},
|
||||
{"norounding", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noRounding; }},
|
||||
{"noshadow", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noShadow; }},
|
||||
{"noshortcutsinhibit", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }},
|
||||
{"opaque", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.opaque; }},
|
||||
{"forcergbx", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.RGBX; }},
|
||||
{"syncfullscreen", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }},
|
||||
{"immediate", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.tearing; }},
|
||||
{"xray", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.xray; }},
|
||||
};
|
||||
|
@ -222,6 +221,12 @@ class CConfigManager {
|
|||
{"rounding", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.rounding; }}, {"bordersize", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.borderSize; }}};
|
||||
|
||||
|
||||
bool m_bWantsMonitorReload = false;
|
||||
bool m_bForceReload = false;
|
||||
bool m_bNoMonitorReload = false;
|
||||
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<Hyprlang::CConfig> m_pConfig;
|
||||
|
||||
|
|
|
@ -105,7 +105,19 @@ void CrashReporter::createAndSaveCrash(int sig) {
|
|||
finalCrashReport += GIT_COMMIT_HASH;
|
||||
finalCrashReport += "\nTag: ";
|
||||
finalCrashReport += GIT_TAG;
|
||||
finalCrashReport += "\n\n";
|
||||
finalCrashReport += "\nDate: ";
|
||||
finalCrashReport += GIT_COMMIT_DATE;
|
||||
finalCrashReport += "\nFlags:\n";
|
||||
#ifdef LEGACY_RENDERER
|
||||
finalCrashReport += "legacyrenderer\n";
|
||||
#endif
|
||||
#ifndef ISDEBUG
|
||||
finalCrashReport += "debug\n";
|
||||
#endif
|
||||
#ifdef NO_XWAYLAND
|
||||
finalCrashReport += "no xwayland\n";
|
||||
#endif
|
||||
finalCrashReport += "\n";
|
||||
|
||||
if (g_pPluginSystem && g_pPluginSystem->pluginCount() > 0) {
|
||||
finalCrashReport += "Hyprland seems to be running with plugins. This crash might not be Hyprland's fault.\nPlugins:\n";
|
||||
|
|
|
@ -32,6 +32,7 @@ using namespace Hyprutils::String;
|
|||
#include "../devices/IKeyboard.hpp"
|
||||
#include "../devices/ITouch.hpp"
|
||||
#include "../devices/Tablet.hpp"
|
||||
#include "../protocols/GlobalShortcuts.hpp"
|
||||
#include "debug/RollingLogFollow.hpp"
|
||||
#include "config/ConfigManager.hpp"
|
||||
#include "helpers/MiscFunctions.hpp"
|
||||
|
@ -239,8 +240,7 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
"xwayland": {},
|
||||
"pinned": {},
|
||||
"fullscreen": {},
|
||||
"fullscreenMode": {},
|
||||
"fakeFullscreen": {},
|
||||
"fullscreenClient": {},
|
||||
"grouped": [{}],
|
||||
"tags": [{}],
|
||||
"swallowing": "0x{:x}",
|
||||
|
@ -251,19 +251,19 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (w->m_bIsPseudotiled ? "true" : "false"),
|
||||
(int64_t)w->m_iMonitorID, escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass),
|
||||
escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"),
|
||||
(w->m_bIsFullscreen ? "true" : "false"), (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0),
|
||||
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
|
||||
(uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
|
||||
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
|
||||
} else {
|
||||
return std::format(
|
||||
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
|
||||
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
|
||||
"{}\n\txwayland: {}\n\tpinned: "
|
||||
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
|
||||
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
|
||||
(uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y,
|
||||
(int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName),
|
||||
(int)w->m_bIsFloating, (int)w->m_bIsPseudotiled, (int64_t)w->m_iMonitorID, w->m_szClass, w->m_szTitle, w->m_szInitialClass, w->m_szInitialTitle, w->getPID(),
|
||||
(int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, (w->m_bIsFullscreen ? (w->m_pWorkspace ? w->m_pWorkspace->m_efFullscreenMode : 0) : 0),
|
||||
(int)w->m_bFakeFullscreenState, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
|
||||
(int)w->m_bIsX11, (int)w->m_bPinned, (uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
|
||||
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -776,7 +776,7 @@ std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request)
|
|||
|
||||
std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||
std::string ret = "";
|
||||
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
|
||||
const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
|
||||
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
|
||||
for (auto& sh : SHORTCUTS)
|
||||
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
|
||||
|
@ -1604,6 +1604,10 @@ CHyprCtl::CHyprCtl() {
|
|||
CHyprCtl::~CHyprCtl() {
|
||||
if (m_eventSource)
|
||||
wl_event_source_remove(m_eventSource);
|
||||
if (m_iSocketFD >= 0)
|
||||
close(m_iSocketFD);
|
||||
if (!m_socketPath.empty())
|
||||
unlink(m_socketPath.c_str());
|
||||
}
|
||||
|
||||
SP<SHyprCtlCommand> CHyprCtl::registerCommand(SHyprCtlCommand cmd) {
|
||||
|
@ -1821,9 +1825,9 @@ void CHyprCtl::startHyprCtlSocket() {
|
|||
|
||||
sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX};
|
||||
|
||||
std::string socketPath = g_pCompositor->m_szInstancePath + "/.socket.sock";
|
||||
m_socketPath = g_pCompositor->m_szInstancePath + "/.socket.sock";
|
||||
|
||||
strcpy(SERVERADDRESS.sun_path, socketPath.c_str());
|
||||
strcpy(SERVERADDRESS.sun_path, m_socketPath.c_str());
|
||||
|
||||
if (bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
|
||||
Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
|
||||
|
@ -1833,7 +1837,7 @@ void CHyprCtl::startHyprCtlSocket() {
|
|||
// 10 max queued.
|
||||
listen(m_iSocketFD, 10);
|
||||
|
||||
Debug::log(LOG, "Hypr socket started at {}", socketPath);
|
||||
Debug::log(LOG, "Hypr socket started at {}", m_socketPath);
|
||||
|
||||
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ class CHyprCtl {
|
|||
|
||||
std::vector<SP<SHyprCtlCommand>> m_vCommands;
|
||||
wl_event_source* m_eventSource = nullptr;
|
||||
std::string m_socketPath;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHyprCtl> g_pHyprCtl;
|
||||
|
|
|
@ -8,6 +8,11 @@
|
|||
|
||||
void Debug::init(const std::string& IS) {
|
||||
logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
|
||||
logOfs.open(logFile, std::ios::out | std::ios::app);
|
||||
}
|
||||
|
||||
void Debug::close() {
|
||||
logOfs.close();
|
||||
}
|
||||
|
||||
void Debug::log(LogLevel level, std::string str) {
|
||||
|
@ -55,11 +60,8 @@ void Debug::log(LogLevel level, std::string str) {
|
|||
|
||||
if (!disableLogs || !**disableLogs) {
|
||||
// log to a file
|
||||
std::ofstream ofs;
|
||||
ofs.open(logFile, std::ios::out | std::ios::app);
|
||||
ofs << str << "\n";
|
||||
|
||||
ofs.close();
|
||||
logOfs << str << "\n";
|
||||
logOfs.flush();
|
||||
}
|
||||
|
||||
// log it to the stdout too.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include "../includes.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
|
||||
|
@ -22,6 +23,7 @@ enum LogLevel {
|
|||
|
||||
namespace Debug {
|
||||
inline std::string logFile;
|
||||
inline std::ofstream logOfs;
|
||||
inline int64_t* const* disableLogs = nullptr;
|
||||
inline int64_t* const* disableTime = nullptr;
|
||||
inline bool disableStdout = false;
|
||||
|
@ -30,14 +32,18 @@ namespace Debug {
|
|||
inline int64_t* const* coloredLogs = nullptr;
|
||||
|
||||
inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
|
||||
inline std::mutex logMutex;
|
||||
|
||||
void init(const std::string& IS);
|
||||
void close();
|
||||
|
||||
//
|
||||
void log(LogLevel level, std::string str);
|
||||
|
||||
template <typename... Args>
|
||||
void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::lock_guard<std::mutex> guard(logMutex);
|
||||
|
||||
if (level == TRACE && !trace)
|
||||
return;
|
||||
|
||||
|
@ -66,5 +72,6 @@ namespace Debug {
|
|||
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
log(level, logMsg);
|
||||
logMutex.unlock();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -74,7 +74,7 @@ CLayerSurface::~CLayerSurface() {
|
|||
}
|
||||
|
||||
void CLayerSurface::onDestroy() {
|
||||
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layerSurface);
|
||||
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layerSurface.get());
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
|
||||
|
||||
|
@ -111,14 +111,21 @@ void CLayerSurface::onDestroy() {
|
|||
layerSurface.reset();
|
||||
if (surface)
|
||||
surface->unassign();
|
||||
|
||||
listeners.unmap.reset();
|
||||
listeners.destroy.reset();
|
||||
listeners.map.reset();
|
||||
listeners.commit.reset();
|
||||
}
|
||||
|
||||
void CLayerSurface::onMap() {
|
||||
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface);
|
||||
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface.get());
|
||||
|
||||
mapped = true;
|
||||
interactivity = layerSurface->current.interactivity;
|
||||
|
||||
layerSurface->surface->map();
|
||||
|
||||
// this layer might be re-mapped.
|
||||
fadingOut = false;
|
||||
g_pCompositor->removeFromFadingOutSafe(self.lock());
|
||||
|
@ -163,7 +170,7 @@ void CLayerSurface::onMap() {
|
|||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(&geomFixed);
|
||||
const auto WORKSPACE = PMONITOR->activeWorkspace;
|
||||
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
|
||||
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FSMODE_FULLSCREEN;
|
||||
|
||||
startAnimation(!(layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
|
||||
readyToDelete = false;
|
||||
|
@ -177,7 +184,7 @@ void CLayerSurface::onMap() {
|
|||
}
|
||||
|
||||
void CLayerSurface::onUnmap() {
|
||||
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)layerSurface);
|
||||
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)layerSurface.get());
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", layerSurface->layerNamespace});
|
||||
EMIT_HOOK_EVENT("closeLayer", self.lock());
|
||||
|
@ -190,6 +197,8 @@ void CLayerSurface::onUnmap() {
|
|||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
|
||||
mapped = false;
|
||||
if (layerSurface && layerSurface->surface)
|
||||
layerSurface->surface->unmap();
|
||||
|
||||
startAnimation(false);
|
||||
return;
|
||||
|
@ -201,6 +210,8 @@ void CLayerSurface::onUnmap() {
|
|||
startAnimation(false);
|
||||
|
||||
mapped = false;
|
||||
if (layerSurface && layerSurface->surface)
|
||||
layerSurface->surface->unmap();
|
||||
|
||||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
|
||||
|
@ -212,8 +223,11 @@ void CLayerSurface::onUnmap() {
|
|||
return;
|
||||
|
||||
// refocus if needed
|
||||
if (WASLASTFOCUS)
|
||||
// vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window
|
||||
if (WASLASTFOCUS || (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus->hlSurface && !g_pCompositor->m_pLastFocus->hlSurface->keyboardFocusable()))
|
||||
g_pInputManager->refocusLastWindow(PMONITOR);
|
||||
else if (g_pCompositor->m_pLastFocus)
|
||||
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_pLastFocus.lock());
|
||||
|
||||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(&geomFixed);
|
||||
|
@ -233,7 +247,7 @@ void CLayerSurface::onCommit() {
|
|||
|
||||
if (!mapped) {
|
||||
// we're re-mapping if this is the case
|
||||
if (layerSurface->surface && !layerSurface->surface->current.buffer) {
|
||||
if (layerSurface->surface && !layerSurface->surface->current.texture) {
|
||||
fadingOut = false;
|
||||
geometry = {};
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(monitorID);
|
||||
|
|
|
@ -22,7 +22,7 @@ CPopup::CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner) : m_pParent(pOwner),
|
|||
m_pWindowOwner = pOwner->m_pWindowOwner;
|
||||
|
||||
m_vLastSize = popup->surface->current.geometry.size();
|
||||
unconstrain();
|
||||
reposition();
|
||||
|
||||
initAllSignals();
|
||||
}
|
||||
|
@ -188,18 +188,18 @@ void CPopup::onReposition() {
|
|||
|
||||
m_vLastPos = coordsRelativeToParent();
|
||||
|
||||
unconstrain();
|
||||
reposition();
|
||||
}
|
||||
|
||||
void CPopup::unconstrain() {
|
||||
void CPopup::reposition() {
|
||||
const auto COORDS = t1ParentCoords();
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
CBox box = {PMONITOR->vecPosition.x - COORDS.x, PMONITOR->vecPosition.y - COORDS.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
|
||||
m_pResource->applyPositioning(box, COORDS - PMONITOR->vecPosition);
|
||||
CBox box = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
|
||||
m_pResource->applyPositioning(box, COORDS);
|
||||
}
|
||||
|
||||
Vector2D CPopup::coordsRelativeToParent() {
|
||||
|
|
|
@ -74,11 +74,11 @@ class CPopup {
|
|||
} listeners;
|
||||
|
||||
void initAllSignals();
|
||||
void unconstrain();
|
||||
void reposition();
|
||||
void recheckChildrenRecursive();
|
||||
void sendScale();
|
||||
|
||||
Vector2D localToGlobal(const Vector2D& rel);
|
||||
Vector2D t1ParentCoords();
|
||||
static void bfHelper(std::vector<CPopup*> nodes, std::function<void(CPopup*, void*)> fn, void* data);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "WLSurface.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
|
||||
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface) {
|
||||
m_pResource = pSurface;
|
||||
|
@ -56,12 +57,12 @@ bool CWLSurface::small() const {
|
|||
if (!validMapped(m_pWindowOwner) || !exists())
|
||||
return false;
|
||||
|
||||
if (!m_pResource->current.buffer)
|
||||
if (!m_pResource->current.texture)
|
||||
return false;
|
||||
|
||||
const auto O = m_pWindowOwner.lock();
|
||||
|
||||
return O->m_vReportedSize.x > m_pResource->current.buffer->size.x + 1 || O->m_vReportedSize.y > m_pResource->current.buffer->size.y + 1;
|
||||
return O->m_vReportedSize.x > m_pResource->current.bufferSize.x + 1 || O->m_vReportedSize.y > m_pResource->current.bufferSize.y + 1;
|
||||
}
|
||||
|
||||
Vector2D CWLSurface::correctSmallVec() const {
|
||||
|
@ -74,33 +75,52 @@ Vector2D CWLSurface::correctSmallVec() const {
|
|||
return Vector2D{(O->m_vReportedSize.x - SIZE.x) / 2, (O->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_vRealSize.value() / O->m_vReportedSize);
|
||||
}
|
||||
|
||||
Vector2D CWLSurface::getViewporterCorrectedSize() const {
|
||||
if (!exists() || !m_pResource->current.buffer)
|
||||
Vector2D CWLSurface::correctSmallVecBuf() const {
|
||||
if (!exists() || !small() || m_bFillIgnoreSmall || !m_pResource->current.texture)
|
||||
return {};
|
||||
|
||||
return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.buffer->size;
|
||||
const auto SIZE = getViewporterCorrectedSize();
|
||||
const auto BS = m_pResource->current.bufferSize;
|
||||
|
||||
return Vector2D{(BS.x - SIZE.x) / 2, (BS.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY});
|
||||
}
|
||||
|
||||
CRegion CWLSurface::logicalDamage() const {
|
||||
if (!m_pResource->current.buffer)
|
||||
Vector2D CWLSurface::getViewporterCorrectedSize() const {
|
||||
if (!exists() || !m_pResource->current.texture)
|
||||
return {};
|
||||
|
||||
return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.bufferSize;
|
||||
}
|
||||
|
||||
CRegion CWLSurface::computeDamage() const {
|
||||
if (!m_pResource->current.texture)
|
||||
return {};
|
||||
|
||||
CRegion damage = m_pResource->accumulateCurrentBufferDamage();
|
||||
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.buffer->size.x, m_pResource->current.buffer->size.y);
|
||||
damage.scale(1.0 / m_pResource->current.scale);
|
||||
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y);
|
||||
|
||||
const auto VPSIZE = getViewporterCorrectedSize();
|
||||
const auto CORRECTVEC = correctSmallVec();
|
||||
const auto BUFSIZE = m_pResource->current.bufferSize;
|
||||
const auto CORRECTVEC = correctSmallVecBuf();
|
||||
|
||||
if (m_pResource->current.viewport.hasSource)
|
||||
damage.intersect(m_pResource->current.viewport.source);
|
||||
|
||||
const auto SCALEDSRCSIZE =
|
||||
m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.buffer->size;
|
||||
const auto SCALEDSRCSIZE = m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.bufferSize;
|
||||
|
||||
damage.scale({VPSIZE.x / SCALEDSRCSIZE.x, VPSIZE.y / SCALEDSRCSIZE.y});
|
||||
damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y});
|
||||
damage.translate(CORRECTVEC);
|
||||
|
||||
// go from buffer coords in the damage to hl logical
|
||||
|
||||
const auto BOX = getSurfaceBoxGlobal();
|
||||
const Vector2D SCALE = BOX.has_value() ? BOX->size() / m_pResource->current.bufferSize :
|
||||
Vector2D{1.0 / m_pResource->current.scale, 1.0 / m_pResource->current.scale /* Wrong... but we can't really do better */};
|
||||
|
||||
damage.scale(SCALE);
|
||||
|
||||
if (m_pWindowOwner)
|
||||
damage.scale(m_pWindowOwner->m_fX11SurfaceScaledBy); // fix xwayland:force_zero_scaling stuff that will be fucked by the above a bit
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
@ -141,27 +161,27 @@ void CWLSurface::init() {
|
|||
Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this);
|
||||
}
|
||||
|
||||
PHLWINDOW CWLSurface::getWindow() {
|
||||
PHLWINDOW CWLSurface::getWindow() const {
|
||||
return m_pWindowOwner.lock();
|
||||
}
|
||||
|
||||
PHLLS CWLSurface::getLayer() {
|
||||
PHLLS CWLSurface::getLayer() const {
|
||||
return m_pLayerOwner.lock();
|
||||
}
|
||||
|
||||
CPopup* CWLSurface::getPopup() {
|
||||
CPopup* CWLSurface::getPopup() const {
|
||||
return m_pPopupOwner;
|
||||
}
|
||||
|
||||
CSubsurface* CWLSurface::getSubsurface() {
|
||||
CSubsurface* CWLSurface::getSubsurface() const {
|
||||
return m_pSubsurfaceOwner;
|
||||
}
|
||||
|
||||
bool CWLSurface::desktopComponent() {
|
||||
bool CWLSurface::desktopComponent() const {
|
||||
return !m_pLayerOwner.expired() || !m_pWindowOwner.expired() || m_pSubsurfaceOwner || m_pPopupOwner;
|
||||
}
|
||||
|
||||
std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() {
|
||||
std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() const {
|
||||
if (!desktopComponent())
|
||||
return {};
|
||||
|
||||
|
@ -181,7 +201,7 @@ void CWLSurface::appendConstraint(WP<CPointerConstraint> constraint) {
|
|||
m_pConstraint = constraint;
|
||||
}
|
||||
|
||||
SP<CPointerConstraint> CWLSurface::constraint() {
|
||||
SP<CPointerConstraint> CWLSurface::constraint() const {
|
||||
return m_pConstraint.lock();
|
||||
}
|
||||
|
||||
|
@ -202,3 +222,11 @@ SP<CWLSurface> CWLSurface::fromResource(SP<CWLSurfaceResource> pSurface) {
|
|||
return nullptr;
|
||||
return pSurface->hlSurface.lock();
|
||||
}
|
||||
|
||||
bool CWLSurface::keyboardFocusable() const {
|
||||
if (m_pWindowOwner || m_pPopupOwner || m_pSubsurfaceOwner)
|
||||
return true;
|
||||
if (m_pLayerOwner && m_pLayerOwner->layerSurface)
|
||||
return m_pLayerOwner->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -33,22 +33,24 @@ class CWLSurface {
|
|||
|
||||
SP<CWLSurfaceResource> resource() const;
|
||||
bool exists() const;
|
||||
bool small() const; // means surface is smaller than the requested size
|
||||
Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces
|
||||
bool small() const; // means surface is smaller than the requested size
|
||||
Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces
|
||||
Vector2D correctSmallVecBuf() const; // returns a corrective vector for small() surfaces, in BL coords
|
||||
Vector2D getViewporterCorrectedSize() const;
|
||||
CRegion logicalDamage() const;
|
||||
CRegion computeDamage() const; // logical coordinates. May be wrong if the surface is unassigned
|
||||
bool visible();
|
||||
bool keyboardFocusable() const;
|
||||
|
||||
// getters for owners.
|
||||
PHLWINDOW getWindow();
|
||||
PHLLS getLayer();
|
||||
CPopup* getPopup();
|
||||
CSubsurface* getSubsurface();
|
||||
PHLWINDOW getWindow() const;
|
||||
PHLLS getLayer() const;
|
||||
CPopup* getPopup() const;
|
||||
CSubsurface* getSubsurface() const;
|
||||
|
||||
// desktop components misc utils
|
||||
std::optional<CBox> getSurfaceBoxGlobal();
|
||||
std::optional<CBox> getSurfaceBoxGlobal() const;
|
||||
void appendConstraint(WP<CPointerConstraint> constraint);
|
||||
SP<CPointerConstraint> constraint();
|
||||
SP<CPointerConstraint> constraint() const;
|
||||
|
||||
// allow stretching. Useful for plugins.
|
||||
bool m_bFillIgnoreSmall = false;
|
||||
|
@ -107,7 +109,7 @@ class CWLSurface {
|
|||
|
||||
void destroy();
|
||||
void init();
|
||||
bool desktopComponent();
|
||||
bool desktopComponent() const;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener destroy;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <any>
|
||||
#include <bit>
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
#include "Window.hpp"
|
||||
|
@ -192,7 +193,7 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
|
|||
auto POS = m_vPosition;
|
||||
auto SIZE = m_vSize;
|
||||
|
||||
if (m_bIsFullscreen) {
|
||||
if (isFullscreen()) {
|
||||
POS = PMONITOR->vecPosition;
|
||||
SIZE = PMONITOR->vecSize;
|
||||
|
||||
|
@ -477,13 +478,7 @@ void unregisterVar(void* ptr) {
|
|||
|
||||
void CWindow::onUnmap() {
|
||||
static auto PCLOSEONLASTSPECIAL = CConfigValue<Hyprlang::INT>("misc:close_special_on_empty");
|
||||
|
||||
if (g_pCompositor->m_pLastWindow.lock().get() == this)
|
||||
g_pCompositor->m_pLastWindow.reset();
|
||||
if (g_pInputManager->currentlyDraggedWindow.lock().get() == this)
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
|
||||
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
|
||||
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
|
||||
|
||||
if (!m_szInitialWorkspaceToken.empty()) {
|
||||
const auto TOKEN = g_pTokenManager->getToken(m_szInitialWorkspaceToken);
|
||||
|
@ -976,8 +971,9 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
|
|||
return;
|
||||
|
||||
const auto PCURRENT = getGroupCurrent();
|
||||
const bool FULLSCREEN = PCURRENT->m_bIsFullscreen;
|
||||
const bool FULLSCREEN = PCURRENT->isFullscreen();
|
||||
const auto WORKSPACE = PCURRENT->m_pWorkspace;
|
||||
const auto MODE = PCURRENT->m_sFullscreenState.client;
|
||||
|
||||
const auto PWINDOWSIZE = PCURRENT->m_vRealSize.goal();
|
||||
const auto PWINDOWPOS = PCURRENT->m_vRealPosition.goal();
|
||||
|
@ -985,7 +981,7 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
|
|||
const auto CURRENTISFOCUS = PCURRENT == g_pCompositor->m_pLastWindow.lock();
|
||||
|
||||
if (FULLSCREEN)
|
||||
g_pCompositor->setWindowFullscreen(PCURRENT, false, WORKSPACE->m_efFullscreenMode);
|
||||
g_pCompositor->setWindowFullscreenInternal(PCURRENT, FSMODE_NONE);
|
||||
|
||||
PCURRENT->setHidden(true);
|
||||
pWindow->setHidden(false); // can remove m_pLastWindow
|
||||
|
@ -1003,7 +999,7 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
|
|||
g_pCompositor->focusWindow(pWindow);
|
||||
|
||||
if (FULLSCREEN)
|
||||
g_pCompositor->setWindowFullscreen(pWindow, true, WORKSPACE->m_efFullscreenMode);
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, MODE);
|
||||
|
||||
g_pHyprRenderer->damageWindow(pWindow);
|
||||
|
||||
|
@ -1102,18 +1098,18 @@ bool CWindow::opaque() {
|
|||
if (PWORKSPACE->m_fAlpha.value() != 1.f)
|
||||
return false;
|
||||
|
||||
if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.buffer)
|
||||
return m_pXWaylandSurface->surface->current.buffer->opaque;
|
||||
if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.texture)
|
||||
return m_pXWaylandSurface->surface->current.texture->m_bOpaque;
|
||||
|
||||
if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.buffer)
|
||||
if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.texture)
|
||||
return false;
|
||||
|
||||
// TODO: this is wrong
|
||||
const auto EXTENTS = m_pXDGSurface->surface->current.opaque.getExtents();
|
||||
if (EXTENTS.w >= m_pXDGSurface->surface->current.buffer->size.x && EXTENTS.h >= m_pXDGSurface->surface->current.buffer->size.y)
|
||||
if (EXTENTS.w >= m_pXDGSurface->surface->current.bufferSize.x && EXTENTS.h >= m_pXDGSurface->surface->current.bufferSize.y)
|
||||
return true;
|
||||
|
||||
return m_pWLSurface->resource()->current.buffer->opaque;
|
||||
return m_pWLSurface->resource()->current.texture->m_bOpaque;
|
||||
}
|
||||
|
||||
float CWindow::rounding() {
|
||||
|
@ -1146,7 +1142,7 @@ void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) {
|
|||
}
|
||||
|
||||
int CWindow::getRealBorderSize() {
|
||||
if (m_sWindowData.noBorder.valueOrDefault() || (m_pWorkspace && m_bIsFullscreen && (m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL)))
|
||||
if (m_sWindowData.noBorder.valueOrDefault() || (m_pWorkspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN)))
|
||||
return 0;
|
||||
|
||||
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
|
||||
|
@ -1159,11 +1155,6 @@ bool CWindow::canBeTorn() {
|
|||
return m_sWindowData.tearing.valueOr(m_bTearingHint) && *PTEARING;
|
||||
}
|
||||
|
||||
bool CWindow::shouldSendFullscreenState() {
|
||||
const auto MODE = m_pWorkspace->m_efFullscreenMode;
|
||||
return m_bDontSendFullscreen ? false : (m_bFakeFullscreenState || (m_bIsFullscreen && (MODE == FULLSCREEN_FULL)));
|
||||
}
|
||||
|
||||
void CWindow::setSuspended(bool suspend) {
|
||||
if (suspend == m_bSuspended)
|
||||
return;
|
||||
|
@ -1190,7 +1181,7 @@ void CWindow::setAnimationsToMove() {
|
|||
|
||||
void CWindow::onWorkspaceAnimUpdate() {
|
||||
// clip box for animated offsets
|
||||
if (!m_bIsFloating || m_bPinned || m_bIsFullscreen) {
|
||||
if (!m_bIsFloating || m_bPinned || isFullscreen()) {
|
||||
m_vFloatingOffset = Vector2D(0, 0);
|
||||
return;
|
||||
}
|
||||
|
@ -1244,6 +1235,14 @@ int CWindow::surfacesCount() {
|
|||
return no;
|
||||
}
|
||||
|
||||
bool CWindow::isFullscreen() {
|
||||
return m_sFullscreenState.internal != FSMODE_NONE;
|
||||
}
|
||||
|
||||
bool CWindow::isEffectiveInternalFSMode(const eFullscreenMode MODE) {
|
||||
return (eFullscreenMode)std::bit_floor((uint8_t)m_sFullscreenState.internal) == MODE;
|
||||
}
|
||||
|
||||
int CWindow::workspaceID() {
|
||||
return m_pWorkspace ? m_pWorkspace->m_iID : m_iLastWorkspace;
|
||||
}
|
||||
|
@ -1321,19 +1320,17 @@ void CWindow::onUpdateState() {
|
|||
|
||||
if (requestsFS.has_value() && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) {
|
||||
bool fs = requestsFS.value();
|
||||
|
||||
if (fs != m_bIsFullscreen && m_bIsMapped)
|
||||
g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_FULL);
|
||||
if (m_bIsMapped) {
|
||||
g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_FULLSCREEN, requestsFS.value());
|
||||
}
|
||||
|
||||
if (!m_bIsMapped)
|
||||
m_bWantsInitialFullscreen = fs;
|
||||
}
|
||||
|
||||
if (requestsMX.has_value() && !(m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) {
|
||||
bool fs = requestsMX.value();
|
||||
|
||||
if (fs != m_bIsFullscreen && m_bIsMapped)
|
||||
g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_MAXIMIZED);
|
||||
if (m_bIsMapped)
|
||||
g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_MAXIMIZED, requestsMX.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1438,7 +1435,7 @@ void CWindow::onX11Configure(CBox box) {
|
|||
|
||||
g_pHyprRenderer->damageWindow(m_pSelf.lock());
|
||||
|
||||
if (!m_bIsFloating || m_bIsFullscreen || g_pInputManager->currentlyDraggedWindow == m_pSelf) {
|
||||
if (!m_bIsFloating || isFullscreen() || g_pInputManager->currentlyDraggedWindow == m_pSelf) {
|
||||
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.goal(), true);
|
||||
g_pInputManager->refocus();
|
||||
g_pHyprRenderer->damageWindow(m_pSelf.lock());
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "Popup.hpp"
|
||||
#include "Subsurface.hpp"
|
||||
#include "WLSurface.hpp"
|
||||
#include "Workspace.hpp"
|
||||
|
||||
class CXDGSurfaceResource;
|
||||
class CXWaylandSurface;
|
||||
|
@ -153,24 +154,26 @@ struct SWindowData {
|
|||
CWindowOverridableVar<SAlphaValue> alphaInactive = SAlphaValue{1.f, false};
|
||||
CWindowOverridableVar<SAlphaValue> alphaFullscreen = SAlphaValue{1.f, false};
|
||||
|
||||
CWindowOverridableVar<bool> allowsInput = false;
|
||||
CWindowOverridableVar<bool> dimAround = false;
|
||||
CWindowOverridableVar<bool> decorate = true;
|
||||
CWindowOverridableVar<bool> focusOnActivate = false;
|
||||
CWindowOverridableVar<bool> keepAspectRatio = false;
|
||||
CWindowOverridableVar<bool> nearestNeighbor = false;
|
||||
CWindowOverridableVar<bool> noAnim = false;
|
||||
CWindowOverridableVar<bool> noBorder = false;
|
||||
CWindowOverridableVar<bool> noBlur = false;
|
||||
CWindowOverridableVar<bool> noDim = false;
|
||||
CWindowOverridableVar<bool> noFocus = false;
|
||||
CWindowOverridableVar<bool> noMaxSize = false;
|
||||
CWindowOverridableVar<bool> noRounding = false;
|
||||
CWindowOverridableVar<bool> noShadow = false;
|
||||
CWindowOverridableVar<bool> opaque = false;
|
||||
CWindowOverridableVar<bool> RGBX = false;
|
||||
CWindowOverridableVar<bool> tearing = false;
|
||||
CWindowOverridableVar<bool> xray = false;
|
||||
CWindowOverridableVar<bool> allowsInput = false;
|
||||
CWindowOverridableVar<bool> dimAround = false;
|
||||
CWindowOverridableVar<bool> decorate = true;
|
||||
CWindowOverridableVar<bool> focusOnActivate = false;
|
||||
CWindowOverridableVar<bool> keepAspectRatio = false;
|
||||
CWindowOverridableVar<bool> nearestNeighbor = false;
|
||||
CWindowOverridableVar<bool> noAnim = false;
|
||||
CWindowOverridableVar<bool> noBorder = false;
|
||||
CWindowOverridableVar<bool> noBlur = false;
|
||||
CWindowOverridableVar<bool> noDim = false;
|
||||
CWindowOverridableVar<bool> noFocus = false;
|
||||
CWindowOverridableVar<bool> noMaxSize = false;
|
||||
CWindowOverridableVar<bool> noRounding = false;
|
||||
CWindowOverridableVar<bool> noShadow = false;
|
||||
CWindowOverridableVar<bool> noShortcutsInhibit = false;
|
||||
CWindowOverridableVar<bool> opaque = false;
|
||||
CWindowOverridableVar<bool> RGBX = false;
|
||||
CWindowOverridableVar<bool> syncFullscreen = true;
|
||||
CWindowOverridableVar<bool> tearing = false;
|
||||
CWindowOverridableVar<bool> xray = false;
|
||||
|
||||
CWindowOverridableVar<int> rounding;
|
||||
CWindowOverridableVar<int> borderSize;
|
||||
|
@ -207,6 +210,11 @@ struct SInitialWorkspaceToken {
|
|||
std::string workspace;
|
||||
};
|
||||
|
||||
struct sFullscreenState {
|
||||
eFullscreenMode internal = FSMODE_NONE;
|
||||
eFullscreenMode client = FSMODE_NONE;
|
||||
};
|
||||
|
||||
class CWindow {
|
||||
public:
|
||||
static PHLWINDOW create(SP<CXDGSurfaceResource>);
|
||||
|
@ -255,24 +263,23 @@ class CWindow {
|
|||
Vector2D m_vPseudoSize = Vector2D(1280, 720);
|
||||
|
||||
// for recovering relative cursor position
|
||||
Vector2D m_vRelativeCursorCoordsOnLastWarp = Vector2D(-1, -1);
|
||||
Vector2D m_vRelativeCursorCoordsOnLastWarp = Vector2D(-1, -1);
|
||||
|
||||
bool m_bFirstMap = false; // for layouts
|
||||
bool m_bIsFloating = false;
|
||||
bool m_bDraggingTiled = false; // for dragging around tiled windows
|
||||
bool m_bIsFullscreen = false;
|
||||
bool m_bDontSendFullscreen = false;
|
||||
bool m_bWasMaximized = false;
|
||||
uint64_t m_iMonitorID = -1;
|
||||
std::string m_szTitle = "";
|
||||
std::string m_szClass = "";
|
||||
std::string m_szInitialTitle = "";
|
||||
std::string m_szInitialClass = "";
|
||||
PHLWORKSPACE m_pWorkspace;
|
||||
bool m_bFirstMap = false; // for layouts
|
||||
bool m_bIsFloating = false;
|
||||
bool m_bDraggingTiled = false; // for dragging around tiled windows
|
||||
bool m_bWasMaximized = false;
|
||||
sFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
|
||||
uint64_t m_iMonitorID = -1;
|
||||
std::string m_szTitle = "";
|
||||
std::string m_szClass = "";
|
||||
std::string m_szInitialTitle = "";
|
||||
std::string m_szInitialClass = "";
|
||||
PHLWORKSPACE m_pWorkspace;
|
||||
|
||||
bool m_bIsMapped = false;
|
||||
bool m_bIsMapped = false;
|
||||
|
||||
bool m_bRequestsFloat = false;
|
||||
bool m_bRequestsFloat = false;
|
||||
|
||||
// This is for fullscreen apps
|
||||
bool m_bCreatedOverFullscreen = false;
|
||||
|
@ -321,9 +328,6 @@ class CWindow {
|
|||
// urgency hint
|
||||
bool m_bIsUrgent = false;
|
||||
|
||||
// fakefullscreen
|
||||
bool m_bFakeFullscreenState = false;
|
||||
|
||||
// for proper cycling. While cycling we can't just move the pointers, so we need to keep track of the last cycled window.
|
||||
PHLWINDOWREF m_pLastCycledWindow;
|
||||
|
||||
|
@ -415,7 +419,6 @@ class CWindow {
|
|||
bool opaque();
|
||||
float rounding();
|
||||
bool canBeTorn();
|
||||
bool shouldSendFullscreenState();
|
||||
void setSuspended(bool suspend);
|
||||
bool visibleOnMonitor(CMonitor* pMonitor);
|
||||
int workspaceID();
|
||||
|
@ -423,6 +426,9 @@ class CWindow {
|
|||
void activate(bool force = false);
|
||||
int surfacesCount();
|
||||
|
||||
bool isFullscreen();
|
||||
bool isEffectiveInternalFSMode(const eFullscreenMode);
|
||||
|
||||
int getRealBorderSize();
|
||||
void updateWindowData();
|
||||
void updateWindowData(const struct SWorkspaceRule&);
|
||||
|
|
|
@ -480,16 +480,11 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
|
|||
return false;
|
||||
break;
|
||||
case 0: // fullscreen full
|
||||
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FULLSCREEN_FULL)
|
||||
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FSMODE_FULLSCREEN)
|
||||
return false;
|
||||
break;
|
||||
case 1: // maximized
|
||||
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FULLSCREEN_MAXIMIZED)
|
||||
return false;
|
||||
break;
|
||||
case 2: // fullscreen without sending fullscreen state to window
|
||||
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FULLSCREEN_FULL || !g_pCompositor->getFullscreenWindowOnWorkspace(m_iID) ||
|
||||
!g_pCompositor->getFullscreenWindowOnWorkspace(m_iID)->m_bDontSendFullscreen)
|
||||
if (!m_bHasFullscreenWindow || m_efFullscreenMode != FSMODE_MAXIMIZED)
|
||||
return false;
|
||||
break;
|
||||
default: break;
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
#include "../helpers/MiscFunctions.hpp"
|
||||
|
||||
enum eFullscreenMode : int8_t {
|
||||
FULLSCREEN_INVALID = -1,
|
||||
FULLSCREEN_FULL = 0,
|
||||
FULLSCREEN_MAXIMIZED
|
||||
FSMODE_NONE = 0,
|
||||
FSMODE_MAXIMIZED = 1 << 0,
|
||||
FSMODE_FULLSCREEN = 1 << 1,
|
||||
FSMODE_MAX = (1 << 2) - 1
|
||||
};
|
||||
|
||||
class CWindow;
|
||||
|
@ -31,7 +32,7 @@ class CWorkspace {
|
|||
SWorkspaceIDName m_sPrevWorkspace, m_sPrevWorkspacePerMonitor;
|
||||
|
||||
bool m_bHasFullscreenWindow = false;
|
||||
eFullscreenMode m_efFullscreenMode = FULLSCREEN_FULL;
|
||||
eFullscreenMode m_efFullscreenMode = FSMODE_NONE;
|
||||
|
||||
wl_array m_wlrCoordinateArr;
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@ void IKeyboard::clearManuallyAllocd() {
|
|||
}
|
||||
|
||||
void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
||||
if (keymapOverridden) {
|
||||
Debug::log(LOG, "Ignoring setKeymap: keymap is overridden");
|
||||
return;
|
||||
}
|
||||
|
||||
currentRules = rules;
|
||||
xkb_rule_names XKBRULES = {
|
||||
.rules = rules.rules.c_str(),
|
||||
|
@ -103,10 +108,6 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
|||
xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
// set internal translation state
|
||||
// demo sunao ni ienai
|
||||
xkbStaticState = xkb_state_new(xkbKeymap);
|
||||
|
||||
updateXKBTranslationState(xkbKeymap);
|
||||
|
||||
const auto NUMLOCKON = g_pConfigManager->getDeviceInt(hlName, "numlock_by_default", "input:numlock_by_default");
|
||||
|
@ -131,6 +132,20 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
|||
Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES.at(i), modIndexes.at(i));
|
||||
}
|
||||
|
||||
updateKeymapFD();
|
||||
|
||||
xkb_context_unref(CONTEXT);
|
||||
|
||||
g_pSeatManager->updateActiveKeyboardData();
|
||||
}
|
||||
|
||||
void IKeyboard::updateKeymapFD() {
|
||||
Debug::log(LOG, "Updating keymap fd for keyboard {}", deviceName);
|
||||
|
||||
if (xkbKeymapFD >= 0)
|
||||
close(xkbKeymapFD);
|
||||
xkbKeymapFD = -1;
|
||||
|
||||
auto cKeymapStr = xkb_keymap_get_as_string(xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1);
|
||||
xkbKeymapString = cKeymapStr;
|
||||
free(cKeymapStr);
|
||||
|
@ -151,21 +166,24 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
|
|||
}
|
||||
}
|
||||
|
||||
xkb_context_unref(CONTEXT);
|
||||
|
||||
g_pSeatManager->updateActiveKeyboardData();
|
||||
Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD);
|
||||
}
|
||||
|
||||
void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
|
||||
|
||||
if (xkbStaticState)
|
||||
xkb_state_unref(xkbStaticState);
|
||||
|
||||
if (xkbState)
|
||||
xkb_state_unref(xkbState);
|
||||
|
||||
xkbState = nullptr;
|
||||
xkbState = nullptr;
|
||||
xkbStaticState = nullptr;
|
||||
|
||||
if (keymap) {
|
||||
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
|
||||
xkbState = xkb_state_new(keymap);
|
||||
xkbStaticState = xkb_state_new(keymap);
|
||||
xkbState = xkb_state_new(keymap);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -209,7 +227,8 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
|
|||
KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
}
|
||||
|
||||
xkbState = xkb_state_new(KEYMAP);
|
||||
xkbState = xkb_state_new(KEYMAP);
|
||||
xkbStaticState = xkb_state_new(KEYMAP);
|
||||
|
||||
xkb_keymap_unref(KEYMAP);
|
||||
xkb_context_unref(PCONTEXT);
|
||||
|
@ -230,7 +249,8 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
|
|||
|
||||
const auto NEWKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
xkbState = xkb_state_new(NEWKEYMAP);
|
||||
xkbState = xkb_state_new(NEWKEYMAP);
|
||||
xkbStaticState = xkb_state_new(NEWKEYMAP);
|
||||
|
||||
xkb_keymap_unref(NEWKEYMAP);
|
||||
xkb_context_unref(PCONTEXT);
|
||||
|
@ -254,9 +274,9 @@ std::string IKeyboard::getActiveLayout() {
|
|||
return "none";
|
||||
}
|
||||
|
||||
void IKeyboard::updateLEDs() {
|
||||
std::optional<uint32_t> IKeyboard::getLEDs() {
|
||||
if (xkbState == nullptr)
|
||||
return;
|
||||
return {};
|
||||
|
||||
uint32_t leds = 0;
|
||||
for (uint32_t i = 0; i < LED_COUNT; ++i) {
|
||||
|
@ -264,7 +284,16 @@ void IKeyboard::updateLEDs() {
|
|||
leds |= (1 << i);
|
||||
}
|
||||
|
||||
updateLEDs(leds);
|
||||
return leds;
|
||||
}
|
||||
|
||||
void IKeyboard::updateLEDs() {
|
||||
std::optional<uint32_t> leds = getLEDs();
|
||||
|
||||
if (!leds.has_value())
|
||||
return;
|
||||
|
||||
updateLEDs(leds.value());
|
||||
}
|
||||
|
||||
void IKeyboard::updateLEDs(uint32_t leds) {
|
||||
|
@ -305,6 +334,13 @@ void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t l
|
|||
if (!updateModifiersState())
|
||||
return;
|
||||
|
||||
keyboardEvents.modifiers.emit(SModifiersEvent{
|
||||
.depressed = modifiersState.depressed,
|
||||
.latched = modifiersState.latched,
|
||||
.locked = modifiersState.locked,
|
||||
.group = modifiersState.group,
|
||||
});
|
||||
|
||||
updateLEDs();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../macros.hpp"
|
||||
#include "../helpers/math/Math.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
AQUAMARINE_FORWARD(IKeyboard);
|
||||
|
@ -61,18 +62,25 @@ class IKeyboard : public IHID {
|
|||
std::string rules = "";
|
||||
};
|
||||
|
||||
void setKeymap(const SStringRuleNames& rules);
|
||||
void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
|
||||
std::string getActiveLayout();
|
||||
void updateLEDs();
|
||||
void updateLEDs(uint32_t leds);
|
||||
uint32_t getModifiers();
|
||||
void updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
|
||||
bool updateModifiersState(); // rets whether changed
|
||||
void updateXkbStateWithKey(uint32_t xkbKey, bool pressed);
|
||||
void setKeymap(const SStringRuleNames& rules);
|
||||
void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
|
||||
std::string getActiveLayout();
|
||||
std::optional<uint32_t> getLEDs();
|
||||
void updateLEDs();
|
||||
void updateLEDs(uint32_t leds);
|
||||
uint32_t getModifiers();
|
||||
void updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
|
||||
bool updateModifiersState(); // rets whether changed
|
||||
void updateXkbStateWithKey(uint32_t xkbKey, bool pressed);
|
||||
void updateKeymapFD();
|
||||
|
||||
bool active = false;
|
||||
bool enabled = true;
|
||||
bool active = false;
|
||||
bool enabled = true;
|
||||
|
||||
// if the keymap is overridden by the implementation,
|
||||
// don't try to set keyboard rules anymore, to avoid overwriting the requested one.
|
||||
// e.g. Virtual keyboards with custom maps.
|
||||
bool keymapOverridden = false;
|
||||
|
||||
xkb_layout_index_t activeLayout = 0;
|
||||
xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr;
|
||||
|
|
|
@ -106,7 +106,8 @@ class IPointer : public IHID {
|
|||
} pointerEvents;
|
||||
|
||||
std::string hlName;
|
||||
bool connected = false; // means connected to the cursor
|
||||
bool connected = false; // means connected to the cursor
|
||||
std::string boundOutput = "";
|
||||
|
||||
WP<IPointer> self;
|
||||
};
|
||||
|
|
|
@ -32,13 +32,13 @@ CKeyboard::CKeyboard(SP<Aquamarine::IKeyboard> keeb) : keyboard(keeb) {
|
|||
listeners.key = keeb->events.key.registerListener([this](std::any d) {
|
||||
auto E = std::any_cast<Aquamarine::IKeyboard::SKeyEvent>(d);
|
||||
|
||||
updateXkbStateWithKey(E.key + 8, E.pressed);
|
||||
|
||||
keyboardEvents.key.emit(SKeyEvent{
|
||||
.timeMs = E.timeMs,
|
||||
.keycode = E.key,
|
||||
.state = E.pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
});
|
||||
|
||||
updateXkbStateWithKey(E.key + 8, E.pressed);
|
||||
});
|
||||
|
||||
listeners.modifiers = keeb->events.modifiers.registerListener([this](std::any d) {
|
||||
|
|
|
@ -31,8 +31,13 @@ CVirtualKeyboard::CVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keeb_) : keybo
|
|||
});
|
||||
});
|
||||
listeners.keymap = keeb_->events.keymap.registerListener([this](std::any d) {
|
||||
auto E = std::any_cast<SKeymapEvent>(d);
|
||||
xkbKeymap = xkb_keymap_ref(E.keymap);
|
||||
auto E = std::any_cast<SKeymapEvent>(d);
|
||||
if (xkbKeymap)
|
||||
xkb_keymap_unref(xkbKeymap);
|
||||
xkbKeymap = xkb_keymap_ref(E.keymap);
|
||||
keymapOverridden = true;
|
||||
updateXKBTranslationState(xkbKeymap);
|
||||
updateKeymapFD();
|
||||
keyboardEvents.keymap.emit(d);
|
||||
});
|
||||
|
||||
|
|
|
@ -20,7 +20,12 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
|
|||
});
|
||||
|
||||
listeners.motion = pointer->events.move.registerListener([this](std::any d) { pointerEvents.motion.emit(d); });
|
||||
listeners.motionAbsolute = pointer->events.warp.registerListener([this](std::any d) { pointerEvents.motionAbsolute.emit(d); });
|
||||
listeners.motionAbsolute = pointer->events.warp.registerListener([this](std::any d) {
|
||||
// we need to unpack the event and add our device here because it's required to calculate the position correctly
|
||||
auto E = std::any_cast<SMotionAbsoluteEvent>(d);
|
||||
E.device = self.lock();
|
||||
pointerEvents.motionAbsolute.emit(E);
|
||||
});
|
||||
listeners.button = pointer->events.button.registerListener([this](std::any d) { pointerEvents.button.emit(d); });
|
||||
listeners.axis = pointer->events.axis.registerListener([this](std::any d) { pointerEvents.axis.emit(d); });
|
||||
listeners.frame = pointer->events.frame.registerListener([this](std::any d) { pointerEvents.frame.emit(); });
|
||||
|
@ -33,6 +38,8 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
|
|||
listeners.holdBegin = pointer->events.holdBegin.registerListener([this](std::any d) { pointerEvents.holdBegin.emit(d); });
|
||||
listeners.holdEnd = pointer->events.holdEnd.registerListener([this](std::any d) { pointerEvents.holdEnd.emit(d); });
|
||||
|
||||
boundOutput = resource->boundOutput ? resource->boundOutput->szName : "entire";
|
||||
|
||||
deviceName = pointer->name;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "Events.hpp"
|
||||
#include "../debug/HyprCtl.hpp"
|
||||
#include "../config/ConfigValue.hpp"
|
||||
#include "../protocols/Screencopy.hpp"
|
||||
#include "../protocols/ToplevelExport.hpp"
|
||||
#include <aquamarine/output/Output.hpp>
|
||||
|
||||
// --------------------------------------------------------- //
|
||||
|
@ -118,8 +120,8 @@ void Events::listener_monitorCommit(void* owner, void* data) {
|
|||
const auto PMONITOR = (CMonitor*)owner;
|
||||
|
||||
if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR);
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR);
|
||||
PROTO::screencopy->onOutputCommit(PMONITOR);
|
||||
PROTO::toplevelExport->onOutputCommit(PMONITOR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../protocols/XDGShell.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../protocols/ToplevelExport.hpp"
|
||||
#include "../xwayland/XSurface.hpp"
|
||||
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
@ -132,12 +133,11 @@ void Events::listener_mapWindow(void* owner, void* data) {
|
|||
}
|
||||
|
||||
// window rules
|
||||
PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false);
|
||||
bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen);
|
||||
bool requestsFakeFullscreen = false;
|
||||
bool requestsMaximize = false;
|
||||
bool overridingNoFullscreen = false;
|
||||
bool overridingNoMaximize = false;
|
||||
PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false);
|
||||
std::optional<eFullscreenMode> requestedInternalFSMode, requestedClientFSMode;
|
||||
std::optional<sFullscreenState> requestedFSState;
|
||||
if (PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen))
|
||||
requestedClientFSMode = FSMODE_FULLSCREEN;
|
||||
|
||||
for (auto& r : PWINDOW->m_vMatchedRules) {
|
||||
if (r.szRule.starts_with("monitor")) {
|
||||
|
@ -198,6 +198,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
|
|||
PWINDOW->m_bIsPseudotiled = true;
|
||||
} else if (r.szRule.starts_with("noinitialfocus")) {
|
||||
PWINDOW->m_bNoInitialFocus = true;
|
||||
} else if (r.szRule.starts_with("fullscreenstate")) {
|
||||
const auto ARGS = CVarList(r.szRule.substr(r.szRule.find_first_of(' ') + 1), 2, ' ');
|
||||
int internalMode, clientMode;
|
||||
try {
|
||||
internalMode = std::stoi(ARGS[0]);
|
||||
} catch (std::exception& e) { internalMode = 0; }
|
||||
try {
|
||||
clientMode = std::stoi(ARGS[1]);
|
||||
} catch (std::exception& e) { clientMode = 0; }
|
||||
requestedFSState = sFullscreenState{.internal = (eFullscreenMode)internalMode, .client = (eFullscreenMode)clientMode};
|
||||
} else if (r.szRule.starts_with("suppressevent")) {
|
||||
CVarList vars(r.szRule, 0, 's', true);
|
||||
for (size_t i = 1; i < vars.size(); ++i) {
|
||||
|
@ -212,16 +222,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
|
|||
else
|
||||
Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]);
|
||||
}
|
||||
} else if (r.szRule == "fullscreen") {
|
||||
requestsFullscreen = true;
|
||||
overridingNoFullscreen = true;
|
||||
} else if (r.szRule == "fakefullscreen") {
|
||||
requestsFakeFullscreen = true;
|
||||
} else if (r.szRule == "pin") {
|
||||
PWINDOW->m_bPinned = true;
|
||||
} else if (r.szRule == "fullscreen") {
|
||||
requestedInternalFSMode = FSMODE_FULLSCREEN;
|
||||
} else if (r.szRule == "maximize") {
|
||||
requestsMaximize = true;
|
||||
overridingNoMaximize = true;
|
||||
requestedInternalFSMode = FSMODE_MAXIMIZED;
|
||||
} else if (r.szRule == "stayfocused") {
|
||||
PWINDOW->m_bStayFocused = true;
|
||||
} else if (r.szRule.starts_with("group")) {
|
||||
|
@ -460,17 +466,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
|
|||
const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock());
|
||||
if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE)
|
||||
PWINDOW->m_bNoInitialFocus = true;
|
||||
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow && !requestsFullscreen && !PWINDOW->m_bIsFloating) {
|
||||
|
||||
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow && !requestedInternalFSMode.has_value() && !requestedClientFSMode.has_value() && !PWINDOW->m_bIsFloating) {
|
||||
if (*PNEWTAKESOVERFS == 0)
|
||||
PWINDOW->m_bNoInitialFocus = true;
|
||||
else if (*PNEWTAKESOVERFS == 1)
|
||||
requestedInternalFSMode = PWINDOW->m_pWorkspace->m_efFullscreenMode;
|
||||
else if (*PNEWTAKESOVERFS == 2)
|
||||
g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), false, FULLSCREEN_INVALID);
|
||||
else if (PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
|
||||
requestsMaximize = true;
|
||||
if (*PNEWTAKESOVERFS == 1)
|
||||
overridingNoMaximize = true;
|
||||
} else
|
||||
requestsFullscreen = true;
|
||||
g_pCompositor->setWindowFullscreenInternal(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), FSMODE_NONE);
|
||||
}
|
||||
|
||||
if (!PWINDOW->m_sWindowData.noFocus.valueOrDefault() && !PWINDOW->m_bNoInitialFocus &&
|
||||
|
@ -484,24 +487,27 @@ void Events::listener_mapWindow(void* owner, void* data) {
|
|||
PWINDOW->m_fDimPercent.setValueAndWarp(0);
|
||||
}
|
||||
|
||||
if ((requestsFullscreen && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN) || overridingNoFullscreen)) ||
|
||||
(requestsMaximize && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) || overridingNoMaximize)) || requestsFakeFullscreen) {
|
||||
// fix fullscreen on requested (basically do a switcheroo)
|
||||
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow) {
|
||||
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID);
|
||||
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, FULLSCREEN_FULL);
|
||||
}
|
||||
if (requestedClientFSMode.has_value() && (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN))
|
||||
requestedClientFSMode = (eFullscreenMode)((uint8_t)requestedClientFSMode.value_or(FSMODE_NONE) & ~(uint8_t)FSMODE_FULLSCREEN);
|
||||
if (requestedClientFSMode.has_value() && (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE))
|
||||
requestedClientFSMode = (eFullscreenMode)((uint8_t)requestedClientFSMode.value_or(FSMODE_NONE) & ~(uint8_t)FSMODE_MAXIMIZED);
|
||||
|
||||
if (requestsFakeFullscreen && !PWINDOW->m_bFakeFullscreenState) {
|
||||
PWINDOW->m_bFakeFullscreenState = !PWINDOW->m_bFakeFullscreenState;
|
||||
g_pXWaylandManager->setWindowFullscreen(PWINDOW, true);
|
||||
} else {
|
||||
overridingNoFullscreen = false;
|
||||
overridingNoMaximize = false;
|
||||
PWINDOW->m_vRealPosition.warp();
|
||||
PWINDOW->m_vRealSize.warp();
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, true, requestsFullscreen ? FULLSCREEN_FULL : FULLSCREEN_MAXIMIZED);
|
||||
}
|
||||
if (!PWINDOW->m_bNoInitialFocus && (requestedInternalFSMode.has_value() || requestedClientFSMode.has_value() || requestedFSState.has_value())) {
|
||||
// fix fullscreen on requested (basically do a switcheroo)
|
||||
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow)
|
||||
g_pCompositor->setWindowFullscreenInternal(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), FSMODE_NONE);
|
||||
|
||||
PWINDOW->m_vRealPosition.warp();
|
||||
PWINDOW->m_vRealSize.warp();
|
||||
if (requestedFSState.has_value()) {
|
||||
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_WINDOW_RULE);
|
||||
g_pCompositor->setWindowFullscreenState(PWINDOW, requestedFSState.value());
|
||||
} else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
|
||||
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()});
|
||||
else if (requestedInternalFSMode.has_value())
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, requestedInternalFSMode.value());
|
||||
else if (requestedClientFSMode.has_value())
|
||||
g_pCompositor->setWindowFullscreenClient(PWINDOW, requestedClientFSMode.value());
|
||||
}
|
||||
|
||||
// recheck idle inhibitors
|
||||
|
@ -557,7 +563,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
|
|||
// recalc the values for this window
|
||||
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
|
||||
// avoid this window being visible
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFullscreen && !PWINDOW->m_bIsFloating)
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen() && !PWINDOW->m_bIsFloating)
|
||||
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
|
||||
|
||||
g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->scale);
|
||||
|
@ -582,8 +588,8 @@ void Events::listener_unmapWindow(void* owner, void* data) {
|
|||
|
||||
static auto PEXITRETAINSFS = CConfigValue<Hyprlang::INT>("misc:exit_window_retains_fullscreen");
|
||||
|
||||
const auto CURRENTWINDOWFSSTATE = PWINDOW->m_bIsFullscreen;
|
||||
const auto CURRENTWINDOWFSMODE = PWINDOW->m_pWorkspace->m_efFullscreenMode;
|
||||
const auto CURRENTWINDOWFSSTATE = PWINDOW->isFullscreen();
|
||||
const auto CURRENTFSMODE = PWINDOW->m_sFullscreenState.internal;
|
||||
|
||||
if (!PWINDOW->m_pWLSurface->exists() || !PWINDOW->m_bIsMapped) {
|
||||
Debug::log(WARN, "{} unmapped without being mapped??", PWINDOW);
|
||||
|
@ -601,10 +607,10 @@ void Events::listener_unmapWindow(void* owner, void* data) {
|
|||
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
|
||||
EMIT_HOOK_EVENT("closeWindow", PWINDOW);
|
||||
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW);
|
||||
PROTO::toplevelExport->onWindowUnmap(PWINDOW);
|
||||
|
||||
if (PWINDOW->m_bIsFullscreen)
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
|
||||
if (PWINDOW->isFullscreen())
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
|
||||
|
||||
// Allow the renderer to catch the last frame.
|
||||
g_pHyprOpenGL->makeWindowSnapshot(PWINDOW);
|
||||
|
@ -626,10 +632,13 @@ void Events::listener_unmapWindow(void* owner, void* data) {
|
|||
g_pInputManager->releaseAllMouseButtons();
|
||||
}
|
||||
|
||||
if (PWINDOW == g_pInputManager->currentlyDraggedWindow.lock())
|
||||
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
|
||||
|
||||
// remove the fullscreen window status from workspace if we closed it
|
||||
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && PWINDOW->m_bIsFullscreen)
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && PWINDOW->isFullscreen())
|
||||
PWORKSPACE->m_bHasFullscreenWindow = false;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);
|
||||
|
@ -646,7 +655,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
|
|||
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow.lock() && PWINDOWCANDIDATE) {
|
||||
g_pCompositor->focusWindow(PWINDOWCANDIDATE);
|
||||
if (*PEXITRETAINSFS && CURRENTWINDOWFSSTATE)
|
||||
g_pCompositor->setWindowFullscreen(PWINDOWCANDIDATE, true, CURRENTWINDOWFSMODE);
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOWCANDIDATE, CURRENTFSMODE);
|
||||
}
|
||||
|
||||
if (!PWINDOWCANDIDATE && g_pCompositor->getWindowsOnWorkspace(PWINDOW->workspaceID()) == 0)
|
||||
|
@ -702,14 +711,9 @@ void Events::listener_commitWindow(void* owner, void* data) {
|
|||
if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden())
|
||||
return;
|
||||
|
||||
if (PWINDOW->m_bIsX11)
|
||||
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
|
||||
else if (PWINDOW->m_pPendingSizeAck.has_value()) {
|
||||
PWINDOW->m_vReportedSize = PWINDOW->m_pPendingSizeAck->second;
|
||||
PWINDOW->m_pPendingSizeAck.reset();
|
||||
}
|
||||
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
|
||||
|
||||
if (!PWINDOW->m_bIsX11 && !PWINDOW->m_bIsFullscreen && PWINDOW->m_bIsFloating) {
|
||||
if (!PWINDOW->m_bIsX11 && !PWINDOW->isFullscreen() && PWINDOW->m_bIsFloating) {
|
||||
const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize;
|
||||
const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize;
|
||||
|
||||
|
@ -748,7 +752,7 @@ void Events::listener_commitWindow(void* owner, void* data) {
|
|||
|
||||
// tearing: if solitary, redraw it. This still might be a single surface window
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
|
||||
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.buffer) {
|
||||
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) {
|
||||
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()};
|
||||
|
||||
if (!damageBox.empty()) {
|
||||
|
@ -786,6 +790,11 @@ void Events::listener_destroyWindow(void* owner, void* data) {
|
|||
Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW);
|
||||
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
|
||||
}
|
||||
|
||||
PWINDOW->listeners.unmap.reset();
|
||||
PWINDOW->listeners.destroy.reset();
|
||||
PWINDOW->listeners.map.reset();
|
||||
PWINDOW->listeners.commit.reset();
|
||||
}
|
||||
|
||||
void Events::listener_setTitleWindow(void* owner, void* data) {
|
||||
|
@ -836,7 +845,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
|
|||
else
|
||||
PWINDOW->setHidden(true);
|
||||
|
||||
if (PWINDOW->m_bIsFullscreen || !PWINDOW->m_bIsFloating) {
|
||||
if (PWINDOW->isFullscreen() || !PWINDOW->m_bIsFloating) {
|
||||
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal(), true);
|
||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||
return;
|
||||
|
|
|
@ -30,8 +30,10 @@ void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
|
|||
const auto POINTSSIZE = m_aPointsBaked.size() * sizeof(m_aPointsBaked[0]) / 1000.f;
|
||||
|
||||
const auto BEGINCALC = std::chrono::high_resolution_clock::now();
|
||||
for (float i = 0.1f; i < 1.f; i += 0.1f)
|
||||
for (int j = 1; j < 10; ++j) {
|
||||
float i = j / 10.0f;
|
||||
getYForPoint(i);
|
||||
}
|
||||
const auto ELAPSEDCALCAVG = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - BEGINCALC).count() / 1000.f / 10.f;
|
||||
|
||||
Debug::log(LOG, "Created a bezier curve, baked {} points, mem usage: {:.2f}kB, time to bake: {:.2f}µs. Estimated average calc time: {:.2f}µs.", BAKEDPOINTS, POINTSSIZE,
|
||||
|
|
|
@ -148,7 +148,7 @@ void CMonitor::onConnect(bool noRule) {
|
|||
|
||||
damage.setSize(vecTransformedSize);
|
||||
|
||||
Debug::log(LOG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", output->name, vecPosition, vecPixelSize, (uintptr_t)output);
|
||||
Debug::log(LOG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", output->name, vecPosition, vecPixelSize, (uintptr_t)output.get());
|
||||
|
||||
setupDefaultWS(monitorRule);
|
||||
|
||||
|
@ -354,7 +354,7 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
|
|||
|
||||
// skip scheduling extra frames for fullsreen apps with vrr
|
||||
bool shouldSkip =
|
||||
*PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FULLSCREEN_FULL;
|
||||
*PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
|
||||
|
||||
// keep requested minimum refresh rate
|
||||
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) {
|
||||
|
@ -801,18 +801,18 @@ bool CMonitor::attemptDirectScanout() {
|
|||
|
||||
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE);
|
||||
|
||||
if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.buffer->size != vecPixelSize || PSURFACE->current.transform != transform)
|
||||
if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform)
|
||||
return false;
|
||||
|
||||
// we can't scanout shm buffers.
|
||||
if (!PSURFACE->current.buffer->dmabuf().success)
|
||||
if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
|
||||
return false;
|
||||
|
||||
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
|
||||
// and comes from the appropriate device. This may implode on multi-gpu!!
|
||||
|
||||
output->state->setBuffer(PSURFACE->current.buffer);
|
||||
output->state->setPresentationMode(Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
||||
output->state->setBuffer(PSURFACE->current.buffer->buffer.lock());
|
||||
output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
|
||||
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
|
||||
|
||||
if (!state.test())
|
||||
return false;
|
||||
|
@ -822,6 +822,8 @@ bool CMonitor::attemptDirectScanout() {
|
|||
Debug::log(TRACE, "presentFeedback for DS");
|
||||
PSURFACE->presentFeedback(&now, this, true);
|
||||
|
||||
output->state->addDamage(CBox{{}, vecPixelSize});
|
||||
|
||||
if (state.commit()) {
|
||||
if (lastScanout.expired()) {
|
||||
lastScanout = PCANDIDATE;
|
||||
|
@ -863,6 +865,8 @@ bool CMonitorState::commit() {
|
|||
if (!updateSwapchain())
|
||||
return false;
|
||||
|
||||
EMIT_HOOK_EVENT("preMonitorCommit", m_pOwner);
|
||||
|
||||
ensureBufferPresent();
|
||||
|
||||
bool ret = m_pOwner->output->commit();
|
||||
|
|
|
@ -88,14 +88,14 @@ void CHyprError::createQueued() {
|
|||
cairo_arc(CAIRO, X + RADIUS, Y + RADIUS, RADIUS, 180 * DEGREES, 270 * DEGREES);
|
||||
cairo_close_path(CAIRO);
|
||||
|
||||
cairo_set_source_rgba(CAIRO, m_cQueued.r, m_cQueued.g, m_cQueued.b, m_cQueued.a);
|
||||
cairo_set_source_rgba(CAIRO, 0.06, 0.06, 0.06, 1.0);
|
||||
cairo_fill_preserve(CAIRO);
|
||||
cairo_set_source_rgba(CAIRO, 0, 0, 0, 1);
|
||||
cairo_set_source_rgba(CAIRO, m_cQueued.r, m_cQueued.g, m_cQueued.b, m_cQueued.a);
|
||||
cairo_set_line_width(CAIRO, 2);
|
||||
cairo_stroke(CAIRO);
|
||||
|
||||
// draw the text with a common font
|
||||
const CColor textColor = m_cQueued.r + m_cQueued.g + m_cQueued.b < 0.2f ? CColor(1.0, 1.0, 1.0, 1.0) : CColor(0, 0, 0, 1.0);
|
||||
const CColor textColor = CColor(0.9, 0.9, 0.9, 1.0);
|
||||
cairo_set_source_rgba(CAIRO, textColor.r, textColor.g, textColor.b, textColor.a);
|
||||
|
||||
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
|
||||
|
|
|
@ -136,7 +136,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
|
|||
return;
|
||||
}
|
||||
|
||||
if (PWINDOW->m_bIsFullscreen && !pNode->ignoreFullscreenChecks)
|
||||
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
|
||||
return;
|
||||
|
||||
PWINDOW->unsetWindowData(PRIORITY_LAYOUT);
|
||||
|
@ -158,8 +158,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
|
|||
|
||||
const auto NODESONWORKSPACE = getNodesOnWorkspace(PWINDOW->workspaceID());
|
||||
|
||||
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() &&
|
||||
(NODESONWORKSPACE == 1 || (PWINDOW->m_bIsFullscreen && PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) {
|
||||
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() && (NODESONWORKSPACE == 1 || PWINDOW->isEffectiveInternalFSMode(FSMODE_MAXIMIZED))) {
|
||||
|
||||
PWINDOW->m_sWindowData.decorate = CWindowOverridableVar(true, PRIORITY_LAYOUT);
|
||||
PWINDOW->m_sWindowData.noBorder = CWindowOverridableVar(*PNOGAPSWHENONLY != 2, PRIORITY_LAYOUT);
|
||||
|
@ -178,6 +177,8 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
|
|||
return;
|
||||
}
|
||||
|
||||
PWINDOW->updateWindowDecos();
|
||||
|
||||
auto calcPos = PWINDOW->m_vPosition;
|
||||
auto calcSize = PWINDOW->m_vSize;
|
||||
|
||||
|
@ -216,7 +217,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
|
|||
calcPos = calcPos + RESERVED.topLeft;
|
||||
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
||||
|
||||
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->m_bIsFullscreen) {
|
||||
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) {
|
||||
// if special, we adjust the coords a bit
|
||||
static auto PSCALEFACTOR = CConfigValue<Hyprlang::FLOAT>("dwindle:special_scale_factor");
|
||||
|
||||
|
@ -245,8 +246,6 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
|
|||
|
||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||
}
|
||||
|
||||
PWINDOW->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection direction) {
|
||||
|
@ -500,8 +499,8 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
|
|||
pWindow->unsetWindowData(PRIORITY_LAYOUT);
|
||||
pWindow->updateWindowData();
|
||||
|
||||
if (pWindow->m_bIsFullscreen)
|
||||
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
|
||||
if (pWindow->isFullscreen())
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
|
||||
|
||||
const auto PPARENT = PNODE->pParent;
|
||||
|
||||
|
@ -560,10 +559,10 @@ void CHyprDwindleLayout::calculateWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
// massive hack from the fullscreen func
|
||||
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(pWorkspace->m_iID);
|
||||
|
||||
if (pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL) {
|
||||
if (pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
|
||||
PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition;
|
||||
PFULLWINDOW->m_vRealSize = PMONITOR->vecSize;
|
||||
} else if (pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
|
||||
} else if (pWorkspace->m_efFullscreenMode == FSMODE_MAXIMIZED) {
|
||||
SDwindleNodeData fakeNode;
|
||||
fakeNode.pWindow = PFULLWINDOW;
|
||||
fakeNode.box = {PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft, PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight};
|
||||
|
@ -788,41 +787,19 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
|
|||
}
|
||||
}
|
||||
|
||||
void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscreenMode fullscreenMode, bool on) {
|
||||
if (!validMapped(pWindow))
|
||||
return;
|
||||
|
||||
if (on == pWindow->m_bIsFullscreen)
|
||||
return; // ignore
|
||||
|
||||
void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) {
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
||||
const auto PWORKSPACE = pWindow->m_pWorkspace;
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
|
||||
// if the window wants to be fullscreen but there already is one,
|
||||
// ignore the request.
|
||||
return;
|
||||
}
|
||||
|
||||
// save position and size if floating
|
||||
if (pWindow->m_bIsFloating && on) {
|
||||
if (pWindow->m_bIsFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE) {
|
||||
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goal();
|
||||
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goal();
|
||||
pWindow->m_vPosition = pWindow->m_vRealPosition.goal();
|
||||
pWindow->m_vSize = pWindow->m_vRealSize.goal();
|
||||
}
|
||||
|
||||
// otherwise, accept it.
|
||||
pWindow->m_bIsFullscreen = on;
|
||||
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
|
||||
|
||||
pWindow->updateDynamicRules();
|
||||
pWindow->updateWindowDecos();
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)});
|
||||
EMIT_HOOK_EVENT("fullscreen", pWindow);
|
||||
|
||||
if (!pWindow->m_bIsFullscreen) {
|
||||
if (EFFECTIVE_MODE == FSMODE_NONE) {
|
||||
// if it got its fullscreen disabled, set back its node if it had one
|
||||
const auto PNODE = getNodeFromWindow(pWindow);
|
||||
if (PNODE)
|
||||
|
@ -836,12 +813,8 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscre
|
|||
pWindow->updateWindowData();
|
||||
}
|
||||
} else {
|
||||
// if it now got fullscreen, make it fullscreen
|
||||
|
||||
PWORKSPACE->m_efFullscreenMode = fullscreenMode;
|
||||
|
||||
// apply new pos and size being monitors' box
|
||||
if (fullscreenMode == FULLSCREEN_FULL) {
|
||||
if (EFFECTIVE_MODE == FSMODE_FULLSCREEN) {
|
||||
pWindow->m_vRealPosition = PMONITOR->vecPosition;
|
||||
pWindow->m_vRealSize = PMONITOR->vecSize;
|
||||
} else {
|
||||
|
@ -861,13 +834,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscre
|
|||
}
|
||||
}
|
||||
|
||||
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
|
||||
|
||||
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goal());
|
||||
|
||||
g_pCompositor->changeWindowZOrder(pWindow, true);
|
||||
|
||||
recalculateMonitor(PMONITOR->ID);
|
||||
}
|
||||
|
||||
void CHyprDwindleLayout::recalculateWindow(PHLWINDOW pWindow) {
|
||||
|
@ -957,13 +924,11 @@ void CHyprDwindleLayout::switchWindows(PHLWINDOW pWindow, PHLWINDOW pWindow2) {
|
|||
if (!PNODE2 || !PNODE)
|
||||
return;
|
||||
|
||||
const bool FS1 = pWindow->m_bIsFullscreen;
|
||||
const bool FS2 = pWindow2->m_bIsFullscreen;
|
||||
const eFullscreenMode MODE1 = pWindow->m_sFullscreenState.internal;
|
||||
const eFullscreenMode MODE2 = pWindow2->m_sFullscreenState.internal;
|
||||
|
||||
if (FS1)
|
||||
g_pCompositor->setWindowFullscreen(pWindow, false);
|
||||
if (FS2)
|
||||
g_pCompositor->setWindowFullscreen(pWindow2, false);
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow2, FSMODE_NONE);
|
||||
|
||||
SDwindleNodeData* ACTIVE1 = nullptr;
|
||||
SDwindleNodeData* ACTIVE2 = nullptr;
|
||||
|
@ -1001,10 +966,8 @@ void CHyprDwindleLayout::switchWindows(PHLWINDOW pWindow, PHLWINDOW pWindow2) {
|
|||
g_pHyprRenderer->damageWindow(pWindow);
|
||||
g_pHyprRenderer->damageWindow(pWindow2);
|
||||
|
||||
if (FS1)
|
||||
g_pCompositor->setWindowFullscreen(pWindow2, true);
|
||||
if (FS2)
|
||||
g_pCompositor->setWindowFullscreen(pWindow, true);
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow2, MODE1);
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, MODE2);
|
||||
}
|
||||
|
||||
void CHyprDwindleLayout::alterSplitRatio(PHLWINDOW pWindow, float ratio, bool exact) {
|
||||
|
@ -1072,7 +1035,7 @@ void CHyprDwindleLayout::toggleSplit(PHLWINDOW pWindow) {
|
|||
if (!PNODE || !PNODE->pParent)
|
||||
return;
|
||||
|
||||
if (pWindow->m_bIsFullscreen)
|
||||
if (pWindow->isFullscreen())
|
||||
return;
|
||||
|
||||
PNODE->pParent->splitTop = !PNODE->pParent->splitTop;
|
||||
|
@ -1086,7 +1049,7 @@ void CHyprDwindleLayout::swapSplit(PHLWINDOW pWindow) {
|
|||
if (!PNODE || !PNODE->pParent)
|
||||
return;
|
||||
|
||||
if (pWindow->m_bIsFullscreen)
|
||||
if (pWindow->isFullscreen())
|
||||
return;
|
||||
|
||||
std::swap(PNODE->pParent->children[0], PNODE->pParent->children[1]);
|
||||
|
|
|
@ -52,7 +52,7 @@ class CHyprDwindleLayout : public IHyprLayout {
|
|||
virtual void recalculateWindow(PHLWINDOW);
|
||||
virtual void onBeginDragWindow();
|
||||
virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr);
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW, eFullscreenMode, bool);
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE);
|
||||
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
|
||||
virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW);
|
||||
virtual void switchWindows(PHLWINDOW, PHLWINDOW);
|
||||
|
|
|
@ -29,8 +29,8 @@ void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) {
|
|||
}
|
||||
|
||||
void IHyprLayout::onWindowRemoved(PHLWINDOW pWindow) {
|
||||
if (pWindow->m_bIsFullscreen)
|
||||
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
|
||||
if (pWindow->isFullscreen())
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
|
||||
|
||||
if (!pWindow->m_sGroupData.pNextWindow.expired()) {
|
||||
if (pWindow->m_sGroupData.pNextWindow.lock() == pWindow)
|
||||
|
@ -187,20 +187,20 @@ void IHyprLayout::onBeginDragWindow() {
|
|||
// Window will be floating. Let's check if it's valid. It should be, but I don't like crashing.
|
||||
if (!validMapped(DRAGGINGWINDOW)) {
|
||||
Debug::log(ERR, "Dragging attempted on an invalid window!");
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DRAGGINGWINDOW->m_bIsFullscreen) {
|
||||
if (DRAGGINGWINDOW->isFullscreen()) {
|
||||
Debug::log(LOG, "Dragging a fullscreen window");
|
||||
g_pCompositor->setWindowFullscreen(DRAGGINGWINDOW, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(DRAGGINGWINDOW, FSMODE_NONE);
|
||||
}
|
||||
|
||||
const auto PWORKSPACE = DRAGGINGWINDOW->m_pWorkspace;
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && (!DRAGGINGWINDOW->m_bCreatedOverFullscreen || !DRAGGINGWINDOW->m_bIsFloating)) {
|
||||
Debug::log(LOG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)");
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -288,7 +288,6 @@ void IHyprLayout::onEndDragWindow() {
|
|||
}
|
||||
|
||||
g_pInputManager->unsetCursorImage();
|
||||
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
g_pInputManager->m_bWasDraggingWindow = true;
|
||||
|
||||
|
@ -325,12 +324,14 @@ void IHyprLayout::onEndDragWindow() {
|
|||
}
|
||||
|
||||
void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
|
||||
if (g_pInputManager->currentlyDraggedWindow.expired())
|
||||
return;
|
||||
|
||||
const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock();
|
||||
|
||||
// Window invalid or drag begin size 0,0 meaning we rejected it.
|
||||
if (!validMapped(DRAGGINGWINDOW) || m_vBeginDragSizeXY == Vector2D()) {
|
||||
onEndDragWindow();
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
if ((!validMapped(DRAGGINGWINDOW) || m_vBeginDragSizeXY == Vector2D())) {
|
||||
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -474,9 +475,9 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
|
|||
|
||||
void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) {
|
||||
|
||||
if (pWindow->m_bIsFullscreen) {
|
||||
if (pWindow->isFullscreen()) {
|
||||
Debug::log(LOG, "changeWindowFloatingMode: fullscreen");
|
||||
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
|
||||
}
|
||||
|
||||
pWindow->m_bPinned = false;
|
||||
|
@ -496,7 +497,7 @@ void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) {
|
|||
const auto PWORKSPACE = PNEWMON->activeSpecialWorkspace ? PNEWMON->activeSpecialWorkspace : PNEWMON->activeWorkspace;
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow)
|
||||
g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID), false);
|
||||
g_pCompositor->setWindowFullscreenInternal(g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID), FSMODE_NONE);
|
||||
|
||||
// save real pos cuz the func applies the default 5,5 mid
|
||||
const auto PSAVEDPOS = pWindow->m_vRealPosition.goal();
|
||||
|
|
|
@ -110,7 +110,7 @@ class IHyprLayout {
|
|||
The layout sets all the fullscreen flags.
|
||||
It can either accept or ignore.
|
||||
*/
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW, eFullscreenMode, bool) = 0;
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) = 0;
|
||||
|
||||
/*
|
||||
Called when a dispatcher requests a custom message
|
||||
|
|
|
@ -267,7 +267,8 @@ void CHyprMasterLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
|
|||
pWindow->unsetWindowData(PRIORITY_LAYOUT);
|
||||
pWindow->updateWindowData();
|
||||
|
||||
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
|
||||
if (pWindow->isFullscreen())
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
|
||||
|
||||
if (PNODE->isMaster && (MASTERSLEFT <= 1 || *SMALLSPLIT == 1)) {
|
||||
// find a new master from top of the list
|
||||
|
@ -327,10 +328,10 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) {
|
|||
// massive hack from the fullscreen func
|
||||
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(pWorkspace->m_iID);
|
||||
|
||||
if (pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL) {
|
||||
if (pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
|
||||
PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition;
|
||||
PFULLWINDOW->m_vRealSize = PMONITOR->vecSize;
|
||||
} else if (pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
|
||||
} else if (pWorkspace->m_efFullscreenMode == FSMODE_MAXIMIZED) {
|
||||
SMasterNodeData fakeNode;
|
||||
fakeNode.pWindow = PFULLWINDOW;
|
||||
fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
|
||||
|
@ -644,7 +645,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
|
|||
// if user specified them in config
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(PWINDOW->m_pWorkspace);
|
||||
|
||||
if (PWINDOW->m_bIsFullscreen && !pNode->ignoreFullscreenChecks)
|
||||
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
|
||||
return;
|
||||
|
||||
PWINDOW->unsetWindowData(PRIORITY_LAYOUT);
|
||||
|
@ -668,8 +669,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
|
|||
PWINDOW->m_vSize = pNode->size;
|
||||
PWINDOW->m_vPosition = pNode->position;
|
||||
|
||||
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() &&
|
||||
(getNodesOnWorkspace(PWINDOW->workspaceID()) == 1 || (PWINDOW->m_bIsFullscreen && PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) {
|
||||
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() && (getNodesOnWorkspace(PWINDOW->workspaceID()) == 1 || PWINDOW->isEffectiveInternalFSMode(FSMODE_MAXIMIZED))) {
|
||||
|
||||
PWINDOW->m_sWindowData.decorate = CWindowOverridableVar(true, PRIORITY_LAYOUT);
|
||||
PWINDOW->m_sWindowData.noBorder = CWindowOverridableVar(*PNOGAPSWHENONLY != 2, PRIORITY_LAYOUT);
|
||||
|
@ -688,6 +688,8 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
|
|||
return;
|
||||
}
|
||||
|
||||
PWINDOW->updateWindowDecos();
|
||||
|
||||
auto calcPos = PWINDOW->m_vPosition;
|
||||
auto calcSize = PWINDOW->m_vSize;
|
||||
|
||||
|
@ -702,7 +704,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
|
|||
calcPos = calcPos + RESERVED.topLeft;
|
||||
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
||||
|
||||
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->m_bIsFullscreen) {
|
||||
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) {
|
||||
static auto PSCALEFACTOR = CConfigValue<Hyprlang::FLOAT>("master:special_scale_factor");
|
||||
|
||||
CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR};
|
||||
|
@ -730,8 +732,6 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
|
|||
|
||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||
}
|
||||
|
||||
PWINDOW->updateWindowDecos();
|
||||
}
|
||||
|
||||
bool CHyprMasterLayout::isWindowTiled(PHLWINDOW pWindow) {
|
||||
|
@ -880,41 +880,19 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne
|
|||
m_bForceWarps = false;
|
||||
}
|
||||
|
||||
void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscreenMode fullscreenMode, bool on) {
|
||||
if (!validMapped(pWindow))
|
||||
return;
|
||||
|
||||
if (on == pWindow->m_bIsFullscreen)
|
||||
return; // ignore
|
||||
|
||||
void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) {
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
||||
const auto PWORKSPACE = pWindow->m_pWorkspace;
|
||||
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
|
||||
// if the window wants to be fullscreen but there already is one,
|
||||
// ignore the request.
|
||||
return;
|
||||
}
|
||||
|
||||
// save position and size if floating
|
||||
if (pWindow->m_bIsFloating && on) {
|
||||
if (pWindow->m_bIsFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE) {
|
||||
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goal();
|
||||
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goal();
|
||||
pWindow->m_vPosition = pWindow->m_vRealPosition.goal();
|
||||
pWindow->m_vSize = pWindow->m_vRealSize.goal();
|
||||
}
|
||||
|
||||
// otherwise, accept it.
|
||||
pWindow->m_bIsFullscreen = on;
|
||||
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
|
||||
|
||||
pWindow->updateDynamicRules();
|
||||
pWindow->updateWindowDecos();
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)});
|
||||
EMIT_HOOK_EVENT("fullscreen", pWindow);
|
||||
|
||||
if (!pWindow->m_bIsFullscreen) {
|
||||
if (EFFECTIVE_MODE == FSMODE_NONE) {
|
||||
// if it got its fullscreen disabled, set back its node if it had one
|
||||
const auto PNODE = getNodeFromWindow(pWindow);
|
||||
if (PNODE)
|
||||
|
@ -928,12 +906,8 @@ void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscree
|
|||
pWindow->updateWindowData();
|
||||
}
|
||||
} else {
|
||||
// if it now got fullscreen, make it fullscreen
|
||||
|
||||
PWORKSPACE->m_efFullscreenMode = fullscreenMode;
|
||||
|
||||
// apply new pos and size being monitors' box
|
||||
if (fullscreenMode == FULLSCREEN_FULL) {
|
||||
if (EFFECTIVE_MODE == FSMODE_FULLSCREEN) {
|
||||
pWindow->m_vRealPosition = PMONITOR->vecPosition;
|
||||
pWindow->m_vRealSize = PMONITOR->vecSize;
|
||||
} else {
|
||||
|
@ -954,13 +928,7 @@ void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscree
|
|||
}
|
||||
}
|
||||
|
||||
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
|
||||
|
||||
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goal());
|
||||
|
||||
g_pCompositor->changeWindowZOrder(pWindow, true);
|
||||
|
||||
recalculateMonitor(PMONITOR->ID);
|
||||
}
|
||||
|
||||
void CHyprMasterLayout::recalculateWindow(PHLWINDOW pWindow) {
|
||||
|
@ -1081,14 +1049,14 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
|
|||
if (!validMapped(PWINDOWTOCHANGETO))
|
||||
return;
|
||||
|
||||
if (header.pWindow->m_bIsFullscreen) {
|
||||
if (header.pWindow->isFullscreen()) {
|
||||
const auto PWORKSPACE = header.pWindow->m_pWorkspace;
|
||||
const auto FSMODE = PWORKSPACE->m_efFullscreenMode;
|
||||
const auto FSMODE = header.pWindow->m_sFullscreenState.internal;
|
||||
static auto INHERITFULLSCREEN = CConfigValue<Hyprlang::INT>("master:inherit_fullscreen");
|
||||
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
|
||||
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
|
||||
if (*INHERITFULLSCREEN)
|
||||
g_pCompositor->setWindowFullscreen(PWINDOWTOCHANGETO, true, FSMODE);
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOWTOCHANGETO, FSMODE);
|
||||
} else {
|
||||
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
|
||||
g_pCompositor->warpCursorTo(PWINDOWTOCHANGETO->middle());
|
||||
|
@ -1208,7 +1176,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
|
|||
const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, true);
|
||||
|
||||
if (PWINDOWTOSWAPWITH) {
|
||||
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
|
||||
switchWindows(header.pWindow, PWINDOWTOSWAPWITH);
|
||||
switchToWindow(header.pWindow);
|
||||
}
|
||||
|
@ -1224,7 +1192,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
|
|||
const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, false);
|
||||
|
||||
if (PWINDOWTOSWAPWITH) {
|
||||
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenClient(header.pWindow, FSMODE_NONE);
|
||||
switchWindows(header.pWindow, PWINDOWTOSWAPWITH);
|
||||
switchToWindow(header.pWindow);
|
||||
}
|
||||
|
@ -1243,7 +1211,8 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
|
|||
|
||||
if (MASTERS + 2 > WINDOWS && *SMALLSPLIT == 0)
|
||||
return 0;
|
||||
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
|
||||
|
||||
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
|
||||
|
||||
if (!PNODE || PNODE->isMaster) {
|
||||
// first non-master node
|
||||
|
@ -1275,7 +1244,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
|
|||
if (WINDOWS < 2 || MASTERS < 2)
|
||||
return 0;
|
||||
|
||||
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
|
||||
|
||||
if (!PNODE || !PNODE->isMaster) {
|
||||
// first non-master node
|
||||
|
@ -1296,7 +1265,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
|
|||
if (!PWINDOW)
|
||||
return 0;
|
||||
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
|
||||
|
||||
const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->workspaceID());
|
||||
|
||||
|
@ -1392,7 +1361,7 @@ void CHyprMasterLayout::runOrientationCycle(SLayoutMessageHeader& header, CVarLi
|
|||
if (!PWINDOW)
|
||||
return;
|
||||
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
|
||||
|
||||
const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->workspaceID());
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ class CHyprMasterLayout : public IHyprLayout {
|
|||
virtual void recalculateMonitor(const int&);
|
||||
virtual void recalculateWindow(PHLWINDOW);
|
||||
virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr);
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW, eFullscreenMode, bool);
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE);
|
||||
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
|
||||
virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW);
|
||||
virtual void switchWindows(PHLWINDOW, PHLWINDOW);
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -147,7 +147,7 @@ int main(int argc, char** argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
g_pCompositor->initServer();
|
||||
g_pCompositor->initServer(socketName, socketFd);
|
||||
|
||||
if (!envEnabled("HYPRLAND_NO_RT"))
|
||||
Init::gainRealTime();
|
||||
|
@ -155,13 +155,11 @@ int main(int argc, char** argv) {
|
|||
Debug::log(LOG, "Hyprland init finished.");
|
||||
|
||||
// If all's good to go, start.
|
||||
g_pCompositor->startCompositor(socketName, socketFd);
|
||||
g_pCompositor->startCompositor();
|
||||
|
||||
g_pCompositor->m_bIsShuttingDown = true;
|
||||
g_pCompositor->cleanup();
|
||||
|
||||
// If we are here it means we got yote.
|
||||
Debug::log(LOG, "Hyprland reached the end.");
|
||||
g_pCompositor.reset();
|
||||
Debug::log(LOG, "Hyprland has reached the end.");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ CCursorManager::CCursorManager() {
|
|||
if (m_iSize == 0)
|
||||
m_iSize = 24;
|
||||
|
||||
xcursor.loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "", m_iSize * std::ceil(m_fCursorScale));
|
||||
xcursor.loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "default", m_iSize * std::ceil(m_fCursorScale));
|
||||
|
||||
m_pAnimationTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ::cursorAnimTimer, nullptr);
|
||||
|
||||
|
@ -135,14 +135,20 @@ void CCursorManager::setXCursor(const std::string& name) {
|
|||
|
||||
if (!xcursor.themeLoaded) {
|
||||
Debug::log(ERR, "XCursor failed to find theme in setXCursor");
|
||||
g_pPointerManager->resetCursorImage();
|
||||
return;
|
||||
if (!xcursor.defaultCursor) {
|
||||
g_pPointerManager->resetCursorImage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto& icon = xcursor.defaultCursor;
|
||||
// try to get an icon we know if we have one
|
||||
if (xcursor.cursors.contains(name))
|
||||
icon = xcursor.cursors.at(name);
|
||||
for (auto const& c : xcursor.cursors) {
|
||||
if (c.first != name)
|
||||
continue;
|
||||
|
||||
icon = c.second;
|
||||
}
|
||||
|
||||
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>((uint8_t*)icon->pixels.data(), icon->size, icon->hotspot));
|
||||
|
||||
|
@ -305,87 +311,86 @@ bool CCursorManager::changeTheme(const std::string& name, const int size) {
|
|||
}
|
||||
|
||||
// Taken from https://gitlab.freedesktop.org/xorg/lib/libxcursor/-/blob/master/src/library.c
|
||||
// however modified to fit wayland cursor shape names better.
|
||||
// _ -> -
|
||||
// clang-format off
|
||||
static std::array<const char*, 77> XCURSOR_STANDARD_NAMES = {
|
||||
"X_cursor",
|
||||
"default", // arrow
|
||||
"ns-resize", // based-arrow-down
|
||||
"ns-resize", // based-arrow-up
|
||||
"arrow",
|
||||
"based_arrow_down",
|
||||
"based_arrow_up",
|
||||
"boat",
|
||||
"bogosity",
|
||||
"sw-resize", // bottom-left-corner
|
||||
"se-resize", // bottom-right-corner
|
||||
"s-resize", // bottom-side
|
||||
"bottom-tee",
|
||||
"box-spiral",
|
||||
"center-ptr",
|
||||
"bottom_left_corner",
|
||||
"bottom_right_corner",
|
||||
"bottom_side",
|
||||
"bottom_tee",
|
||||
"box_spiral",
|
||||
"center_ptr",
|
||||
"circle",
|
||||
"clock",
|
||||
"coffee-mug",
|
||||
"coffee_mug",
|
||||
"cross",
|
||||
"cross-reverse",
|
||||
"cross_reverse",
|
||||
"crosshair",
|
||||
"diamond-cross",
|
||||
"diamond_cross",
|
||||
"dot",
|
||||
"dotbox",
|
||||
"double-arrow",
|
||||
"draft-large",
|
||||
"draft-small",
|
||||
"draped-box",
|
||||
"double_arrow",
|
||||
"draft_large",
|
||||
"draft_small",
|
||||
"draped_box",
|
||||
"exchange",
|
||||
"move", // fleur
|
||||
"fleur",
|
||||
"gobbler",
|
||||
"gumby",
|
||||
"pointer", // hand1
|
||||
"grabbing", // hand2
|
||||
"hand1",
|
||||
"hand2",
|
||||
"heart",
|
||||
"icon",
|
||||
"iron-cross",
|
||||
"default", // left-ptr
|
||||
"w-resize", // left-side
|
||||
"left-tee",
|
||||
"iron_cross",
|
||||
"left_ptr",
|
||||
"left_side",
|
||||
"left_tee",
|
||||
"leftbutton",
|
||||
"ll-angle",
|
||||
"lr-angle",
|
||||
"ll_angle",
|
||||
"lr_angle",
|
||||
"man",
|
||||
"middlebutton",
|
||||
"mouse",
|
||||
"pencil",
|
||||
"pirate",
|
||||
"plus",
|
||||
"help", // question-arrow
|
||||
"right-ptr",
|
||||
"e-resize", // right-side
|
||||
"right-tee",
|
||||
"question_arrow",
|
||||
"right_ptr",
|
||||
"right_side",
|
||||
"right_tee",
|
||||
"rightbutton",
|
||||
"rtl-logo",
|
||||
"rtl_logo",
|
||||
"sailboat",
|
||||
"ns-resize", // sb-down-arrow
|
||||
"ew-resize", // sb-h-double-arrow
|
||||
"ew-resize", // sb-left-arrow
|
||||
"ew-resize", // sb-right-arrow
|
||||
"n-resize", // sb-up-arrow
|
||||
"s-resize", // sb-v-double-arrow
|
||||
"sb_down_arrow",
|
||||
"sb_h_double_arrow",
|
||||
"sb_left_arrow",
|
||||
"sb_right_arrow",
|
||||
"sb_up_arrow",
|
||||
"sb_v_double_arrow",
|
||||
"shuttle",
|
||||
"sizing",
|
||||
"spider",
|
||||
"spraycan",
|
||||
"star",
|
||||
"target",
|
||||
"cell", // tcross
|
||||
"nw-resize", // top-left-arrow
|
||||
"nw-resize", // top-left-corner
|
||||
"ne-resize", // top-right-corner
|
||||
"n-resize", // top-side
|
||||
"top-tee",
|
||||
"tcross",
|
||||
"top_left_arrow",
|
||||
"top_left_corner",
|
||||
"top_right_corner",
|
||||
"top_side",
|
||||
"top_tee",
|
||||
"trek",
|
||||
"ul-angle",
|
||||
"ul_angle",
|
||||
"umbrella",
|
||||
"ur-angle",
|
||||
"wait", // watch
|
||||
"text", // xterm
|
||||
"ur_angle",
|
||||
"watch",
|
||||
"xterm",
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
@ -397,51 +402,21 @@ void CCursorManager::SXCursorManager::loadTheme(const std::string& name, int siz
|
|||
themeLoaded = false;
|
||||
themeName = name.empty() ? "default" : name;
|
||||
|
||||
auto img = XcursorShapeLoadImage(2, themeName.c_str(), size);
|
||||
std::vector<std::pair<std::string, SP<SXCursor>>> newCursors;
|
||||
|
||||
if (!img) {
|
||||
Debug::log(ERR, "XCursor failed finding theme \"{}\". Trying size 24.", themeName);
|
||||
size = 24;
|
||||
img = XcursorShapeLoadImage(2, themeName.c_str(), size);
|
||||
if (!img) {
|
||||
Debug::log(ERR, "XCursor failed finding theme \"{}\".", themeName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
defaultCursor = makeShared<SXCursor>();
|
||||
defaultCursor->size = {(int)img->width, (int)img->height};
|
||||
defaultCursor->hotspot = {(int)img->xhot, (int)img->yhot};
|
||||
|
||||
defaultCursor->pixels.resize(img->width * img->height);
|
||||
std::memcpy(defaultCursor->pixels.data(), img->pixels, img->width * img->height * sizeof(uint32_t));
|
||||
|
||||
themeLoaded = true;
|
||||
|
||||
XcursorImageDestroy(img);
|
||||
|
||||
// gather as many shapes as we can find.
|
||||
cursors.clear();
|
||||
|
||||
for (auto& shape : CURSOR_SHAPE_NAMES) {
|
||||
int id = -1;
|
||||
for (size_t i = 0; i < XCURSOR_STANDARD_NAMES.size(); ++i) {
|
||||
if (XCURSOR_STANDARD_NAMES.at(i) == std::string{shape}) {
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id < 0) {
|
||||
Debug::log(LOG, "XCursor has no shape {}, skipping", shape);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto xImage = XcursorShapeLoadImage(id << 1 /* wtf xcursor? */, themeName.c_str(), size);
|
||||
// load the default xcursor shapes that exist in the theme
|
||||
for (size_t i = 0; i < XCURSOR_STANDARD_NAMES.size(); ++i) {
|
||||
auto shape = XCURSOR_STANDARD_NAMES.at(i);
|
||||
auto xImage = XcursorShapeLoadImage(i << 1 /* wtf xcursor? */, themeName.c_str(), size);
|
||||
|
||||
if (!xImage) {
|
||||
Debug::log(LOG, "XCursor failed to find a shape with name {}, skipping", shape);
|
||||
continue;
|
||||
Debug::log(LOG, "XCursor failed to find a shape with name {}, trying size 24.", shape);
|
||||
xImage = XcursorShapeLoadImage(i << 1 /* wtf xcursor? */, themeName.c_str(), 24);
|
||||
|
||||
if (!xImage) {
|
||||
Debug::log(LOG, "XCursor failed to find a shape with name {}, skipping", shape);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto xcursor = makeShared<SXCursor>();
|
||||
|
@ -451,8 +426,214 @@ void CCursorManager::SXCursorManager::loadTheme(const std::string& name, int siz
|
|||
xcursor->pixels.resize(xImage->width * xImage->height);
|
||||
std::memcpy(xcursor->pixels.data(), xImage->pixels, xImage->width * xImage->height * sizeof(uint32_t));
|
||||
|
||||
cursors.emplace(std::string{shape}, xcursor);
|
||||
newCursors.emplace_back(std::string{shape}, xcursor);
|
||||
|
||||
XcursorImageDestroy(xImage);
|
||||
}
|
||||
|
||||
if (newCursors.empty()) {
|
||||
Debug::log(ERR, "XCursor failed finding any shapes in theme \"{}\".", themeName);
|
||||
if (!defaultCursor) {
|
||||
defaultCursor = createDefaultCursor();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cursors.clear();
|
||||
cursors = std::move(newCursors);
|
||||
defaultCursor.reset();
|
||||
themeLoaded = true;
|
||||
|
||||
for (auto const& shape : CURSOR_SHAPE_NAMES) {
|
||||
auto legacyName = getLegacyShapeName(shape);
|
||||
if (legacyName.empty())
|
||||
continue;
|
||||
|
||||
auto it = std::find_if(cursors.begin(), cursors.end(), [&legacyName](auto const& c) { return c.first == legacyName; });
|
||||
|
||||
if (it == cursors.end()) {
|
||||
Debug::log(LOG, "XCursor failed to find a legacy shape with name {}, skipping", legacyName);
|
||||
continue;
|
||||
}
|
||||
|
||||
cursors.emplace_back(shape, it->second);
|
||||
}
|
||||
|
||||
// set default cursor
|
||||
for (auto const& c : cursors) {
|
||||
if (c.first == "left_ptr" || c.first == "arrow") {
|
||||
defaultCursor = c.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// just assign the first one then.
|
||||
if (!defaultCursor)
|
||||
defaultCursor = cursors.at(0).second;
|
||||
}
|
||||
|
||||
std::string CCursorManager::SXCursorManager::getLegacyShapeName(std::string const& shape) {
|
||||
if (shape == "invalid")
|
||||
return std::string();
|
||||
else if (shape == "default")
|
||||
return "left_ptr";
|
||||
else if (shape == "context-menu")
|
||||
return "left_ptr";
|
||||
else if (shape == "help")
|
||||
return "left_ptr";
|
||||
else if (shape == "pointer")
|
||||
return "hand2";
|
||||
else if (shape == "progress")
|
||||
return "watch";
|
||||
else if (shape == "wait")
|
||||
return "watch";
|
||||
else if (shape == "cell")
|
||||
return "plus";
|
||||
else if (shape == "crosshair")
|
||||
return "cross";
|
||||
else if (shape == "text")
|
||||
return "xterm";
|
||||
else if (shape == "vertical-text")
|
||||
return "xterm";
|
||||
else if (shape == "alias")
|
||||
return "dnd-link";
|
||||
else if (shape == "copy")
|
||||
return "dnd-copy";
|
||||
else if (shape == "move")
|
||||
return "dnd-move";
|
||||
else if (shape == "no-drop")
|
||||
return "dnd-none";
|
||||
else if (shape == "not-allowed")
|
||||
return "crossed_circle";
|
||||
else if (shape == "grab")
|
||||
return "hand1";
|
||||
else if (shape == "grabbing")
|
||||
return "hand1";
|
||||
else if (shape == "e-resize")
|
||||
return "right_side";
|
||||
else if (shape == "n-resize")
|
||||
return "top_side";
|
||||
else if (shape == "ne-resize")
|
||||
return "top_right_corner";
|
||||
else if (shape == "nw-resize")
|
||||
return "top_left_corner";
|
||||
else if (shape == "s-resize")
|
||||
return "bottom_side";
|
||||
else if (shape == "se-resize")
|
||||
return "bottom_right_corner";
|
||||
else if (shape == "sw-resize")
|
||||
return "bottom_left_corner";
|
||||
else if (shape == "w-resize")
|
||||
return "left_side";
|
||||
else if (shape == "ew-resize")
|
||||
return "sb_h_double_arrow";
|
||||
else if (shape == "ns-resize")
|
||||
return "sb_v_double_arrow";
|
||||
else if (shape == "nesw-resize")
|
||||
return "fd_double_arrow";
|
||||
else if (shape == "nwse-resize")
|
||||
return "bd_double_arrow";
|
||||
else if (shape == "col-resize")
|
||||
return "sb_h_double_arrow";
|
||||
else if (shape == "row-resize")
|
||||
return "sb_v_double_arrow";
|
||||
else if (shape == "all-scroll")
|
||||
return "fleur";
|
||||
else if (shape == "zoom-in")
|
||||
return "left_ptr";
|
||||
else if (shape == "zoom-out")
|
||||
return "left_ptr";
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
SP<CCursorManager::SXCursor> CCursorManager::SXCursorManager::createDefaultCursor() {
|
||||
int cursorWidth = 32;
|
||||
int cursorHeight = 32;
|
||||
int cursorHotspotX = 3;
|
||||
int cursorHotspotY = 2;
|
||||
|
||||
static const uint32_t pixels[] = {
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1b001816, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8e008173, 0x5f00564d, 0x16001412, 0x09000807, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2b002624, 0x05000404, 0x00000000, 0x35002f2b, 0xd400bead,
|
||||
0xc300b09e, 0x90008275, 0x44003e37, 0x04000403, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x67005a56,
|
||||
0x6f00615c, 0x00000000, 0x00000000, 0x8b007c72, 0xf200d7c6, 0xfa00e0cc, 0xe800d0bd, 0xa0009181, 0x44003e37, 0x1a001815, 0x06000505, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x8d007976, 0xd600b8b3, 0x2500201f, 0x00000000, 0x17001413, 0xbd00a79c, 0xf600dacb, 0xff00e3d1, 0xfc00e1ce, 0xe800d0bc, 0xbf00ac9b,
|
||||
0x95008778, 0x51004a41, 0x0f000e0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x92007b7b, 0xf500d0cf, 0x9e008685, 0x00000000, 0x00000000, 0x23001f1d, 0x64005853,
|
||||
0x9b008980, 0xd900bfb3, 0xfb00dfce, 0xff00e4d0, 0xfb00e1cd, 0xec00d5c0, 0xa7009788, 0x47004139, 0x1e001b18, 0x05000504, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xa200878a, 0xff00d6d9, 0xd600b4b5,
|
||||
0x0e000c0c, 0x00000000, 0x00000000, 0x02000202, 0x0c000b0a, 0x30002a28, 0x8e007d75, 0xd600bdb0, 0xef00d4c4, 0xfb00e0ce, 0xff00e4d0, 0xe600cfbb, 0xb800a695, 0x5f00564d,
|
||||
0x06000505, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x02000202, 0xc600a3aa, 0xff00d3da, 0xea00c3c8, 0x08000707, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000101, 0x2a002523, 0x61005550, 0x9500837b,
|
||||
0xd800bfb1, 0xfd00e1cf, 0xff00e5d0, 0xf500dcc7, 0x7c007065, 0x2a002622, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x06000505, 0xd600aeb9, 0xff00d0dc, 0xcc00a7af, 0x04000303, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x01000101, 0x01000101, 0x2c002724, 0xa1008e85, 0xe600ccbd, 0xf800ddcb, 0xef00d6c3, 0xc300af9f, 0x2c002824, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x09000708, 0xd800adbc, 0xff00cdde, 0xb90095a0, 0x02000202, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000e0d, 0x4b00423e, 0xa4009088, 0xfd00dfd0, 0xff00e3d1,
|
||||
0xae009c8f, 0x42003b36, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x14001012, 0xf400c0d6,
|
||||
0xff00cadf, 0xb2008e9c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x02000202, 0x1200100f, 0xa2008e86, 0xec00cfc3, 0xfc00ded0, 0xc300ada0, 0x15001311, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x2e002429, 0xfd00c4e0, 0xff00c7e2, 0x8f00707e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1e001a19, 0x75006662, 0xfb00dbd1, 0xf700d9cc, 0x9600847c, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3e002f37, 0xfc00c1e1, 0xff00c5e3, 0x60004b55, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x15001212, 0xa6008f8b, 0xff00ddd5,
|
||||
0xf800d8ce, 0x36002f2d, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x51003d49, 0xfe00bfe5, 0xfe00c1e4, 0x4c003a44,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x01000101, 0x1d001918, 0xf400d1cd, 0xfe00dad5, 0xb3009a96, 0x03000303, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x66004b5d, 0xff00bee7, 0xfd00bee5, 0x4500343f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000202, 0x82006e6e, 0xff00d8d7, 0xd800b9b6, 0x33002c2b, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x70005267, 0xff00bbe9, 0xfa00b8e3, 0x29001e25, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3f003536, 0xea00c3c7, 0xf800d1d3,
|
||||
0x4a003e3f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5f004458, 0xff00b8eb, 0xf400b1e0, 0x29001e26, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x1b001617, 0xe100bac0, 0xff00d4da, 0x82006c6f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5a004054, 0xfe00b4eb,
|
||||
0xfb00b3e8, 0x3b002a36, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0a000809, 0xc900a4ad, 0xff00d1dc, 0x88007075, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x44002f3f, 0xf300aae3, 0xfc00b1ea, 0x48003343, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05000404, 0xdf00b3c2, 0xff00cedd, 0x8f00747c, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x25001a23, 0xf200a6e4, 0xff00b1ef, 0x84005c7c, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x09000708,
|
||||
0xe400b5c8, 0xff00cbdf, 0x78006068, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x12000c11, 0xc00082b6, 0xff00aef1, 0xaa0075a0,
|
||||
0x11000c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x1e00171b, 0xf700c1db, 0xff00c8e1, 0x4b003b42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x05000305, 0x8d005f86, 0xfc00aaef, 0xed00a0e1, 0x26001a24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6f005463, 0xff00c4e3, 0xf500beda, 0x0c00090b, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3e00293b, 0xed009de2, 0xff00aaf3, 0x8b005d84, 0x04000304, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000506, 0xeb00b1d4, 0xff00c1e6, 0xba008ea7,
|
||||
0x02000202, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xb20075ab, 0xff00a7f4, 0xf300a1e9, 0x35002333,
|
||||
0x06000406, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x8900647d, 0xff00bde8, 0xfb00bce2, 0x26001d22, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x2b001c29, 0xf1009ce8, 0xfe00a5f4, 0xc60082be, 0x3b002738, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x01000101, 0x54003c4d, 0xfd00b8e8, 0xff00baea, 0x96006f89, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x78004d74, 0xe20091da, 0xff00a5f6, 0xb60077af, 0x42002b3f, 0x0c00080c, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x9400688a, 0xff00b5ed, 0xff00b6ec, 0xcc0093bc, 0x02000102, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000304, 0x8100527d, 0xeb0096e4, 0xf900a0f1, 0xdc008dd4,
|
||||
0x9b006595, 0x32002130, 0x0a00070a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2100161f, 0x5300394e, 0xe2009cd4, 0xff00b1ef, 0xff00b2ee, 0xc8008cba, 0x17001015,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x19001018, 0x5e003b5b, 0xcb0081c5, 0xff00a2f8, 0xfc00a1f4, 0xde0090d6, 0xa9006da3, 0x8300567e, 0x6f00496a, 0x76004e71, 0xbb007db2, 0xeb009edf, 0xfb00a9ee, 0xff00adf1,
|
||||
0xfd00adee, 0x9e006d95, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000304, 0x34002133, 0xa60069a1, 0xd70089d1, 0xf2009bea, 0xff00a3f7, 0xfb00a1f2, 0xfb00a2f2, 0xff00a6f5,
|
||||
0xff00a8f4, 0xfd00a7f2, 0xed009ee2, 0xcf008bc5, 0x14000e13, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000b11, 0x35002134, 0x5b003959,
|
||||
0x8b005887, 0xb30072ae, 0xc90080c3, 0xd10086ca, 0xa6006ba0, 0x65004261, 0x3c002839, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x01000101, 0x06000406, 0x0d00080d, 0x0f000a0f, 0x0a00060a, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000};
|
||||
|
||||
auto hackcursor = makeShared<SXCursor>();
|
||||
hackcursor->size = {cursorWidth, cursorHeight};
|
||||
hackcursor->hotspot = {cursorHotspotX, cursorHotspotY};
|
||||
hackcursor->pixels.resize(cursorWidth * cursorHeight);
|
||||
std::memcpy(hackcursor->pixels.data(), pixels, cursorWidth * cursorHeight * sizeof(uint32_t));
|
||||
|
||||
return hackcursor;
|
||||
}
|
||||
|
|
|
@ -83,14 +83,15 @@ class CCursorManager {
|
|||
};
|
||||
|
||||
struct SXCursorManager {
|
||||
void loadTheme(const std::string& name, int size);
|
||||
void loadTheme(const std::string& name, int size);
|
||||
std::string getLegacyShapeName(std::string const& shape);
|
||||
SP<SXCursor> createDefaultCursor();
|
||||
|
||||
int lastLoadSize = 0;
|
||||
|
||||
bool themeLoaded = false;
|
||||
std::string themeName = "";
|
||||
SP<SXCursor> defaultCursor;
|
||||
std::unordered_map<std::string, SP<SXCursor>> cursors;
|
||||
int lastLoadSize = 0;
|
||||
bool themeLoaded = false;
|
||||
std::string themeName = "";
|
||||
SP<SXCursor> defaultCursor;
|
||||
std::vector<std::pair<std::string, SP<SXCursor>>> cursors;
|
||||
} xcursor;
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../managers/SeatManager.hpp"
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../protocols/ShortcutsInhibit.hpp"
|
||||
#include "../protocols/GlobalShortcuts.hpp"
|
||||
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
|
||||
#include "../devices/IKeyboard.hpp"
|
||||
#include "KeybindManager.hpp"
|
||||
|
@ -36,7 +37,7 @@ using namespace Hyprutils::String;
|
|||
static std::vector<std::pair<std::string, std::string>> getHyprlandLaunchEnv() {
|
||||
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
|
||||
|
||||
if (!*PINITIALWSTRACKING)
|
||||
if (!*PINITIALWSTRACKING || g_pConfigManager->isLaunchingExecOnce)
|
||||
return {};
|
||||
|
||||
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
|
||||
|
@ -67,7 +68,7 @@ CKeybindManager::CKeybindManager() {
|
|||
m_mDispatchers["workspace"] = changeworkspace;
|
||||
m_mDispatchers["renameworkspace"] = renameWorkspace;
|
||||
m_mDispatchers["fullscreen"] = fullscreenActive;
|
||||
m_mDispatchers["fakefullscreen"] = fakeFullscreenActive;
|
||||
m_mDispatchers["fullscreenstate"] = fullscreenStateActive;
|
||||
m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace;
|
||||
m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent;
|
||||
m_mDispatchers["pseudo"] = toggleActivePseudo;
|
||||
|
@ -101,7 +102,6 @@ CKeybindManager::CKeybindManager() {
|
|||
m_mDispatchers["pass"] = pass;
|
||||
m_mDispatchers["sendshortcut"] = sendshortcut;
|
||||
m_mDispatchers["layoutmsg"] = layoutmsg;
|
||||
m_mDispatchers["toggleopaque"] = toggleOpaque;
|
||||
m_mDispatchers["dpms"] = dpms;
|
||||
m_mDispatchers["movewindowpixel"] = moveWindow;
|
||||
m_mDispatchers["resizewindowpixel"] = resizeWindow;
|
||||
|
@ -243,27 +243,14 @@ void CKeybindManager::updateXKBTranslationState() {
|
|||
}
|
||||
|
||||
bool CKeybindManager::ensureMouseBindState() {
|
||||
if (!m_bIsMouseBindActive)
|
||||
if (!g_pInputManager->currentlyDraggedWindow)
|
||||
return false;
|
||||
|
||||
if (!g_pInputManager->currentlyDraggedWindow.expired()) {
|
||||
PHLWINDOW lastDraggedWindow = g_pInputManager->currentlyDraggedWindow.lock();
|
||||
|
||||
m_bIsMouseBindActive = false;
|
||||
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
g_pInputManager->dragMode = MBIND_INVALID;
|
||||
|
||||
g_pCompositor->updateWorkspaceWindows(lastDraggedWindow->workspaceID());
|
||||
g_pCompositor->updateWorkspaceWindowData(lastDraggedWindow->workspaceID());
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(lastDraggedWindow->m_iMonitorID);
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
changeMouseBindMode(MBIND_INVALID);
|
||||
return true;
|
||||
}
|
||||
|
||||
m_bIsMouseBindActive = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -282,13 +269,18 @@ bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) {
|
|||
return false;
|
||||
|
||||
const auto LASTMONITOR = g_pCompositor->m_pLastMonitor.get();
|
||||
if (!LASTMONITOR)
|
||||
return false;
|
||||
if (LASTMONITOR == monitor) {
|
||||
Debug::log(LOG, "Tried to move to active monitor");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto PWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace;
|
||||
const auto PNEWMAINWORKSPACE = monitor->activeWorkspace;
|
||||
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
|
||||
static auto PNOWARPS = CConfigValue<Hyprlang::INT>("cursor:no_warps");
|
||||
|
||||
const auto PWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace;
|
||||
const auto PNEWMAINWORKSPACE = monitor->activeWorkspace;
|
||||
|
||||
g_pInputManager->unconstrainMouse();
|
||||
PNEWMAINWORKSPACE->rememberPrevWorkspace(PWORKSPACE);
|
||||
|
@ -301,9 +293,11 @@ bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) {
|
|||
g_pCompositor->focusWindow(PNEWWINDOW);
|
||||
PNEWWINDOW->warpCursor();
|
||||
|
||||
g_pInputManager->m_pForcedFocus = PNEWWINDOW;
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
g_pInputManager->m_pForcedFocus.reset();
|
||||
if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) {
|
||||
g_pInputManager->m_pForcedFocus = PNEWWINDOW;
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
g_pInputManager->m_pForcedFocus.reset();
|
||||
}
|
||||
} else {
|
||||
g_pCompositor->focusWindow(nullptr);
|
||||
g_pCompositor->warpCursorTo(monitor->middle());
|
||||
|
@ -314,7 +308,10 @@ bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) {
|
|||
}
|
||||
|
||||
void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO) {
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
|
||||
static auto PNOWARPS = CConfigValue<Hyprlang::INT>("cursor:no_warps");
|
||||
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
|
||||
if (PWINDOWTOCHANGETO == PLASTWINDOW || !PWINDOWTOCHANGETO)
|
||||
return;
|
||||
|
@ -322,25 +319,28 @@ void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO) {
|
|||
// remove constraints
|
||||
g_pInputManager->unconstrainMouse();
|
||||
|
||||
if (PLASTWINDOW && PLASTWINDOW->m_pWorkspace == PWINDOWTOCHANGETO->m_pWorkspace && PLASTWINDOW->m_bIsFullscreen) {
|
||||
if (PLASTWINDOW && PLASTWINDOW->m_pWorkspace == PWINDOWTOCHANGETO->m_pWorkspace && PLASTWINDOW->isFullscreen()) {
|
||||
const auto PWORKSPACE = PLASTWINDOW->m_pWorkspace;
|
||||
const auto FSMODE = PWORKSPACE->m_efFullscreenMode;
|
||||
const auto MODE = PWORKSPACE->m_efFullscreenMode;
|
||||
|
||||
if (!PWINDOWTOCHANGETO->m_bPinned)
|
||||
g_pCompositor->setWindowFullscreen(PLASTWINDOW, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenInternal(PLASTWINDOW, FSMODE_NONE);
|
||||
|
||||
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
|
||||
|
||||
if (!PWINDOWTOCHANGETO->m_bPinned)
|
||||
g_pCompositor->setWindowFullscreen(PWINDOWTOCHANGETO, true, FSMODE);
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOWTOCHANGETO, MODE);
|
||||
} else {
|
||||
updateRelativeCursorCoords();
|
||||
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
|
||||
PWINDOWTOCHANGETO->warpCursor();
|
||||
|
||||
g_pInputManager->m_pForcedFocus = PWINDOWTOCHANGETO;
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
g_pInputManager->m_pForcedFocus.reset();
|
||||
// Move mouse focus to the new window if required by current follow_mouse and warp modes
|
||||
if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) {
|
||||
g_pInputManager->m_pForcedFocus = PWINDOWTOCHANGETO;
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
g_pInputManager->m_pForcedFocus.reset();
|
||||
}
|
||||
|
||||
if (PLASTWINDOW && PLASTWINDOW->m_iMonitorID != PWINDOWTOCHANGETO->m_iMonitorID) {
|
||||
// event
|
||||
|
@ -528,10 +528,7 @@ bool CKeybindManager::onMouseEvent(const IPointer::SButtonEvent& e) {
|
|||
}
|
||||
|
||||
void CKeybindManager::resizeWithBorder(const IPointer::SButtonEvent& e) {
|
||||
if (e.state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
mouse("1resizewindow");
|
||||
else
|
||||
mouse("0resizewindow");
|
||||
changeMouseBindMode(e.state == WL_POINTER_BUTTON_STATE_PRESSED ? MBIND_RESIZE : MBIND_INVALID);
|
||||
}
|
||||
|
||||
void CKeybindManager::onSwitchEvent(const std::string& switchName) {
|
||||
|
@ -607,10 +604,8 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
|
|||
|
||||
static auto PDISABLEINHIBIT = CConfigValue<Hyprlang::INT>("binds:disable_keybind_grabbing");
|
||||
|
||||
if (!*PDISABLEINHIBIT && PROTO::shortcutsInhibit->isInhibited()) {
|
||||
if (!*PDISABLEINHIBIT && PROTO::shortcutsInhibit->isInhibited())
|
||||
Debug::log(LOG, "Keybind handling is disabled due to an inhibitor");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& k : m_lKeybinds) {
|
||||
const bool SPECIALDISPATCHER = k.handler == "global" || k.handler == "pass" || k.handler == "sendshortcut" || k.handler == "mouse";
|
||||
|
@ -619,6 +614,9 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
|
|||
const bool IGNORECONDITIONS =
|
||||
SPECIALDISPATCHER && !pressed && SPECIALTRIGGERED; // ignore mods. Pass, global dispatchers should be released immediately once the key is released.
|
||||
|
||||
if (!k.dontInhibit && !*PDISABLEINHIBIT && PROTO::shortcutsInhibit->isInhibited())
|
||||
continue;
|
||||
|
||||
if (!k.locked && g_pSessionLockManager->isSessionLocked())
|
||||
continue;
|
||||
|
||||
|
@ -958,7 +956,8 @@ static void toggleActiveFloatingCore(std::string args, std::optional<bool> float
|
|||
return;
|
||||
|
||||
// remove drag status
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
if (!g_pInputManager->currentlyDraggedWindow.expired())
|
||||
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
|
||||
|
||||
if (PWINDOW->m_sGroupData.pNextWindow.lock() && PWINDOW->m_sGroupData.pNextWindow.lock() != PWINDOW) {
|
||||
const auto PCURRENT = PWINDOW->getGroupCurrent();
|
||||
|
@ -997,7 +996,7 @@ void CKeybindManager::setActiveTiled(std::string args) {
|
|||
void CKeybindManager::centerWindow(std::string args) {
|
||||
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
|
||||
if (!PWINDOW || !PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
|
||||
if (!PWINDOW || !PWINDOW->m_bIsFloating || PWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
|
||||
|
@ -1023,7 +1022,7 @@ void CKeybindManager::toggleActivePseudo(std::string args) {
|
|||
|
||||
PWINDOW->m_bIsPseudotiled = !PWINDOW->m_bIsPseudotiled;
|
||||
|
||||
if (!PWINDOW->m_bIsFullscreen)
|
||||
if (!PWINDOW->isFullscreen())
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW);
|
||||
}
|
||||
|
||||
|
@ -1150,10 +1149,52 @@ void CKeybindManager::fullscreenActive(std::string args) {
|
|||
if (!PWINDOW)
|
||||
return;
|
||||
|
||||
PWINDOW->m_bDontSendFullscreen = false;
|
||||
if (args == "2")
|
||||
PWINDOW->m_bDontSendFullscreen = true;
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, args == "1" ? FULLSCREEN_MAXIMIZED : FULLSCREEN_FULL);
|
||||
const eFullscreenMode MODE = args == "1" ? FSMODE_MAXIMIZED : FSMODE_FULLSCREEN;
|
||||
|
||||
if (PWINDOW->isEffectiveInternalFSMode(MODE))
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
|
||||
else
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, MODE);
|
||||
}
|
||||
|
||||
void CKeybindManager::fullscreenStateActive(std::string args) {
|
||||
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
const auto ARGS = CVarList(args, 2, ' ');
|
||||
|
||||
if (!PWINDOW)
|
||||
return;
|
||||
|
||||
int internalMode, clientMode;
|
||||
try {
|
||||
internalMode = std::stoi(ARGS[0]);
|
||||
} catch (std::exception& e) { internalMode = -1; }
|
||||
try {
|
||||
clientMode = std::stoi(ARGS[1]);
|
||||
} catch (std::exception& e) { clientMode = -1; }
|
||||
|
||||
const sFullscreenState STATE = sFullscreenState{.internal = (internalMode != -1 ? (eFullscreenMode)internalMode : PWINDOW->m_sFullscreenState.internal),
|
||||
.client = (clientMode != -1 ? (eFullscreenMode)clientMode : PWINDOW->m_sFullscreenState.client)};
|
||||
|
||||
if (internalMode != -1 && clientMode != -1 && PWINDOW->m_sFullscreenState.internal == STATE.internal && PWINDOW->m_sFullscreenState.client == STATE.client) {
|
||||
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = FSMODE_NONE, .client = FSMODE_NONE});
|
||||
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(true, PRIORITY_SET_PROP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (internalMode != -1 && clientMode == -1 && PWINDOW->m_sFullscreenState.internal == STATE.internal) {
|
||||
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = PWINDOW->m_sFullscreenState.client, .client = PWINDOW->m_sFullscreenState.client});
|
||||
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(true, PRIORITY_SET_PROP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (internalMode == -1 && clientMode != -1 && PWINDOW->m_sFullscreenState.client == STATE.client) {
|
||||
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = PWINDOW->m_sFullscreenState.internal, .client = PWINDOW->m_sFullscreenState.internal});
|
||||
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(true, PRIORITY_SET_PROP);
|
||||
return;
|
||||
}
|
||||
|
||||
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_SET_PROP);
|
||||
g_pCompositor->setWindowFullscreenState(PWINDOW, STATE);
|
||||
}
|
||||
|
||||
void CKeybindManager::moveActiveToWorkspace(std::string args) {
|
||||
|
@ -1278,7 +1319,7 @@ void CKeybindManager::moveFocusTo(std::string args) {
|
|||
return;
|
||||
}
|
||||
|
||||
const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->m_bIsFullscreen ?
|
||||
const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->isFullscreen() ?
|
||||
(arg == 'd' || arg == 'b' || arg == 'r' ? g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, true) : g_pCompositor->getPrevWindowOnWorkspace(PLASTWINDOW, true)) :
|
||||
g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
|
||||
|
||||
|
@ -1335,7 +1376,7 @@ void CKeybindManager::swapActive(std::string args) {
|
|||
|
||||
Debug::log(LOG, "Swapping active window in direction {}", arg);
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
|
||||
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
|
||||
|
@ -1373,7 +1414,7 @@ void CKeybindManager::moveActiveTo(std::string args) {
|
|||
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
|
||||
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
|
||||
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
if (PLASTWINDOW->m_bIsFloating) {
|
||||
|
@ -1428,7 +1469,8 @@ void CKeybindManager::toggleGroup(std::string args) {
|
|||
if (!PWINDOW)
|
||||
return;
|
||||
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
|
||||
if (PWINDOW->isFullscreen())
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
|
||||
|
||||
if (PWINDOW->m_sGroupData.pNextWindow.expired())
|
||||
PWINDOW->createGroup();
|
||||
|
@ -1662,7 +1704,7 @@ void CKeybindManager::renameWorkspace(std::string args) {
|
|||
}
|
||||
|
||||
void CKeybindManager::exitHyprland(std::string argz) {
|
||||
g_pEventLoopManager->doLater([]() { g_pCompositor->cleanup(); });
|
||||
g_pCompositor->stopCompositor();
|
||||
}
|
||||
|
||||
void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) {
|
||||
|
@ -1820,7 +1862,7 @@ void CKeybindManager::forceRendererReload(std::string args) {
|
|||
void CKeybindManager::resizeActive(std::string args) {
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
|
||||
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
|
||||
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealSize.goal());
|
||||
|
@ -1837,7 +1879,7 @@ void CKeybindManager::resizeActive(std::string args) {
|
|||
void CKeybindManager::moveActive(std::string args) {
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
|
||||
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
|
||||
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealPosition.goal());
|
||||
|
@ -1857,7 +1899,7 @@ void CKeybindManager::moveWindow(std::string args) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (PWINDOW->m_bIsFullscreen)
|
||||
if (PWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealPosition.goal());
|
||||
|
@ -1877,7 +1919,7 @@ void CKeybindManager::resizeWindow(std::string args) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (PWINDOW->m_bIsFullscreen)
|
||||
if (PWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealSize.goal());
|
||||
|
@ -1954,12 +1996,12 @@ void CKeybindManager::focusWindow(std::string regexp) {
|
|||
g_pCompositor->focusWindow(PWINDOW);
|
||||
} else {
|
||||
if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned)
|
||||
g_pCompositor->setWindowFullscreen(FSWINDOW, false, FULLSCREEN_FULL);
|
||||
g_pCompositor->setWindowFullscreenClient(FSWINDOW, FSMODE_NONE);
|
||||
|
||||
g_pCompositor->focusWindow(PWINDOW);
|
||||
|
||||
if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned)
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, true, FSMODE);
|
||||
g_pCompositor->setWindowFullscreenClient(PWINDOW, FSMODE);
|
||||
}
|
||||
} else
|
||||
g_pCompositor->focusWindow(PWINDOW);
|
||||
|
@ -2236,17 +2278,6 @@ void CKeybindManager::layoutmsg(std::string msg) {
|
|||
g_pLayoutManager->getCurrentLayout()->layoutMessage(hd, msg);
|
||||
}
|
||||
|
||||
void CKeybindManager::toggleOpaque(std::string unused) {
|
||||
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
|
||||
if (!PWINDOW)
|
||||
return;
|
||||
|
||||
PWINDOW->m_sWindowData.opaque = CWindowOverridableVar(!PWINDOW->m_sWindowData.opaque.valueOrDefault(), PRIORITY_SET_PROP);
|
||||
|
||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||
}
|
||||
|
||||
void CKeybindManager::dpms(std::string arg) {
|
||||
bool enable = arg.starts_with("on");
|
||||
std::string port = "";
|
||||
|
@ -2342,11 +2373,19 @@ void CKeybindManager::pinActive(std::string args) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
|
||||
if (!PWINDOW->m_bIsFloating || PWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
PWINDOW->m_bPinned = !PWINDOW->m_bPinned;
|
||||
PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->activeWorkspace;
|
||||
PWINDOW->m_bPinned = !PWINDOW->m_bPinned;
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
|
||||
|
||||
if (!PMONITOR) {
|
||||
Debug::log(ERR, "pin: monitor not found");
|
||||
return;
|
||||
}
|
||||
|
||||
PWINDOW->m_pWorkspace = PMONITOR->activeWorkspace;
|
||||
|
||||
PWINDOW->updateDynamicRules();
|
||||
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
|
||||
|
@ -2363,54 +2402,50 @@ void CKeybindManager::mouse(std::string args) {
|
|||
const auto ARGS = CVarList(args.substr(1), 2, ' ');
|
||||
const auto PRESSED = args[0] == '1';
|
||||
|
||||
if (!PRESSED) {
|
||||
changeMouseBindMode(MBIND_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ARGS[0] == "movewindow") {
|
||||
if (PRESSED) {
|
||||
g_pKeybindManager->m_bIsMouseBindActive = true;
|
||||
|
||||
const auto mouseCoords = g_pInputManager->getMouseCoordsInternal();
|
||||
PHLWINDOW pWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
|
||||
|
||||
if (pWindow && !pWindow->m_bIsFullscreen)
|
||||
pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_START, mouseCoords);
|
||||
|
||||
if (g_pInputManager->currentlyDraggedWindow.expired())
|
||||
g_pInputManager->currentlyDraggedWindow = pWindow;
|
||||
|
||||
g_pInputManager->dragMode = MBIND_MOVE;
|
||||
g_pLayoutManager->getCurrentLayout()->onBeginDragWindow();
|
||||
} else {
|
||||
g_pKeybindManager->m_bIsMouseBindActive = false;
|
||||
|
||||
if (!g_pInputManager->currentlyDraggedWindow.expired()) {
|
||||
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
g_pInputManager->dragMode = MBIND_INVALID;
|
||||
changeMouseBindMode(MBIND_MOVE);
|
||||
} else {
|
||||
try {
|
||||
switch (std::stoi(ARGS[1])) {
|
||||
case 1: changeMouseBindMode(MBIND_RESIZE_FORCE_RATIO); break;
|
||||
case 2: changeMouseBindMode(MBIND_RESIZE_BLOCK_RATIO); break;
|
||||
default: changeMouseBindMode(MBIND_RESIZE);
|
||||
}
|
||||
}
|
||||
} else if (ARGS[0] == "resizewindow") {
|
||||
if (PRESSED) {
|
||||
g_pKeybindManager->m_bIsMouseBindActive = true;
|
||||
} catch (std::exception& e) { changeMouseBindMode(MBIND_RESIZE); }
|
||||
}
|
||||
}
|
||||
|
||||
g_pInputManager->currentlyDraggedWindow =
|
||||
g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
|
||||
void CKeybindManager::changeMouseBindMode(const eMouseBindMode MODE) {
|
||||
if (MODE != MBIND_INVALID) {
|
||||
if (!g_pInputManager->currentlyDraggedWindow.expired() || g_pInputManager->dragMode != MBIND_INVALID)
|
||||
return;
|
||||
|
||||
try {
|
||||
switch (std::stoi(ARGS[1])) {
|
||||
case 1: g_pInputManager->dragMode = MBIND_RESIZE_FORCE_RATIO; break;
|
||||
case 2: g_pInputManager->dragMode = MBIND_RESIZE_BLOCK_RATIO; break;
|
||||
default: g_pInputManager->dragMode = MBIND_RESIZE;
|
||||
}
|
||||
} catch (std::exception& e) { g_pInputManager->dragMode = MBIND_RESIZE; }
|
||||
g_pLayoutManager->getCurrentLayout()->onBeginDragWindow();
|
||||
} else {
|
||||
g_pKeybindManager->m_bIsMouseBindActive = false;
|
||||
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
|
||||
const PHLWINDOW PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
|
||||
|
||||
if (!g_pInputManager->currentlyDraggedWindow.expired()) {
|
||||
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
|
||||
g_pInputManager->currentlyDraggedWindow.reset();
|
||||
g_pInputManager->dragMode = MBIND_INVALID;
|
||||
}
|
||||
}
|
||||
if (!PWINDOW)
|
||||
return;
|
||||
|
||||
if (!PWINDOW->isFullscreen() && MODE == MBIND_MOVE)
|
||||
PWINDOW->checkInputOnDecos(INPUT_TYPE_DRAG_START, MOUSECOORDS);
|
||||
|
||||
if (g_pInputManager->currentlyDraggedWindow.expired())
|
||||
g_pInputManager->currentlyDraggedWindow = PWINDOW;
|
||||
|
||||
g_pInputManager->dragMode = MODE;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onBeginDragWindow();
|
||||
} else {
|
||||
if (g_pInputManager->currentlyDraggedWindow.expired() || g_pInputManager->dragMode == MBIND_INVALID)
|
||||
return;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
|
||||
g_pInputManager->dragMode = MODE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2444,14 +2479,6 @@ void CKeybindManager::alterZOrder(std::string args) {
|
|||
g_pInputManager->simulateMouseMovement();
|
||||
}
|
||||
|
||||
void CKeybindManager::fakeFullscreenActive(std::string args) {
|
||||
if (!g_pCompositor->m_pLastWindow.expired()) {
|
||||
// will also set the flag
|
||||
g_pCompositor->m_pLastWindow->m_bFakeFullscreenState = !g_pCompositor->m_pLastWindow->m_bFakeFullscreenState;
|
||||
g_pXWaylandManager->setWindowFullscreen(g_pCompositor->m_pLastWindow.lock(), g_pCompositor->m_pLastWindow->shouldSendFullscreenState());
|
||||
}
|
||||
}
|
||||
|
||||
void CKeybindManager::lockGroups(std::string args) {
|
||||
if (args == "lock" || args.empty() || args == "lockgroups")
|
||||
g_pKeybindManager->m_bGroupsLocked = true;
|
||||
|
@ -2489,6 +2516,11 @@ void CKeybindManager::moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowIn
|
|||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); // This removes groupped property!
|
||||
|
||||
if (pWindow->m_iMonitorID != pWindowInDirection->m_iMonitorID) {
|
||||
pWindow->moveToWorkspace(pWindowInDirection->m_pWorkspace);
|
||||
pWindow->m_iMonitorID = pWindowInDirection->m_iMonitorID;
|
||||
}
|
||||
|
||||
static auto USECURRPOS = CConfigValue<Hyprlang::INT>("group:insert_after_current");
|
||||
pWindowInDirection = *USECURRPOS ? pWindowInDirection : pWindowInDirection->getGroupTail();
|
||||
|
||||
|
@ -2606,7 +2638,7 @@ void CKeybindManager::moveWindowOrGroup(std::string args) {
|
|||
}
|
||||
|
||||
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
if (!PWINDOW || PWINDOW->m_bIsFullscreen)
|
||||
if (!PWINDOW || PWINDOW->isFullscreen())
|
||||
return;
|
||||
|
||||
if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) {
|
||||
|
@ -2676,10 +2708,10 @@ void CKeybindManager::global(std::string args) {
|
|||
if (NAME.empty())
|
||||
return;
|
||||
|
||||
if (!g_pProtocolManager->m_pGlobalShortcutsProtocolManager->globalShortcutExists(APPID, NAME))
|
||||
if (!PROTO::globalShortcuts->isTaken(APPID, NAME))
|
||||
return;
|
||||
|
||||
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->sendGlobalShortcutEvent(APPID, NAME, g_pKeybindManager->m_iPassPressed);
|
||||
PROTO::globalShortcuts->sendGlobalShortcutEvent(APPID, NAME, g_pKeybindManager->m_iPassPressed);
|
||||
}
|
||||
|
||||
void CKeybindManager::moveGroupWindow(std::string args) {
|
||||
|
|
|
@ -34,6 +34,7 @@ struct SKeybind {
|
|||
bool ignoreMods = false;
|
||||
bool multiKey = false;
|
||||
bool hasDescription = false;
|
||||
bool dontInhibit = false;
|
||||
|
||||
// DO NOT INITIALIZE
|
||||
bool shadowed = false;
|
||||
|
@ -104,6 +105,8 @@ class CKeybindManager {
|
|||
//we also store the keyboard pointer (in the string) to differentiate between different keyboard (layouts)
|
||||
std::unordered_map<std::string, xkb_keycode_t> m_mKeyToCodeCache;
|
||||
|
||||
static void changeMouseBindMode(const eMouseBindMode mode);
|
||||
|
||||
private:
|
||||
std::deque<SPressedKeyWithMods> m_dPressedKeys;
|
||||
|
||||
|
@ -115,7 +118,6 @@ class CKeybindManager {
|
|||
uint32_t m_uLastCode = 0;
|
||||
uint32_t m_uLastMouseCode = 0;
|
||||
|
||||
bool m_bIsMouseBindActive = false;
|
||||
std::vector<SKeybind*> m_vPressedSpecialBinds;
|
||||
|
||||
int m_iPassPressed = -1; // used for pass
|
||||
|
@ -153,7 +155,7 @@ class CKeybindManager {
|
|||
static void setActiveTiled(std::string);
|
||||
static void changeworkspace(std::string);
|
||||
static void fullscreenActive(std::string);
|
||||
static void fakeFullscreenActive(std::string);
|
||||
static void fullscreenStateActive(std::string args);
|
||||
static void moveActiveToWorkspace(std::string);
|
||||
static void moveActiveToWorkspaceSilent(std::string);
|
||||
static void moveFocusTo(std::string);
|
||||
|
@ -189,7 +191,6 @@ class CKeybindManager {
|
|||
static void pass(std::string);
|
||||
static void sendshortcut(std::string);
|
||||
static void layoutmsg(std::string);
|
||||
static void toggleOpaque(std::string);
|
||||
static void dpms(std::string);
|
||||
static void swapnext(std::string);
|
||||
static void swapActiveWorkspaces(std::string);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "../protocols/PointerGestures.hpp"
|
||||
#include "../protocols/FractionalScale.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../protocols/core/Seat.hpp"
|
||||
#include "eventLoop/EventLoopManager.hpp"
|
||||
#include "SeatManager.hpp"
|
||||
#include <cstring>
|
||||
|
@ -24,6 +25,14 @@ CPointerManager::CPointerManager() {
|
|||
},
|
||||
nullptr);
|
||||
});
|
||||
|
||||
hooks.monitorPreRender = g_pHookSystem->hookDynamic("preMonitorCommit", [this](void* self, SCallbackInfo& info, std::any data) {
|
||||
auto state = stateFor(std::any_cast<CMonitor*>(data)->self.lock());
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
state->cursorRendered = false;
|
||||
});
|
||||
}
|
||||
|
||||
void CPointerManager::lockSoftwareAll() {
|
||||
|
@ -143,18 +152,20 @@ void CPointerManager::setCursorSurface(SP<CWLSurface> surf, const Vector2D& hots
|
|||
currentCursorImage.surface = surf;
|
||||
currentCursorImage.scale = surf->resource()->current.scale;
|
||||
|
||||
surf->resource()->map();
|
||||
|
||||
currentCursorImage.destroySurface = surf->events.destroy.registerListener([this](std::any data) { resetCursorImage(); });
|
||||
currentCursorImage.commitSurface = surf->resource()->events.commit.registerListener([this](std::any data) {
|
||||
damageIfSoftware();
|
||||
currentCursorImage.size = currentCursorImage.surface->resource()->current.buffer ? currentCursorImage.surface->resource()->current.buffer->size : Vector2D{};
|
||||
currentCursorImage.size = currentCursorImage.surface->resource()->current.texture ? currentCursorImage.surface->resource()->current.bufferSize : Vector2D{};
|
||||
currentCursorImage.scale = currentCursorImage.surface ? currentCursorImage.surface->resource()->current.scale : 1.F;
|
||||
recheckEnteredOutputs();
|
||||
updateCursorBackend();
|
||||
damageIfSoftware();
|
||||
});
|
||||
|
||||
if (surf->resource()->current.buffer) {
|
||||
currentCursorImage.size = surf->resource()->current.buffer->size;
|
||||
if (surf->resource()->current.texture) {
|
||||
currentCursorImage.size = surf->resource()->current.bufferSize;
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
surf->resource()->frame(&now);
|
||||
|
@ -214,6 +225,8 @@ void CPointerManager::resetCursorImage(bool apply) {
|
|||
currentCursorImage.surface->resource()->leave(m);
|
||||
}
|
||||
|
||||
currentCursorImage.surface->resource()->unmap();
|
||||
|
||||
currentCursorImage.destroySurface.reset();
|
||||
currentCursorImage.commitSurface.reset();
|
||||
currentCursorImage.surface.reset();
|
||||
|
@ -386,6 +399,14 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
|
|||
}
|
||||
}
|
||||
|
||||
// if we already rendered the cursor, revert the swapchain to avoid rendering the cursor over
|
||||
// the current front buffer
|
||||
// this flag will be reset in the preRender hook, so when we commit this buffer to KMS
|
||||
if (state->cursorRendered)
|
||||
state->monitor->cursorSwapchain->rollback();
|
||||
|
||||
state->cursorRendered = true;
|
||||
|
||||
auto buf = state->monitor->cursorSwapchain->next(nullptr);
|
||||
if (!buf) {
|
||||
Debug::log(TRACE, "Failed to acquire a buffer from the cursor swapchain");
|
||||
|
@ -410,16 +431,39 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
|
|||
// clear buffer
|
||||
memset(bufPtr, 0, std::get<2>(bufData));
|
||||
|
||||
auto texBuffer = currentCursorImage.pBuffer ? currentCursorImage.pBuffer : currentCursorImage.surface->resource()->current.buffer;
|
||||
if (currentCursorImage.pBuffer) {
|
||||
auto texAttrs = currentCursorImage.pBuffer->shm();
|
||||
|
||||
if (texBuffer) {
|
||||
auto textAttrs = texBuffer->shm();
|
||||
auto texData = texBuffer->beginDataPtr(GBM_BO_TRANSFER_WRITE);
|
||||
auto texPtr = std::get<0>(texData);
|
||||
Debug::log(TRACE, "cursor texture {}x{} {} {} {}", textAttrs.size.x, textAttrs.size.y, (void*)texPtr, textAttrs.format, textAttrs.stride);
|
||||
if (!texAttrs.success) {
|
||||
Debug::log(TRACE, "Cannot use dumb copy on dmabuf cursor buffers");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto texData = currentCursorImage.pBuffer->beginDataPtr(GBM_BO_TRANSFER_WRITE);
|
||||
auto texPtr = std::get<0>(texData);
|
||||
Debug::log(TRACE, "cursor texture {}x{} {} {} {}", texAttrs.size.x, texAttrs.size.y, (void*)texPtr, texAttrs.format, texAttrs.stride);
|
||||
// copy cursor texture
|
||||
for (int i = 0; i < texBuffer->shm().size.y; i++)
|
||||
memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * textAttrs.stride, textAttrs.stride);
|
||||
for (int i = 0; i < texAttrs.size.y; i++)
|
||||
memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * texAttrs.stride, texAttrs.stride);
|
||||
} else if (currentCursorImage.surface && currentCursorImage.surface->resource()->role->role() == SURFACE_ROLE_CURSOR) {
|
||||
const auto SURFACE = currentCursorImage.surface->resource();
|
||||
auto& shmBuffer = CCursorSurfaceRole::cursorPixelData(SURFACE);
|
||||
Debug::log(TRACE, "cursor texture pixel data length: {}B", shmBuffer.size());
|
||||
|
||||
if (shmBuffer.data()) {
|
||||
// copy cursor texture
|
||||
// assume format is 32bpp
|
||||
size_t STRIDE = 4 * SURFACE->current.bufferSize.x;
|
||||
for (int i = 0; i < SURFACE->current.bufferSize.y; i++)
|
||||
memcpy(bufPtr + i * buf->dmabuf().strides[0], shmBuffer.data() + i * STRIDE, STRIDE);
|
||||
} else {
|
||||
// if there is no data, hide the cursor
|
||||
memset(bufPtr, '\0', buf->size.x * buf->size.y * 4 /* assume 32bpp */);
|
||||
}
|
||||
|
||||
} else {
|
||||
Debug::log(TRACE, "Unsupported cursor buffer/surface, falling back to sw (can't dumb copy)");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->endDataPtr();
|
||||
|
@ -620,7 +664,7 @@ void CPointerManager::move(const Vector2D& deltaLogical) {
|
|||
void CPointerManager::warpAbsolute(Vector2D abs, SP<IHID> dev) {
|
||||
|
||||
SP<CMonitor> currentMonitor = g_pCompositor->m_pLastMonitor.lock();
|
||||
if (!currentMonitor)
|
||||
if (!currentMonitor || !dev)
|
||||
return;
|
||||
|
||||
if (!std::isnan(abs.x))
|
||||
|
@ -658,6 +702,32 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP<IHID> dev) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case HID_TYPE_POINTER: {
|
||||
IPointer* POINTER = reinterpret_cast<IPointer*>(dev.get());
|
||||
if (!POINTER->boundOutput.empty()) {
|
||||
if (POINTER->boundOutput == "entire") {
|
||||
// find x and y size of the entire space
|
||||
Vector2D bottomRight = {-9999999, -9999999}, topLeft = {9999999, 9999999};
|
||||
for (auto& m : g_pCompositor->m_vMonitors) {
|
||||
const auto EXTENT = m->logicalBox().extent();
|
||||
const auto POS = m->logicalBox().pos();
|
||||
if (EXTENT.x > bottomRight.x)
|
||||
bottomRight.x = EXTENT.x;
|
||||
if (EXTENT.y > bottomRight.y)
|
||||
bottomRight.y = EXTENT.y;
|
||||
if (POS.x < topLeft.x)
|
||||
topLeft.x = POS.x;
|
||||
if (POS.y < topLeft.y)
|
||||
topLeft.y = POS.y;
|
||||
}
|
||||
mappedArea = {topLeft, bottomRight - topLeft};
|
||||
} else if (const auto PMONITOR = g_pCompositor->getMonitorFromString(POINTER->boundOutput); PMONITOR) {
|
||||
currentMonitor = PMONITOR->self.lock();
|
||||
mappedArea = currentMonitor->logicalBox();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
@ -694,7 +764,7 @@ void CPointerManager::onMonitorLayoutChange() {
|
|||
}
|
||||
|
||||
SP<CTexture> CPointerManager::getCurrentCursorTexture() {
|
||||
if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.buffer))
|
||||
if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.texture))
|
||||
return nullptr;
|
||||
|
||||
if (currentCursorImage.pBuffer) {
|
||||
|
@ -703,7 +773,7 @@ SP<CTexture> CPointerManager::getCurrentCursorTexture() {
|
|||
return currentCursorImage.bufferTex;
|
||||
}
|
||||
|
||||
return currentCursorImage.surface->resource()->current.buffer->texture;
|
||||
return currentCursorImage.surface->resource()->current.texture;
|
||||
}
|
||||
|
||||
void CPointerManager::attachPointer(SP<IPointer> pointer) {
|
||||
|
|
|
@ -163,8 +163,9 @@ class CPointerManager {
|
|||
int softwareLocks = 0;
|
||||
bool hardwareFailed = false;
|
||||
CBox box; // logical
|
||||
bool entered = false;
|
||||
bool hwApplied = false;
|
||||
bool entered = false;
|
||||
bool hwApplied = false;
|
||||
bool cursorRendered = false;
|
||||
|
||||
SP<Aquamarine::IBuffer> cursorFrontBuffer;
|
||||
};
|
||||
|
@ -177,6 +178,7 @@ class CPointerManager {
|
|||
|
||||
struct {
|
||||
SP<HOOK_CALLBACK_FN> monitorAdded;
|
||||
SP<HOOK_CALLBACK_FN> monitorPreRender;
|
||||
} hooks;
|
||||
};
|
||||
|
||||
|
|
|
@ -39,6 +39,10 @@
|
|||
#include "../protocols/LinuxDMABUF.hpp"
|
||||
#include "../protocols/DRMLease.hpp"
|
||||
#include "../protocols/DRMSyncobj.hpp"
|
||||
#include "../protocols/Screencopy.hpp"
|
||||
#include "../protocols/ToplevelExport.hpp"
|
||||
#include "../protocols/TextInputV1.hpp"
|
||||
#include "../protocols/GlobalShortcuts.hpp"
|
||||
|
||||
#include "../protocols/core/Seat.hpp"
|
||||
#include "../protocols/core/DataDevice.hpp"
|
||||
|
@ -66,8 +70,7 @@ void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) {
|
|||
else if (!ISMIRROR && (!PROTO::outputs.contains(pMonitor->szName) || PROTO::outputs.at(pMonitor->szName)->isDefunct())) {
|
||||
if (PROTO::outputs.contains(pMonitor->szName))
|
||||
PROTO::outputs.erase(pMonitor->szName);
|
||||
PROTO::outputs.emplace(pMonitor->szName,
|
||||
std::make_unique<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock()));
|
||||
PROTO::outputs.emplace(pMonitor->szName, makeShared<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +89,10 @@ CProtocolManager::CProtocolManager() {
|
|||
|
||||
if (PROTO::outputs.contains(M->szName))
|
||||
PROTO::outputs.erase(M->szName);
|
||||
PROTO::outputs.emplace(M->szName, std::make_unique<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", M->szName), M->self.lock()));
|
||||
|
||||
auto ref = makeShared<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", M->szName), M->self.lock());
|
||||
PROTO::outputs.emplace(M->szName, ref);
|
||||
ref->self = ref;
|
||||
|
||||
m_mModeChangeListeners[M->szName] = M->events.modeChanged.registerListener([M, this](std::any d) { onMonitorModeChange(M); });
|
||||
});
|
||||
|
@ -121,6 +127,7 @@ CProtocolManager::CProtocolManager() {
|
|||
PROTO::pointerGestures = std::make_unique<CPointerGesturesProtocol>(&zwp_pointer_gestures_v1_interface, 3, "PointerGestures");
|
||||
PROTO::foreignToplevelWlr = std::make_unique<CForeignToplevelWlrProtocol>(&zwlr_foreign_toplevel_manager_v1_interface, 3, "ForeignToplevelWlr");
|
||||
PROTO::shortcutsInhibit = std::make_unique<CKeyboardShortcutsInhibitProtocol>(&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1, "ShortcutsInhibit");
|
||||
PROTO::textInputV1 = std::make_unique<CTextInputV1Protocol>(&zwp_text_input_manager_v1_interface, 1, "TextInputV1");
|
||||
PROTO::textInputV3 = std::make_unique<CTextInputV3Protocol>(&zwp_text_input_manager_v3_interface, 1, "TextInputV3");
|
||||
PROTO::constraints = std::make_unique<CPointerConstraintsProtocol>(&zwp_pointer_constraints_v1_interface, 1, "PointerConstraints");
|
||||
PROTO::outputPower = std::make_unique<COutputPowerProtocol>(&zwlr_output_power_manager_v1_interface, 1, "OutputPower");
|
||||
|
@ -140,6 +147,9 @@ CProtocolManager::CProtocolManager() {
|
|||
PROTO::dataWlr = std::make_unique<CDataDeviceWLRProtocol>(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr");
|
||||
PROTO::primarySelection = std::make_unique<CPrimarySelectionProtocol>(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection");
|
||||
PROTO::xwaylandShell = std::make_unique<CXWaylandShellProtocol>(&xwayland_shell_v1_interface, 1, "XWaylandShell");
|
||||
PROTO::screencopy = std::make_unique<CScreencopyProtocol>(&zwlr_screencopy_manager_v1_interface, 3, "Screencopy");
|
||||
PROTO::toplevelExport = std::make_unique<CToplevelExportProtocol>(&hyprland_toplevel_export_manager_v1_interface, 2, "ToplevelExport");
|
||||
PROTO::globalShortcuts = std::make_unique<CGlobalShortcutsProtocol>(&hyprland_global_shortcuts_manager_v1_interface, 1, "GlobalShortcuts");
|
||||
|
||||
for (auto& b : g_pCompositor->m_pAqBackend->getImplementations()) {
|
||||
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
|
||||
|
@ -156,11 +166,62 @@ CProtocolManager::CProtocolManager() {
|
|||
PROTO::linuxDma = std::make_unique<CLinuxDMABufV1Protocol>(&zwp_linux_dmabuf_v1_interface, 5, "LinuxDMABUF");
|
||||
} else
|
||||
Debug::log(WARN, "ProtocolManager: Not binding linux-dmabuf and MesaDRM: DMABUF not available");
|
||||
|
||||
// Old protocol implementations.
|
||||
// TODO: rewrite them to use hyprwayland-scanner.
|
||||
m_pToplevelExportProtocolManager = std::make_unique<CToplevelExportProtocolManager>();
|
||||
m_pTextInputV1ProtocolManager = std::make_unique<CTextInputV1ProtocolManager>();
|
||||
m_pGlobalShortcutsProtocolManager = std::make_unique<CGlobalShortcutsProtocolManager>();
|
||||
m_pScreencopyProtocolManager = std::make_unique<CScreencopyProtocolManager>();
|
||||
}
|
||||
|
||||
CProtocolManager::~CProtocolManager() {
|
||||
// this is dumb but i don't want to replace all 600 PROTO with the right thing
|
||||
|
||||
// Output
|
||||
PROTO::outputs.clear();
|
||||
|
||||
// Core
|
||||
PROTO::seat.reset();
|
||||
PROTO::data.reset();
|
||||
PROTO::compositor.reset();
|
||||
PROTO::subcompositor.reset();
|
||||
PROTO::shm.reset();
|
||||
|
||||
// Extensions
|
||||
PROTO::viewport.reset();
|
||||
PROTO::tearing.reset();
|
||||
PROTO::fractional.reset();
|
||||
PROTO::xdgOutput.reset();
|
||||
PROTO::cursorShape.reset();
|
||||
PROTO::idleInhibit.reset();
|
||||
PROTO::relativePointer.reset();
|
||||
PROTO::xdgDecoration.reset();
|
||||
PROTO::alphaModifier.reset();
|
||||
PROTO::gamma.reset();
|
||||
PROTO::foreignToplevel.reset();
|
||||
PROTO::pointerGestures.reset();
|
||||
PROTO::foreignToplevelWlr.reset();
|
||||
PROTO::shortcutsInhibit.reset();
|
||||
PROTO::textInputV1.reset();
|
||||
PROTO::textInputV3.reset();
|
||||
PROTO::constraints.reset();
|
||||
PROTO::outputPower.reset();
|
||||
PROTO::activation.reset();
|
||||
PROTO::idle.reset();
|
||||
PROTO::sessionLock.reset();
|
||||
PROTO::ime.reset();
|
||||
PROTO::virtualKeyboard.reset();
|
||||
PROTO::virtualPointer.reset();
|
||||
PROTO::outputManagement.reset();
|
||||
PROTO::serverDecorationKDE.reset();
|
||||
PROTO::focusGrab.reset();
|
||||
PROTO::tablet.reset();
|
||||
PROTO::layerShell.reset();
|
||||
PROTO::presentation.reset();
|
||||
PROTO::xdgShell.reset();
|
||||
PROTO::dataWlr.reset();
|
||||
PROTO::primarySelection.reset();
|
||||
PROTO::xwaylandShell.reset();
|
||||
PROTO::screencopy.reset();
|
||||
PROTO::toplevelExport.reset();
|
||||
PROTO::globalShortcuts.reset();
|
||||
|
||||
PROTO::lease.reset();
|
||||
PROTO::sync.reset();
|
||||
PROTO::mesaDRM.reset();
|
||||
PROTO::linuxDma.reset();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../protocols/ToplevelExport.hpp"
|
||||
#include "../protocols/TextInputV1.hpp"
|
||||
#include "../protocols/GlobalShortcuts.hpp"
|
||||
#include "../protocols/Screencopy.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include <unordered_map>
|
||||
|
@ -12,12 +9,7 @@
|
|||
class CProtocolManager {
|
||||
public:
|
||||
CProtocolManager();
|
||||
|
||||
// TODO: rewrite to use the new protocol framework
|
||||
std::unique_ptr<CToplevelExportProtocolManager> m_pToplevelExportProtocolManager;
|
||||
std::unique_ptr<CTextInputV1ProtocolManager> m_pTextInputV1ProtocolManager;
|
||||
std::unique_ptr<CGlobalShortcutsProtocolManager> m_pGlobalShortcutsProtocolManager;
|
||||
std::unique_ptr<CScreencopyProtocolManager> m_pScreencopyProtocolManager;
|
||||
~CProtocolManager();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, CHyprSignalListener> m_mModeChangeListeners;
|
||||
|
|
|
@ -71,7 +71,8 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
|
|||
g_pHyprRenderer->damageMonitor(m.get());
|
||||
});
|
||||
|
||||
m_pSessionLock->listeners.destroy = pLock->events.destroyed.registerListener([](std::any data) {
|
||||
m_pSessionLock->listeners.destroy = pLock->events.destroyed.registerListener([this](std::any data) {
|
||||
m_pSessionLock.reset();
|
||||
g_pCompositor->focusSurface(nullptr);
|
||||
|
||||
for (auto& m : g_pCompositor->m_vMonitors)
|
||||
|
@ -104,7 +105,7 @@ SSessionLockSurface* CSessionLockManager::getSessionLockSurfaceForMonitor(uint64
|
|||
// We don't want the red screen to flash.
|
||||
float CSessionLockManager::getRedScreenAlphaForMonitor(uint64_t id) {
|
||||
if (!m_pSessionLock)
|
||||
return 0.F;
|
||||
return 1.F;
|
||||
|
||||
const auto& NOMAPPEDSURFACETIMER = m_pSessionLock->mMonitorsWithoutMappedSurfaceTimers.find(id);
|
||||
|
||||
|
@ -123,7 +124,7 @@ void CSessionLockManager::onLockscreenRenderedOnMonitor(uint64_t id) {
|
|||
m_pSessionLock->m_lockedMonitors.emplace(id);
|
||||
const auto MONITORS = g_pCompositor->m_vMonitors;
|
||||
const bool LOCKED = std::all_of(MONITORS.begin(), MONITORS.end(), [this](auto m) { return m_pSessionLock->m_lockedMonitors.contains(m->ID); });
|
||||
if (LOCKED) {
|
||||
if (LOCKED && m_pSessionLock->lock->good()) {
|
||||
m_pSessionLock->lock->sendLocked();
|
||||
m_pSessionLock->m_hasSentLocked = true;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,16 @@ CEventLoopManager::CEventLoopManager(wl_display* display, wl_event_loop* wlEvent
|
|||
}
|
||||
|
||||
CEventLoopManager::~CEventLoopManager() {
|
||||
for (auto& eventSource : m_sWayland.aqEventSources) {
|
||||
wl_event_source_remove(eventSource);
|
||||
}
|
||||
|
||||
if (m_sWayland.eventSource)
|
||||
wl_event_source_remove(m_sWayland.eventSource);
|
||||
if (m_sIdle.eventSource)
|
||||
wl_event_source_remove(m_sIdle.eventSource);
|
||||
if (m_sTimers.timerfd >= 0)
|
||||
close(m_sTimers.timerfd);
|
||||
}
|
||||
|
||||
static int timerWrite(int fd, uint32_t mask, void* data) {
|
||||
|
|
|
@ -7,7 +7,7 @@ void CInputManager::newIdleInhibitor(std::any inhibitor) {
|
|||
const auto PINHIBIT = m_vIdleInhibitors.emplace_back(std::make_unique<SIdleInhibitor>()).get();
|
||||
PINHIBIT->inhibitor = std::any_cast<SP<CIdleInhibitor>>(inhibitor);
|
||||
|
||||
Debug::log(LOG, "New idle inhibitor registered for surface {:x}", (uintptr_t)PINHIBIT->inhibitor->surface);
|
||||
Debug::log(LOG, "New idle inhibitor registered for surface {:x}", (uintptr_t)PINHIBIT->inhibitor->surface.get());
|
||||
|
||||
PINHIBIT->inhibitor->listeners.destroy = PINHIBIT->inhibitor->resource->events.destroy.registerListener([this, PINHIBIT](std::any data) {
|
||||
std::erase_if(m_vIdleInhibitors, [PINHIBIT](const auto& other) { return other.get() == PINHIBIT; });
|
||||
|
@ -63,7 +63,7 @@ void CInputManager::recheckIdleInhibitorStatus() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (w->m_eIdleInhibitMode == IDLEINHIBIT_FULLSCREEN && w->m_bIsFullscreen && g_pCompositor->isWorkspaceVisible(w->m_pWorkspace)) {
|
||||
if (w->m_eIdleInhibitMode == IDLEINHIBIT_FULLSCREEN && w->isFullscreen() && g_pCompositor->isWorkspaceVisible(w->m_pWorkspace)) {
|
||||
PROTO::idle->setInhibit(true);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "../../protocols/LayerShell.hpp"
|
||||
#include "../../protocols/core/Seat.hpp"
|
||||
#include "../../protocols/core/DataDevice.hpp"
|
||||
#include "../../protocols/core/Compositor.hpp"
|
||||
#include "../../protocols/XDGShell.hpp"
|
||||
|
||||
#include "../../devices/Mouse.hpp"
|
||||
|
@ -228,7 +229,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
|||
return;
|
||||
|
||||
} else
|
||||
Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF, (uintptr_t)CONSTRAINT.get());
|
||||
Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF.get(), (uintptr_t)CONSTRAINT.get());
|
||||
}
|
||||
|
||||
// if we are holding a pointer button,
|
||||
|
@ -297,7 +298,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
|||
|
||||
// then, we check if the workspace doesnt have a fullscreen window
|
||||
const auto PWORKSPACE = PMONITOR->activeWorkspace;
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !foundSurface && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && !foundSurface && PWORKSPACE->m_efFullscreenMode == FSMODE_FULLSCREEN) {
|
||||
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
|
||||
|
||||
if (!pFoundWindow) {
|
||||
|
@ -324,7 +325,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
|||
|
||||
// then windows
|
||||
if (!foundSurface) {
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FSMODE_MAXIMIZED) {
|
||||
if (!foundSurface) {
|
||||
if (PMONITOR->activeSpecialWorkspace) {
|
||||
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
|
||||
|
@ -469,7 +470,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
|
|||
else if (pFoundWindow) {
|
||||
// change cursor icon if hovering over border
|
||||
if (*PRESIZEONBORDER && *PRESIZECURSORICON) {
|
||||
if (!pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) {
|
||||
if (!pFoundWindow->isFullscreen() && !pFoundWindow->hasPopupAt(mouseCoords)) {
|
||||
setCursorIconOnBorder(pFoundWindow);
|
||||
} else if (m_eBorderIconDirection != BORDERICON_NONE) {
|
||||
unsetCursorImage();
|
||||
|
@ -570,7 +571,7 @@ void CInputManager::processMouseRequest(std::any E) {
|
|||
|
||||
auto e = std::any_cast<CSeatManager::SSetCursorEvent>(E);
|
||||
|
||||
Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf);
|
||||
Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf.get());
|
||||
|
||||
if (e.surf != m_sCursorSurfaceInfo.wlSurface->resource()) {
|
||||
m_sCursorSurfaceInfo.wlSurface->unassign();
|
||||
|
@ -680,7 +681,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) {
|
|||
// clicking on border triggers resize
|
||||
// TODO detect click on LS properly
|
||||
if (*PRESIZEONBORDER && !m_bLastFocusOnLS && e.state == WL_POINTER_BUTTON_STATE_PRESSED && (!w || w->m_iX11Type != 2)) {
|
||||
if (w && !w->m_bIsFullscreen) {
|
||||
if (w && !w->isFullscreen()) {
|
||||
const CBox real = {w->m_vRealPosition.value().x, w->m_vRealPosition.value().y, w->m_vRealSize.value().x, w->m_vRealSize.value().y};
|
||||
const CBox grab = {real.x - BORDER_GRAB_AREA, real.y - BORDER_GRAB_AREA, real.width + 2 * BORDER_GRAB_AREA, real.height + 2 * BORDER_GRAB_AREA};
|
||||
|
||||
|
@ -850,7 +851,7 @@ void CInputManager::newKeyboard(SP<Aquamarine::IKeyboard> keyboard) {
|
|||
|
||||
setupKeyboard(PNEWKEYBOARD);
|
||||
|
||||
Debug::log(LOG, "New keyboard created, pointers Hypr: {:x} and WLR: {:x}", (uintptr_t)PNEWKEYBOARD.get(), (uintptr_t)keyboard);
|
||||
Debug::log(LOG, "New keyboard created, pointers Hypr: {:x} and AQ: {:x}", (uintptr_t)PNEWKEYBOARD.get(), (uintptr_t)keyboard.get());
|
||||
}
|
||||
|
||||
void CInputManager::newVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keyboard) {
|
||||
|
@ -992,7 +993,7 @@ void CInputManager::newMouse(SP<Aquamarine::IPointer> mouse) {
|
|||
|
||||
setupMouse(PMOUSE);
|
||||
|
||||
Debug::log(LOG, "New mouse created, pointer WLR: {:x}", (uintptr_t)mouse);
|
||||
Debug::log(LOG, "New mouse created, pointer AQ: {:x}", (uintptr_t)mouse.get());
|
||||
}
|
||||
|
||||
void CInputManager::setupMouse(SP<IPointer> mauz) {
|
||||
|
@ -1264,7 +1265,14 @@ void CInputManager::updateKeyboardsLeds(SP<IKeyboard> pKeyboard) {
|
|||
if (!pKeyboard)
|
||||
return;
|
||||
|
||||
pKeyboard->updateLEDs();
|
||||
std::optional<uint32_t> leds = pKeyboard->getLEDs();
|
||||
|
||||
if (!leds.has_value())
|
||||
return;
|
||||
|
||||
for (auto& k : m_vKeyboards) {
|
||||
k->updateLEDs(leds.value());
|
||||
}
|
||||
}
|
||||
|
||||
void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
|
||||
|
@ -1382,6 +1390,12 @@ void CInputManager::refocusLastWindow(CMonitor* pMonitor) {
|
|||
g_pCompositor->focusWindow(PLASTWINDOW);
|
||||
} else {
|
||||
// otherwise fall back to a normal refocus.
|
||||
|
||||
if (foundSurface && !foundSurface->hlSurface->keyboardFocusable()) {
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
g_pCompositor->focusWindow(PLASTWINDOW);
|
||||
}
|
||||
|
||||
refocus();
|
||||
}
|
||||
}
|
||||
|
@ -1670,7 +1684,7 @@ void CInputManager::releaseAllMouseButtons() {
|
|||
|
||||
void CInputManager::setCursorIconOnBorder(PHLWINDOW w) {
|
||||
// do not override cursor icons set by mouse binds
|
||||
if (g_pKeybindManager->m_bIsMouseBindActive) {
|
||||
if (g_pInputManager->currentlyDraggedWindow.expired()) {
|
||||
m_eBorderIconDirection = BORDERICON_NONE;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "InputManager.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include "../../protocols/TextInputV3.hpp"
|
||||
#include "../../protocols/TextInputV1.hpp"
|
||||
#include "../../protocols/InputMethodV2.hpp"
|
||||
#include "../../protocols/core/Compositor.hpp"
|
||||
|
||||
|
@ -9,7 +10,8 @@ CInputMethodRelay::CInputMethodRelay() {
|
|||
static auto P =
|
||||
g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast<SP<CWLSurfaceResource>>(param)); });
|
||||
|
||||
listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(ti); });
|
||||
listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(std::any_cast<WP<CTextInputV3>>(ti)); });
|
||||
listeners.newTIV1 = PROTO::textInputV1->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(std::any_cast<WP<CTextInputV1>>(ti)); });
|
||||
listeners.newIME = PROTO::ime->events.newIME.registerListener([this](std::any ime) { onNewIME(std::any_cast<SP<CInputMethodV2>>(ime)); });
|
||||
}
|
||||
|
||||
|
@ -86,11 +88,11 @@ CTextInput* CInputMethodRelay::getFocusedTextInput() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void CInputMethodRelay::onNewTextInput(std::any tiv3) {
|
||||
m_vTextInputs.emplace_back(std::make_unique<CTextInput>(std::any_cast<WP<CTextInputV3>>(tiv3)));
|
||||
void CInputMethodRelay::onNewTextInput(WP<CTextInputV3> tiv3) {
|
||||
m_vTextInputs.emplace_back(std::make_unique<CTextInput>(tiv3));
|
||||
}
|
||||
|
||||
void CInputMethodRelay::onNewTextInput(STextInputV1* pTIV1) {
|
||||
void CInputMethodRelay::onNewTextInput(WP<CTextInputV1> pTIV1) {
|
||||
m_vTextInputs.emplace_back(std::make_unique<CTextInput>(pTIV1));
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class CInputManager;
|
||||
class CHyprRenderer;
|
||||
struct STextInputV1;
|
||||
class CTextInputV1;
|
||||
class CInputMethodV2;
|
||||
|
||||
class CInputMethodRelay {
|
||||
|
@ -18,8 +18,8 @@ class CInputMethodRelay {
|
|||
CInputMethodRelay();
|
||||
|
||||
void onNewIME(SP<CInputMethodV2>);
|
||||
void onNewTextInput(std::any tiv3);
|
||||
void onNewTextInput(STextInputV1* pTIV1);
|
||||
void onNewTextInput(WP<CTextInputV3> tiv3);
|
||||
void onNewTextInput(WP<CTextInputV1> pTIV1);
|
||||
|
||||
void activateIME(CTextInput* pInput);
|
||||
void deactivateIME(CTextInput* pInput);
|
||||
|
@ -48,6 +48,7 @@ class CInputMethodRelay {
|
|||
|
||||
struct {
|
||||
CHyprSignalListener newTIV3;
|
||||
CHyprSignalListener newTIV1;
|
||||
CHyprSignalListener newIME;
|
||||
CHyprSignalListener commitIME;
|
||||
CHyprSignalListener destroyIME;
|
||||
|
|
|
@ -194,7 +194,7 @@ void CInputManager::endWorkspaceSwipe() {
|
|||
|
||||
// apply alpha
|
||||
for (auto& ls : g_pCompositor->m_pLastMonitor->m_aLayerSurfaceLayers[2]) {
|
||||
ls->alpha = pSwitchedTo->m_bHasFullscreenWindow && pSwitchedTo->m_efFullscreenMode == FULLSCREEN_FULL ? 0.f : 1.f;
|
||||
ls->alpha = pSwitchedTo->m_bHasFullscreenWindow && pSwitchedTo->m_efFullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
#include "../../protocols/InputMethodV2.hpp"
|
||||
#include "../../protocols/core/Compositor.hpp"
|
||||
|
||||
CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) {
|
||||
ti->pTextInput = this;
|
||||
CTextInput::CTextInput(WP<CTextInputV1> ti) : pV1Input(ti) {
|
||||
initCallbacks();
|
||||
}
|
||||
|
||||
|
@ -16,17 +15,6 @@ CTextInput::CTextInput(WP<CTextInputV3> ti) : pV3Input(ti) {
|
|||
initCallbacks();
|
||||
}
|
||||
|
||||
CTextInput::~CTextInput() {
|
||||
if (pV1Input)
|
||||
pV1Input->pTextInput = nullptr;
|
||||
}
|
||||
|
||||
void CTextInput::tiV1Destroyed() {
|
||||
pV1Input = nullptr;
|
||||
|
||||
g_pInputManager->m_sIMERelay.removeTextInput(this);
|
||||
}
|
||||
|
||||
void CTextInput::initCallbacks() {
|
||||
if (isV3()) {
|
||||
const auto INPUT = pV3Input.lock();
|
||||
|
@ -41,25 +29,19 @@ void CTextInput::initCallbacks() {
|
|||
g_pInputManager->m_sIMERelay.removeTextInput(this);
|
||||
});
|
||||
} else {
|
||||
hyprListener_textInputEnable.initCallback(&pV1Input->sEnable, [this](void* owner, void* data) { onEnabled(); }, this, "textInput");
|
||||
const auto INPUT = pV1Input.lock();
|
||||
|
||||
hyprListener_textInputCommit.initCallback(&pV1Input->sCommit, [this](void* owner, void* data) { onCommit(); }, this, "textInput");
|
||||
|
||||
hyprListener_textInputDisable.initCallback(&pV1Input->sDisable, [this](void* owner, void* data) { onDisabled(); }, this, "textInput");
|
||||
|
||||
hyprListener_textInputDestroy.initCallback(
|
||||
&pV1Input->sDestroy,
|
||||
[this](void* owner, void* data) {
|
||||
hyprListener_textInputCommit.removeCallback();
|
||||
hyprListener_textInputDestroy.removeCallback();
|
||||
hyprListener_textInputDisable.removeCallback();
|
||||
hyprListener_textInputEnable.removeCallback();
|
||||
listeners.surfaceUnmap.reset();
|
||||
listeners.surfaceDestroy.reset();
|
||||
|
||||
g_pInputManager->m_sIMERelay.removeTextInput(this);
|
||||
},
|
||||
this, "textInput");
|
||||
listeners.enable = INPUT->events.enable.registerListener([this](std::any p) {
|
||||
const auto SURFACE = std::any_cast<SP<CWLSurfaceResource>>(p);
|
||||
onEnabled(SURFACE);
|
||||
});
|
||||
listeners.disable = INPUT->events.disable.registerListener([this](std::any p) { onDisabled(); });
|
||||
listeners.commit = INPUT->events.onCommit.registerListener([this](std::any p) { onCommit(); });
|
||||
listeners.destroy = INPUT->events.destroy.registerListener([this](std::any p) {
|
||||
listeners.surfaceUnmap.reset();
|
||||
listeners.surfaceDestroy.reset();
|
||||
g_pInputManager->m_sIMERelay.removeTextInput(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +131,7 @@ void CTextInput::setFocusedSurface(SP<CWLSurfaceResource> pSurface) {
|
|||
}
|
||||
|
||||
bool CTextInput::isV3() {
|
||||
return !pV1Input;
|
||||
return pV3Input && !pV1Input;
|
||||
}
|
||||
|
||||
void CTextInput::enter(SP<CWLSurfaceResource> pSurface) {
|
||||
|
@ -174,8 +156,7 @@ void CTextInput::enter(SP<CWLSurfaceResource> pSurface) {
|
|||
if (isV3())
|
||||
pV3Input->enter(pSurface);
|
||||
else {
|
||||
zwp_text_input_v1_send_enter(pV1Input->resourceImpl, pSurface->getResource()->resource());
|
||||
pV1Input->active = true;
|
||||
pV1Input->enter(pSurface);
|
||||
}
|
||||
|
||||
setFocusedSurface(pSurface);
|
||||
|
@ -194,8 +175,7 @@ void CTextInput::leave() {
|
|||
if (isV3() && focusedSurface())
|
||||
pV3Input->leave(focusedSurface());
|
||||
else if (focusedSurface() && pV1Input) {
|
||||
zwp_text_input_v1_send_leave(pV1Input->resourceImpl);
|
||||
pV1Input->active = false;
|
||||
pV1Input->leave();
|
||||
}
|
||||
|
||||
setFocusedSurface(nullptr);
|
||||
|
@ -208,7 +188,7 @@ SP<CWLSurfaceResource> CTextInput::focusedSurface() {
|
|||
}
|
||||
|
||||
wl_client* CTextInput::client() {
|
||||
return isV3() ? pV3Input->client() : pV1Input->client;
|
||||
return isV3() ? pV3Input->client() : pV1Input->client();
|
||||
}
|
||||
|
||||
void CTextInput::commitStateToIME(SP<CInputMethodV2> ime) {
|
||||
|
@ -223,13 +203,15 @@ void CTextInput::commitStateToIME(SP<CInputMethodV2> ime) {
|
|||
if (INPUT->current.contentType.updated)
|
||||
ime->textContentType(INPUT->current.contentType.hint, INPUT->current.contentType.purpose);
|
||||
} else {
|
||||
if (pV1Input->pendingSurrounding.isPending)
|
||||
ime->surroundingText(pV1Input->pendingSurrounding.text, pV1Input->pendingSurrounding.cursor, pV1Input->pendingSurrounding.anchor);
|
||||
const auto INPUT = pV1Input.lock();
|
||||
|
||||
if (INPUT->pendingSurrounding.isPending)
|
||||
ime->surroundingText(INPUT->pendingSurrounding.text, INPUT->pendingSurrounding.cursor, INPUT->pendingSurrounding.anchor);
|
||||
|
||||
ime->textChangeCause(ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD);
|
||||
|
||||
if (pV1Input->pendingContentType.isPending)
|
||||
ime->textContentType((zwpTextInputV3ContentHint)pV1Input->pendingContentType.hint, (zwpTextInputV3ContentPurpose)pV1Input->pendingContentType.purpose);
|
||||
ime->textContentType((zwpTextInputV3ContentHint)INPUT->pendingContentType.hint, (zwpTextInputV3ContentPurpose)INPUT->pendingContentType.purpose);
|
||||
}
|
||||
|
||||
g_pInputManager->m_sIMERelay.updateAllPopups();
|
||||
|
@ -252,25 +234,27 @@ void CTextInput::updateIMEState(SP<CInputMethodV2> ime) {
|
|||
|
||||
INPUT->sendDone();
|
||||
} else {
|
||||
const auto INPUT = pV1Input.lock();
|
||||
|
||||
if (ime->current.preeditString.committed) {
|
||||
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
|
||||
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, std::string(ime->current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
|
||||
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str(), "");
|
||||
INPUT->preeditCursor(ime->current.preeditString.begin);
|
||||
INPUT->preeditStyling(0, std::string(ime->current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
|
||||
INPUT->preeditString(pV1Input->serial, ime->current.preeditString.string.c_str(), "");
|
||||
} else {
|
||||
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
|
||||
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
|
||||
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, "", "");
|
||||
INPUT->preeditCursor(ime->current.preeditString.begin);
|
||||
INPUT->preeditStyling(0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
|
||||
INPUT->preeditString(pV1Input->serial, "", "");
|
||||
}
|
||||
|
||||
if (ime->current.committedString.committed)
|
||||
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.committedString.string.c_str());
|
||||
INPUT->commitString(pV1Input->serial, ime->current.committedString.string.c_str());
|
||||
|
||||
if (ime->current.deleteSurrounding.committed) {
|
||||
zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preeditString.string).length() - ime->current.deleteSurrounding.before,
|
||||
ime->current.deleteSurrounding.after + ime->current.deleteSurrounding.before);
|
||||
INPUT->deleteSurroundingText(std::string(ime->current.preeditString.string).length() - ime->current.deleteSurrounding.before,
|
||||
ime->current.deleteSurrounding.after + ime->current.deleteSurrounding.before);
|
||||
|
||||
if (ime->current.preeditString.committed)
|
||||
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str());
|
||||
INPUT->commitString(pV1Input->serial, ime->current.preeditString.string.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,4 +265,4 @@ bool CTextInput::hasCursorRectangle() {
|
|||
|
||||
CBox CTextInput::cursorBox() {
|
||||
return CBox{isV3() ? pV3Input->current.box.cursorBox : pV1Input->cursorRectangle};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
struct wl_client;
|
||||
|
||||
struct STextInputV1;
|
||||
class CTextInputV1;
|
||||
class CTextInputV3;
|
||||
class CInputMethodV2;
|
||||
class CWLSurfaceResource;
|
||||
|
@ -16,8 +16,7 @@ class CWLSurfaceResource;
|
|||
class CTextInput {
|
||||
public:
|
||||
CTextInput(WP<CTextInputV3> ti);
|
||||
CTextInput(STextInputV1* ti);
|
||||
~CTextInput();
|
||||
CTextInput(WP<CTextInputV1> ti);
|
||||
|
||||
bool isV3();
|
||||
void enter(SP<CWLSurfaceResource> pSurface);
|
||||
|
@ -43,12 +42,7 @@ class CTextInput {
|
|||
WP<CWLSurfaceResource> pFocusedSurface;
|
||||
int enterLocks = 0;
|
||||
WP<CTextInputV3> pV3Input;
|
||||
STextInputV1* pV1Input = nullptr;
|
||||
|
||||
DYNLISTENER(textInputEnable);
|
||||
DYNLISTENER(textInputDisable);
|
||||
DYNLISTENER(textInputCommit);
|
||||
DYNLISTENER(textInputDestroy);
|
||||
WP<CTextInputV1> pV1Input;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener enable;
|
||||
|
@ -58,4 +52,4 @@ class CTextInput {
|
|||
CHyprSignalListener surfaceUnmap;
|
||||
CHyprSignalListener surfaceDestroy;
|
||||
} listeners;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,13 +6,15 @@
|
|||
#include "../SeatManager.hpp"
|
||||
|
||||
void CInputManager::onTouchDown(ITouch::SDownEvent e) {
|
||||
m_bLastInputTouch = true;
|
||||
|
||||
static auto PSWIPETOUCH = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_touch");
|
||||
static auto PGAPSOUTDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
|
||||
auto* const PGAPSOUT = (CCssGapData*)(PGAPSOUTDATA.ptr())->getData();
|
||||
// TODO: WORKSPACERULE.gapsOut.value_or()
|
||||
auto gapsOut = *PGAPSOUT;
|
||||
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
|
||||
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_invert");
|
||||
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_touch_invert");
|
||||
EMIT_HOOK_EVENT_CANCELLABLE("touchDown", e);
|
||||
|
||||
auto PMONITOR = g_pCompositor->getMonitorFromName(!e.device->boundOutput.empty() ? e.device->boundOutput : "");
|
||||
|
@ -54,8 +56,6 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
|
|||
}
|
||||
}
|
||||
|
||||
m_bLastInputTouch = true;
|
||||
|
||||
m_sTouchData.touchFocusWindow = m_pFoundWindowToFocus;
|
||||
m_sTouchData.touchFocusSurface = m_pFoundSurfaceToFocus;
|
||||
m_sTouchData.touchFocusLS = m_pFoundLSToFocus;
|
||||
|
@ -83,6 +83,8 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
|
|||
}
|
||||
|
||||
void CInputManager::onTouchUp(ITouch::SUpEvent e) {
|
||||
m_bLastInputTouch = true;
|
||||
|
||||
EMIT_HOOK_EVENT_CANCELLABLE("touchUp", e);
|
||||
if (m_sActiveSwipe.pWorkspaceBegin) {
|
||||
// If there was a swipe from this finger, end it.
|
||||
|
@ -96,6 +98,8 @@ void CInputManager::onTouchUp(ITouch::SUpEvent e) {
|
|||
}
|
||||
|
||||
void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
|
||||
m_bLastInputTouch = true;
|
||||
|
||||
EMIT_HOOK_EVENT_CANCELLABLE("touchMove", e);
|
||||
if (m_sActiveSwipe.pWorkspaceBegin) {
|
||||
// Do nothing if this is using a different finger.
|
||||
|
|
|
@ -14,7 +14,7 @@ executable('Hyprland', src,
|
|||
dependency('cairo'),
|
||||
dependency('hyprcursor', version: '>=0.1.7'),
|
||||
dependency('hyprlang', version: '>= 0.3.2'),
|
||||
dependency('hyprutils', version: '>= 0.2.0'),
|
||||
dependency('hyprutils', version: '>= 0.2.1'),
|
||||
dependency('libdrm'),
|
||||
dependency('egl'),
|
||||
dependency('xkbcommon'),
|
||||
|
|
|
@ -80,6 +80,8 @@ CPlugin* CPluginSystem::loadPlugin(const std::string& path) {
|
|||
PLUGIN->version = PLUGINDATA.version;
|
||||
PLUGIN->name = PLUGINDATA.name;
|
||||
|
||||
g_pConfigManager->m_bForceReload = true;
|
||||
|
||||
Debug::log(LOG, " [PluginSystem] Plugin {} loaded. Handle: {:x}, path: \"{}\", author: \"{}\", description: \"{}\", version: \"{}\"", PLUGINDATA.name, (uintptr_t)MODULE, path,
|
||||
PLUGINDATA.author, PLUGINDATA.description, PLUGINDATA.version);
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ void CAlphaModifierProtocol::destroyModifier(CAlphaModifier* modifier) {
|
|||
|
||||
void CAlphaModifierProtocol::onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, SP<CWLSurfaceResource> surface) {
|
||||
if (std::find_if(m_mAlphaModifiers.begin(), m_mAlphaModifiers.end(), [surface](const auto& e) { return e.first == surface; }) != m_mAlphaModifiers.end()) {
|
||||
LOGM(ERR, "AlphaModifier already present for surface {:x}", (uintptr_t)surface);
|
||||
LOGM(ERR, "AlphaModifier already present for surface {:x}", (uintptr_t)surface.get());
|
||||
pMgr->error(WP_ALPHA_MODIFIER_V1_ERROR_ALREADY_CONSTRUCTED, "AlphaModifier already present");
|
||||
return;
|
||||
}
|
||||
|
@ -100,4 +100,4 @@ void CAlphaModifierProtocol::onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id,
|
|||
m_mAlphaModifiers.erase(surface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurf
|
|||
return;
|
||||
}
|
||||
|
||||
if ((acquireTimeline || releaseTimeline) && !surface->pending.buffer) {
|
||||
if ((acquireTimeline || releaseTimeline) && !surface->pending.texture) {
|
||||
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
|
||||
surface->pending.rejected = true;
|
||||
return;
|
||||
|
|
|
@ -51,7 +51,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
|
|||
}
|
||||
}
|
||||
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_FULL);
|
||||
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_FULLSCREEN, true);
|
||||
g_pHyprRenderer->damageWindow(PWINDOW);
|
||||
});
|
||||
|
||||
|
@ -64,7 +64,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
|
|||
if (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN)
|
||||
return;
|
||||
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, false);
|
||||
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_FULLSCREEN, false);
|
||||
});
|
||||
|
||||
resource->setSetMaximized([this](CZwlrForeignToplevelHandleV1* p) {
|
||||
|
@ -81,7 +81,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
|
|||
return;
|
||||
}
|
||||
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_MAXIMIZED);
|
||||
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, true);
|
||||
});
|
||||
|
||||
resource->setUnsetMaximized([this](CZwlrForeignToplevelHandleV1* p) {
|
||||
|
@ -93,7 +93,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
|
|||
if (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE)
|
||||
return;
|
||||
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, false);
|
||||
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, false);
|
||||
});
|
||||
|
||||
resource->setClose([this](CZwlrForeignToplevelHandleV1* p) {
|
||||
|
@ -155,9 +155,9 @@ void CForeignToplevelHandleWlr::sendState() {
|
|||
*p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED;
|
||||
}
|
||||
|
||||
if (PWINDOW->m_bIsFullscreen) {
|
||||
if (PWINDOW->isFullscreen()) {
|
||||
auto p = (uint32_t*)wl_array_add(&state, sizeof(uint32_t));
|
||||
if (PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL)
|
||||
if (PWINDOW->isEffectiveInternalFSMode(FSMODE_FULLSCREEN))
|
||||
*p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN;
|
||||
else
|
||||
*p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED;
|
||||
|
|
|
@ -28,7 +28,7 @@ void CFractionalScaleProtocol::onManagerResourceDestroy(wl_resource* res) {
|
|||
void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, SP<CWLSurfaceResource> surface) {
|
||||
for (auto& [k, v] : m_mAddons) {
|
||||
if (k == surface) {
|
||||
LOGM(ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface);
|
||||
LOGM(ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface.get());
|
||||
pMgr->error(WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS, "Fractional scale already exists");
|
||||
return;
|
||||
}
|
||||
|
@ -80,4 +80,4 @@ bool CFractionalScaleAddon::good() {
|
|||
|
||||
SP<CWLSurfaceResource> CFractionalScaleAddon::surf() {
|
||||
return surface.lock();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,110 +1,61 @@
|
|||
#include "GlobalShortcuts.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
|
||||
#define GLOBAL_SHORTCUTS_VERSION 1
|
||||
#define LOGM PROTO::globalShortcuts->protoLog
|
||||
|
||||
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->bindManager(client, data, version, id);
|
||||
}
|
||||
|
||||
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
|
||||
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->displayDestroy();
|
||||
}
|
||||
|
||||
void CGlobalShortcutsProtocolManager::displayDestroy() {
|
||||
wl_global_destroy(m_pGlobal);
|
||||
}
|
||||
|
||||
CGlobalShortcutsProtocolManager::CGlobalShortcutsProtocolManager() {
|
||||
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &hyprland_global_shortcuts_manager_v1_interface, GLOBAL_SHORTCUTS_VERSION, this, bindManagerInt);
|
||||
|
||||
if (!m_pGlobal) {
|
||||
Debug::log(ERR, "GlobalShortcutsManager could not start!");
|
||||
CShortcutClient::CShortcutClient(SP<CHyprlandGlobalShortcutsManagerV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
}
|
||||
|
||||
m_liDisplayDestroy.notify = handleDisplayDestroy;
|
||||
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
|
||||
resource->setOnDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); });
|
||||
resource->setDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); });
|
||||
|
||||
Debug::log(LOG, "GlobalShortcutsManager started successfully!");
|
||||
}
|
||||
|
||||
static void handleRegisterShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
|
||||
const char* trigger_description) {
|
||||
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->registerShortcut(client, resource, shortcut, id, app_id, description, trigger_description);
|
||||
}
|
||||
|
||||
static void handleDestroy(wl_client* client, wl_resource* resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static const struct hyprland_global_shortcuts_manager_v1_interface globalShortcutsManagerImpl = {
|
||||
.register_shortcut = handleRegisterShortcut,
|
||||
.destroy = handleDestroy,
|
||||
};
|
||||
|
||||
static const struct hyprland_global_shortcut_v1_interface shortcutImpl = {
|
||||
.destroy = handleDestroy,
|
||||
};
|
||||
|
||||
void CGlobalShortcutsProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
const auto RESOURCE = wl_resource_create(client, &hyprland_global_shortcuts_manager_v1_interface, version, id);
|
||||
wl_resource_set_implementation(RESOURCE, &globalShortcutsManagerImpl, this, nullptr);
|
||||
|
||||
Debug::log(LOG, "GlobalShortcutsManager bound successfully!");
|
||||
|
||||
m_vClients.emplace_back(std::make_unique<SShortcutClient>(client));
|
||||
}
|
||||
|
||||
SShortcutClient* CGlobalShortcutsProtocolManager::clientFromWlClient(wl_client* client) {
|
||||
for (auto& c : m_vClients) {
|
||||
if (c->client == client) {
|
||||
return c.get();
|
||||
resource->setRegisterShortcut([this](CHyprlandGlobalShortcutsManagerV1* pMgr, uint32_t shortcut, const char* id, const char* app_id, const char* description,
|
||||
const char* trigger_description) {
|
||||
if (PROTO::globalShortcuts->isTaken(id, app_id)) {
|
||||
resource->error(HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ALREADY_TAKEN, "Combination is taken");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
const auto PSHORTCUT = shortcuts.emplace_back(makeShared<SShortcut>(makeShared<CHyprlandGlobalShortcutV1>(resource->client(), resource->version(), shortcut)));
|
||||
PSHORTCUT->id = id;
|
||||
PSHORTCUT->description = description;
|
||||
PSHORTCUT->appid = app_id;
|
||||
PSHORTCUT->shortcut = shortcut;
|
||||
|
||||
static void onShortcutDestroy(wl_resource* pResource) {
|
||||
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->destroyShortcut(pResource);
|
||||
}
|
||||
|
||||
void CGlobalShortcutsProtocolManager::registerShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
|
||||
const char* trigger_description) {
|
||||
const auto PCLIENT = clientFromWlClient(client);
|
||||
|
||||
if (!PCLIENT) {
|
||||
Debug::log(ERR, "Error at global shortcuts: no client in register?");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& c : m_vClients) {
|
||||
for (auto& sh : c->shortcuts) {
|
||||
if (sh->appid == app_id && sh->id == id) {
|
||||
wl_resource_post_error(resource, HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ALREADY_TAKEN, "Combination is taken");
|
||||
return;
|
||||
}
|
||||
if (!PSHORTCUT->resource->resource()) {
|
||||
PSHORTCUT->resource->noMemory();
|
||||
shortcuts.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto PSHORTCUT = PCLIENT->shortcuts.emplace_back(std::make_unique<SShortcut>()).get();
|
||||
PSHORTCUT->id = id;
|
||||
PSHORTCUT->description = description;
|
||||
PSHORTCUT->appid = app_id;
|
||||
PSHORTCUT->shortcut = shortcut;
|
||||
PSHORTCUT->resource->setDestroy([this](CHyprlandGlobalShortcutV1* pMgr) { std::erase_if(shortcuts, [&](const auto& other) { return other->resource.get() == pMgr; }); });
|
||||
});
|
||||
}
|
||||
|
||||
PSHORTCUT->resource = wl_resource_create(client, &hyprland_global_shortcut_v1_interface, 1, shortcut);
|
||||
if (!PSHORTCUT->resource) {
|
||||
bool CShortcutClient::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CGlobalShortcutsProtocol::CGlobalShortcutsProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CGlobalShortcutsProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESROUCE = m_vClients.emplace_back(makeShared<CShortcutClient>(makeShared<CHyprlandGlobalShortcutsManagerV1>(client, ver, id)));
|
||||
|
||||
if (!RESROUCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
std::erase_if(PCLIENT->shortcuts, [&](const auto& other) { return other.get() == PSHORTCUT; });
|
||||
m_vClients.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(PSHORTCUT->resource, &shortcutImpl, this, &onShortcutDestroy);
|
||||
}
|
||||
|
||||
bool CGlobalShortcutsProtocolManager::globalShortcutExists(std::string appid, std::string trigger) {
|
||||
void CGlobalShortcutsProtocol::destroyResource(CShortcutClient* client) {
|
||||
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
|
||||
}
|
||||
|
||||
bool CGlobalShortcutsProtocol::isTaken(std::string appid, std::string trigger) {
|
||||
for (auto& c : m_vClients) {
|
||||
for (auto& sh : c->shortcuts) {
|
||||
if (sh->appid == appid && sh->id == trigger) {
|
||||
|
@ -116,7 +67,7 @@ bool CGlobalShortcutsProtocolManager::globalShortcutExists(std::string appid, st
|
|||
return false;
|
||||
}
|
||||
|
||||
void CGlobalShortcutsProtocolManager::sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed) {
|
||||
void CGlobalShortcutsProtocol::sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed) {
|
||||
for (auto& c : m_vClients) {
|
||||
for (auto& sh : c->shortcuts) {
|
||||
if (sh->appid == appid && sh->id == trigger) {
|
||||
|
@ -125,15 +76,15 @@ void CGlobalShortcutsProtocolManager::sendGlobalShortcutEvent(std::string appid,
|
|||
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
|
||||
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
|
||||
if (pressed)
|
||||
hyprland_global_shortcut_v1_send_pressed(sh->resource, tvSecHi, tvSecLo, now.tv_nsec);
|
||||
sh->resource->sendPressed(tvSecHi, tvSecLo, now.tv_nsec);
|
||||
else
|
||||
hyprland_global_shortcut_v1_send_released(sh->resource, tvSecHi, tvSecLo, now.tv_nsec);
|
||||
sh->resource->sendReleased(tvSecHi, tvSecLo, now.tv_nsec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SShortcut> CGlobalShortcutsProtocolManager::getAllShortcuts() {
|
||||
std::vector<SShortcut> CGlobalShortcutsProtocol::getAllShortcuts() {
|
||||
std::vector<SShortcut> copy;
|
||||
for (auto& c : m_vClients) {
|
||||
for (auto& sh : c->shortcuts) {
|
||||
|
@ -143,9 +94,3 @@ std::vector<SShortcut> CGlobalShortcutsProtocolManager::getAllShortcuts() {
|
|||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void CGlobalShortcutsProtocolManager::destroyShortcut(wl_resource* resource) {
|
||||
for (auto& c : m_vClients) {
|
||||
std::erase_if(c->shortcuts, [&](const auto& other) { return other->resource == resource; });
|
||||
}
|
||||
}
|
|
@ -1,39 +1,43 @@
|
|||
#pragma once
|
||||
#include "../defines.hpp"
|
||||
#include "hyprland-global-shortcuts-v1-protocol.h"
|
||||
#include "hyprland-global-shortcuts-v1.hpp"
|
||||
#include "../protocols/WaylandProtocol.hpp"
|
||||
#include <vector>
|
||||
|
||||
struct SShortcut {
|
||||
wl_resource* resource;
|
||||
std::string id, description, appid;
|
||||
uint32_t shortcut = 0;
|
||||
SP<CHyprlandGlobalShortcutV1> resource;
|
||||
std::string id, description, appid;
|
||||
uint32_t shortcut = 0;
|
||||
};
|
||||
|
||||
struct SShortcutClient {
|
||||
wl_client* client = nullptr;
|
||||
std::vector<std::unique_ptr<SShortcut>> shortcuts;
|
||||
};
|
||||
|
||||
class CGlobalShortcutsProtocolManager {
|
||||
class CShortcutClient {
|
||||
public:
|
||||
CGlobalShortcutsProtocolManager();
|
||||
CShortcutClient(SP<CHyprlandGlobalShortcutsManagerV1> resource);
|
||||
|
||||
bool good();
|
||||
|
||||
private:
|
||||
SP<CHyprlandGlobalShortcutsManagerV1> resource;
|
||||
std::vector<SP<SShortcut>> shortcuts;
|
||||
|
||||
friend class CGlobalShortcutsProtocol;
|
||||
};
|
||||
|
||||
class CGlobalShortcutsProtocol : IWaylandProtocol {
|
||||
public:
|
||||
CGlobalShortcutsProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
|
||||
void displayDestroy();
|
||||
void destroyResource(CShortcutClient* client);
|
||||
|
||||
void registerShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
|
||||
const char* trigger_description);
|
||||
void destroyShortcut(wl_resource* resource);
|
||||
|
||||
bool globalShortcutExists(std::string appid, std::string trigger);
|
||||
void sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed);
|
||||
|
||||
bool isTaken(std::string id, std::string app_id);
|
||||
std::vector<SShortcut> getAllShortcuts();
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<SShortcutClient>> m_vClients;
|
||||
std::vector<SP<CShortcutClient>> m_vClients;
|
||||
};
|
||||
|
||||
SShortcutClient* clientFromWlClient(wl_client* client);
|
||||
|
||||
wl_global* m_pGlobal = nullptr;
|
||||
wl_listener m_liDisplayDestroy;
|
||||
};
|
||||
namespace PROTO {
|
||||
inline UP<CGlobalShortcutsProtocol> globalShortcuts;
|
||||
};
|
||||
|
|
|
@ -107,14 +107,14 @@ CInputMethodPopupV2::CInputMethodPopupV2(SP<CZwpInputPopupSurfaceV2> resource_,
|
|||
});
|
||||
|
||||
listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) {
|
||||
if (pSurface->current.buffer && !mapped) {
|
||||
if (pSurface->current.texture && !mapped) {
|
||||
mapped = true;
|
||||
pSurface->map();
|
||||
events.map.emit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pSurface->current.buffer && mapped) {
|
||||
if (!pSurface->current.texture && mapped) {
|
||||
mapped = false;
|
||||
pSurface->unmap();
|
||||
events.unmap.emit();
|
||||
|
|
|
@ -11,7 +11,7 @@ void CLayerShellResource::SState::reset() {
|
|||
exclusive = 0;
|
||||
interactivity = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
||||
layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
||||
exclusiveEdge = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||
exclusiveEdge = (zwlrLayerSurfaceV1Anchor)0;
|
||||
desiredSize = {};
|
||||
margin = {0, 0, 0, 0};
|
||||
}
|
||||
|
@ -38,11 +38,13 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
|
|||
PROTO::layerShell->destroyResource(this);
|
||||
});
|
||||
|
||||
listeners.unmapSurface = surf_->events.unmap.registerListener([this](std::any d) { events.unmap.emit(); });
|
||||
|
||||
listeners.commitSurface = surf_->events.commit.registerListener([this](std::any d) {
|
||||
current = pending;
|
||||
pending.committed = 0;
|
||||
|
||||
bool attachedBuffer = surface->current.buffer;
|
||||
bool attachedBuffer = surface->current.texture;
|
||||
|
||||
if (attachedBuffer && !configured) {
|
||||
surface->error(-1, "layerSurface was not configured, but a buffer was attached");
|
||||
|
@ -71,8 +73,8 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
|
|||
|
||||
if (!attachedBuffer && mapped) {
|
||||
mapped = false;
|
||||
surface->unmap();
|
||||
events.unmap.emit();
|
||||
surface->unmap();
|
||||
configured = false;
|
||||
return;
|
||||
}
|
||||
|
@ -152,6 +154,16 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
|
|||
});
|
||||
|
||||
resource->setSetExclusiveEdge([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1Anchor anchor) {
|
||||
if (anchor > (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
|
||||
r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, "Invalid exclusive edge");
|
||||
return;
|
||||
}
|
||||
|
||||
if (anchor && (!pending.anchor || !(pending.anchor & anchor))) {
|
||||
r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, "Exclusive edge doesn't align with anchor");
|
||||
return;
|
||||
}
|
||||
|
||||
pending.committed |= STATE_EDGE;
|
||||
pending.exclusiveEdge = anchor;
|
||||
});
|
||||
|
@ -163,10 +175,6 @@ CLayerShellResource::~CLayerShellResource() {
|
|||
surface->resetRole();
|
||||
}
|
||||
|
||||
eSurfaceRole CLayerShellResource::role() {
|
||||
return SURFACE_ROLE_LAYER_SHELL;
|
||||
}
|
||||
|
||||
bool CLayerShellResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
@ -233,8 +241,12 @@ void CLayerShellProtocol::onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id
|
|||
return;
|
||||
}
|
||||
|
||||
SURF->role = RESOURCE;
|
||||
SURF->role = makeShared<CLayerShellRole>(RESOURCE);
|
||||
g_pCompositor->m_vLayers.emplace_back(CLayerSurface::create(RESOURCE));
|
||||
|
||||
LOGM(LOG, "New wlr_layer_surface {:x}", (uintptr_t)RESOURCE.get());
|
||||
}
|
||||
|
||||
CLayerShellRole::CLayerShellRole(SP<CLayerShellResource> ls) : layerSurface(ls) {
|
||||
;
|
||||
}
|
||||
|
|
|
@ -12,16 +12,26 @@
|
|||
|
||||
class CMonitor;
|
||||
class CWLSurfaceResource;
|
||||
class CLayerShellResource;
|
||||
|
||||
class CLayerShellResource : public ISurfaceRole {
|
||||
class CLayerShellRole : public ISurfaceRole {
|
||||
public:
|
||||
CLayerShellRole(SP<CLayerShellResource> ls);
|
||||
|
||||
virtual eSurfaceRole role() {
|
||||
return SURFACE_ROLE_LAYER_SHELL;
|
||||
}
|
||||
|
||||
WP<CLayerShellResource> layerSurface;
|
||||
};
|
||||
class CLayerShellResource {
|
||||
public:
|
||||
CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<CWLSurfaceResource> surf_, std::string namespace_, CMonitor* pMonitor, zwlrLayerShellV1Layer layer);
|
||||
~CLayerShellResource();
|
||||
|
||||
bool good();
|
||||
void configure(const Vector2D& size);
|
||||
void sendClosed();
|
||||
virtual eSurfaceRole role();
|
||||
bool good();
|
||||
void configure(const Vector2D& size);
|
||||
void sendClosed();
|
||||
|
||||
enum eCommittedState {
|
||||
STATE_SIZE = (1 << 0),
|
||||
|
@ -47,7 +57,7 @@ class CLayerShellResource : public ISurfaceRole {
|
|||
Vector2D desiredSize;
|
||||
zwlrLayerSurfaceV1KeyboardInteractivity interactivity = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
||||
zwlrLayerShellV1Layer layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
|
||||
zwlrLayerSurfaceV1Anchor exclusiveEdge = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||
zwlrLayerSurfaceV1Anchor exclusiveEdge = (zwlrLayerSurfaceV1Anchor)0;
|
||||
uint32_t committed = 0;
|
||||
|
||||
struct {
|
||||
|
@ -70,6 +80,7 @@ class CLayerShellResource : public ISurfaceRole {
|
|||
struct {
|
||||
CHyprSignalListener commitSurface;
|
||||
CHyprSignalListener destroySurface;
|
||||
CHyprSignalListener unmapSurface;
|
||||
} listeners;
|
||||
|
||||
bool closed = false;
|
||||
|
|
|
@ -60,7 +60,7 @@ void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespe
|
|||
if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION)
|
||||
flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION;
|
||||
|
||||
if (data->wasPresented && when)
|
||||
if (data->wasPresented)
|
||||
resource->sendPresented((uint32_t)(when->tv_sec >> 32), (uint32_t)(when->tv_sec & 0xFFFFFFFF), (uint32_t)(when->tv_nsec), untilRefreshNs, (uint32_t)(seq >> 32),
|
||||
(uint32_t)(seq & 0xFFFFFFFF), (wpPresentationFeedbackKind)flags);
|
||||
else
|
||||
|
@ -104,6 +104,14 @@ void CPresentationProtocol::onGetFeedback(CWpPresentation* pMgr, wl_resource* su
|
|||
}
|
||||
|
||||
void CPresentationProtocol::onPresented(CMonitor* pMonitor, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) {
|
||||
timespec now;
|
||||
timespec* presentedAt = when;
|
||||
if (!presentedAt) {
|
||||
// just put the current time, we don't have anything better
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
when = &now;
|
||||
}
|
||||
|
||||
for (auto& feedback : m_vFeedbacks) {
|
||||
if (!feedback->surface)
|
||||
continue;
|
||||
|
|
|
@ -9,238 +9,39 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#define SCREENCOPY_VERSION 3
|
||||
#define LOGM PROTO::screencopy->protoLog
|
||||
|
||||
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->bindManager(client, data, version, id);
|
||||
CScreencopyFrame::~CScreencopyFrame() {
|
||||
if (buffer && buffer->locked())
|
||||
buffer->unlock();
|
||||
}
|
||||
|
||||
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->displayDestroy();
|
||||
}
|
||||
CScreencopyFrame::CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource_, int32_t overlay_cursor, wl_resource* output, CBox box_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
void CScreencopyProtocolManager::displayDestroy() {
|
||||
wl_global_destroy(m_pGlobal);
|
||||
}
|
||||
overlayCursor = !!overlay_cursor;
|
||||
pMonitor = CWLOutputResource::fromResource(output)->monitor.get();
|
||||
|
||||
static SScreencopyFrame* frameFromResource(wl_resource*);
|
||||
|
||||
CScreencopyProtocolManager::CScreencopyProtocolManager() {
|
||||
|
||||
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &zwlr_screencopy_manager_v1_interface, SCREENCOPY_VERSION, this, bindManagerInt);
|
||||
|
||||
if (!m_pGlobal) {
|
||||
Debug::log(ERR, "ScreencopyProtocolManager could not start! Screensharing will not work!");
|
||||
if (!pMonitor) {
|
||||
LOGM(ERR, "Client requested sharing of a monitor that doesnt exist");
|
||||
resource->sendFailed();
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
m_liDisplayDestroy.notify = handleDisplayDestroy;
|
||||
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
|
||||
|
||||
Debug::log(LOG, "ScreencopyProtocolManager started successfully!");
|
||||
|
||||
m_pSoftwareCursorTimer = makeShared<CEventLoopTimer>(
|
||||
std::nullopt,
|
||||
[this](SP<CEventLoopTimer> self, void* data) {
|
||||
// TODO: make it per-monitor
|
||||
for (auto& m : g_pCompositor->m_vMonitors) {
|
||||
g_pPointerManager->unlockSoftwareForMonitor(m);
|
||||
}
|
||||
m_bTimerArmed = false;
|
||||
|
||||
Debug::log(LOG, "[screencopy] Releasing software cursor lock");
|
||||
},
|
||||
nullptr);
|
||||
g_pEventLoopManager->addTimer(m_pSoftwareCursorTimer);
|
||||
}
|
||||
|
||||
static void handleCaptureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output) {
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output);
|
||||
}
|
||||
|
||||
static void handleCaptureRegion(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, int32_t x, int32_t y, int32_t width,
|
||||
int32_t height) {
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output, {x, y, width, height});
|
||||
}
|
||||
|
||||
static void handleDestroy(wl_client* client, wl_resource* resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) {
|
||||
const auto PFRAME = frameFromResource(resource);
|
||||
|
||||
if (!PFRAME)
|
||||
return;
|
||||
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->copyFrame(client, resource, buffer);
|
||||
}
|
||||
|
||||
static void handleCopyWithDamage(wl_client* client, wl_resource* resource, wl_resource* buffer) {
|
||||
const auto PFRAME = frameFromResource(resource);
|
||||
|
||||
if (!PFRAME)
|
||||
return;
|
||||
|
||||
PFRAME->withDamage = true;
|
||||
handleCopyFrame(client, resource, buffer);
|
||||
}
|
||||
|
||||
static void handleDestroyFrame(wl_client* client, wl_resource* resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static const struct zwlr_screencopy_manager_v1_interface screencopyMgrImpl = {
|
||||
.capture_output = handleCaptureOutput,
|
||||
.capture_output_region = handleCaptureRegion,
|
||||
.destroy = handleDestroy,
|
||||
};
|
||||
|
||||
static const struct zwlr_screencopy_frame_v1_interface screencopyFrameImpl = {
|
||||
.copy = handleCopyFrame,
|
||||
.destroy = handleDestroyFrame,
|
||||
.copy_with_damage = handleCopyWithDamage,
|
||||
};
|
||||
|
||||
static CScreencopyClient* clientFromResource(wl_resource* resource) {
|
||||
ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_manager_v1_interface, &screencopyMgrImpl));
|
||||
return (CScreencopyClient*)wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static SScreencopyFrame* frameFromResource(wl_resource* resource) {
|
||||
ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_frame_v1_interface, &screencopyFrameImpl));
|
||||
return (SScreencopyFrame*)wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::removeClient(CScreencopyClient* client, bool force) {
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
if (!force) {
|
||||
if (!client || client->ref <= 0)
|
||||
return;
|
||||
|
||||
if (--client->ref != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
m_lClients.remove(*client); // TODO: this doesn't get cleaned up after sharing app exits???
|
||||
|
||||
for (auto& f : m_lFrames) {
|
||||
// avoid dangling ptrs
|
||||
if (f.client == client)
|
||||
f.client = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void handleManagerResourceDestroy(wl_resource* resource) {
|
||||
const auto PCLIENT = clientFromResource(resource);
|
||||
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->removeClient(PCLIENT, true);
|
||||
}
|
||||
|
||||
CScreencopyClient::~CScreencopyClient() {
|
||||
g_pHookSystem->unhook(tickCallback);
|
||||
}
|
||||
|
||||
CScreencopyClient::CScreencopyClient() {
|
||||
lastMeasure.reset();
|
||||
lastFrame.reset();
|
||||
tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); });
|
||||
}
|
||||
|
||||
void CScreencopyClient::onTick() {
|
||||
if (lastMeasure.getMillis() < 500)
|
||||
return;
|
||||
|
||||
framesInLastHalfSecond = frameCounter;
|
||||
frameCounter = 0;
|
||||
lastMeasure.reset();
|
||||
|
||||
const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0;
|
||||
const bool FRAMEAWAITING = std::ranges::any_of(g_pProtocolManager->m_pScreencopyProtocolManager->m_lFrames, [&](const auto& frame) { return frame.client == this; }) ||
|
||||
std::ranges::any_of(g_pProtocolManager->m_pToplevelExportProtocolManager->m_lFrames, [&](const auto& frame) { return frame.client == this; });
|
||||
|
||||
if (framesInLastHalfSecond > 3 && !sentScreencast) {
|
||||
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)});
|
||||
sentScreencast = true;
|
||||
} else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) {
|
||||
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)});
|
||||
sentScreencast = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
const auto PCLIENT = &m_lClients.emplace_back();
|
||||
|
||||
PCLIENT->resource = wl_resource_create(client, &zwlr_screencopy_manager_v1_interface, version, id);
|
||||
|
||||
if (!PCLIENT->resource) {
|
||||
Debug::log(ERR, "ScreencopyProtocolManager could not bind! (out of memory?)");
|
||||
m_lClients.remove(*PCLIENT);
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
PCLIENT->ref = 1;
|
||||
|
||||
wl_resource_set_implementation(PCLIENT->resource, &screencopyMgrImpl, PCLIENT, handleManagerResourceDestroy);
|
||||
|
||||
Debug::log(LOG, "ScreencopyProtocolManager bound successfully!");
|
||||
}
|
||||
|
||||
static void handleFrameResourceDestroy(wl_resource* resource) {
|
||||
const auto PFRAME = frameFromResource(resource);
|
||||
|
||||
g_pProtocolManager->m_pScreencopyProtocolManager->removeFrame(PFRAME);
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::removeFrame(SScreencopyFrame* frame, bool force) {
|
||||
if (!frame)
|
||||
return;
|
||||
|
||||
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; });
|
||||
|
||||
wl_resource_set_user_data(frame->resource, nullptr);
|
||||
if (frame->buffer && frame->buffer->locked())
|
||||
frame->buffer->unlock();
|
||||
removeClient(frame->client, force);
|
||||
m_lFrames.remove(*frame);
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, CBox box) {
|
||||
const auto PCLIENT = clientFromResource(resource);
|
||||
|
||||
const auto PFRAME = &m_lFrames.emplace_back();
|
||||
PFRAME->overlayCursor = !!overlay_cursor;
|
||||
PFRAME->resource = wl_resource_create(client, &zwlr_screencopy_frame_v1_interface, wl_resource_get_version(resource), frame);
|
||||
PFRAME->pMonitor = CWLOutputResource::fromResource(output)->monitor.get();
|
||||
|
||||
if (!PFRAME->pMonitor) {
|
||||
Debug::log(ERR, "client requested sharing of a monitor that doesnt exist");
|
||||
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PFRAME->resource) {
|
||||
Debug::log(ERR, "Couldn't alloc frame for sharing! (no memory)");
|
||||
removeFrame(PFRAME);
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(PFRAME->resource, &screencopyFrameImpl, PFRAME, handleFrameResourceDestroy);
|
||||
|
||||
PFRAME->client = PCLIENT;
|
||||
PCLIENT->ref++;
|
||||
resource->setOnDestroy([this](CZwlrScreencopyFrameV1* pMgr) { PROTO::screencopy->destroyResource(this); });
|
||||
resource->setDestroy([this](CZwlrScreencopyFrameV1* pFrame) { PROTO::screencopy->destroyResource(this); });
|
||||
resource->setCopy([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { this->copy(pFrame, res); });
|
||||
resource->setCopyWithDamage([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) {
|
||||
withDamage = true;
|
||||
this->copy(pFrame, res);
|
||||
});
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
if (g_pHyprOpenGL->m_mMonitorRenderResources.contains(PFRAME->pMonitor)) {
|
||||
const auto& RDATA = g_pHyprOpenGL->m_mMonitorRenderResources.at(PFRAME->pMonitor);
|
||||
if (g_pHyprOpenGL->m_mMonitorRenderResources.contains(pMonitor)) {
|
||||
const auto& RDATA = g_pHyprOpenGL->m_mMonitorRenderResources.at(pMonitor);
|
||||
// bind the fb for its format. Suppress gl errors.
|
||||
#ifndef GLES2
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, RDATA.offloadFB.m_iFb);
|
||||
|
@ -248,246 +49,212 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, RDATA.offloadFB.m_iFb);
|
||||
#endif
|
||||
} else
|
||||
Debug::log(ERR, "No RDATA in screencopy???");
|
||||
LOGM(ERR, "No RDATA in screencopy???");
|
||||
|
||||
PFRAME->shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PFRAME->pMonitor);
|
||||
if (PFRAME->shmFormat == DRM_FORMAT_INVALID) {
|
||||
Debug::log(ERR, "No format supported by renderer in capture output");
|
||||
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
shmFormat = g_pHyprOpenGL->getPreferredReadFormat(pMonitor);
|
||||
if (shmFormat == DRM_FORMAT_INVALID) {
|
||||
LOGM(ERR, "No format supported by renderer in capture output");
|
||||
resource->sendFailed();
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(PFRAME->shmFormat);
|
||||
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(shmFormat);
|
||||
if (!PSHMINFO) {
|
||||
Debug::log(ERR, "No pixel format supported by renderer in capture output");
|
||||
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
LOGM(ERR, "No pixel format supported by renderer in capture output");
|
||||
resource->sendFailed();
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
PFRAME->dmabufFormat = PFRAME->pMonitor->output->state->state().drmFormat;
|
||||
dmabufFormat = pMonitor->output->state->state().drmFormat;
|
||||
|
||||
if (box.width == 0 && box.height == 0)
|
||||
PFRAME->box = {0, 0, (int)(PFRAME->pMonitor->vecSize.x), (int)(PFRAME->pMonitor->vecSize.y)};
|
||||
if (box_.width == 0 && box_.height == 0)
|
||||
box = {0, 0, (int)(pMonitor->vecSize.x), (int)(pMonitor->vecSize.y)};
|
||||
else {
|
||||
PFRAME->box = box;
|
||||
box = box_;
|
||||
}
|
||||
|
||||
PFRAME->box.transform(wlTransformToHyprutils(PFRAME->pMonitor->transform), PFRAME->pMonitor->vecTransformedSize.x, PFRAME->pMonitor->vecTransformedSize.y)
|
||||
.scale(PFRAME->pMonitor->scale)
|
||||
.round();
|
||||
box.transform(wlTransformToHyprutils(pMonitor->transform), pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y).scale(pMonitor->scale).round();
|
||||
|
||||
PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w);
|
||||
shmStride = FormatUtils::minStride(PSHMINFO, box.w);
|
||||
|
||||
zwlr_screencopy_frame_v1_send_buffer(PFRAME->resource, FormatUtils::drmToShm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride);
|
||||
resource->sendBuffer(FormatUtils::drmToShm(shmFormat), box.width, box.height, shmStride);
|
||||
|
||||
if (wl_resource_get_version(resource) >= 3) {
|
||||
if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) {
|
||||
zwlr_screencopy_frame_v1_send_linux_dmabuf(PFRAME->resource, PFRAME->dmabufFormat, PFRAME->box.width, PFRAME->box.height);
|
||||
if (resource->version() >= 3) {
|
||||
if (dmabufFormat != DRM_FORMAT_INVALID) {
|
||||
resource->sendLinuxDmabuf(dmabufFormat, box.width, box.height);
|
||||
}
|
||||
|
||||
zwlr_screencopy_frame_v1_send_buffer_done(PFRAME->resource);
|
||||
resource->sendBufferDone();
|
||||
}
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) {
|
||||
const auto PFRAME = frameFromResource(resource);
|
||||
|
||||
if (!PFRAME) {
|
||||
Debug::log(ERR, "No frame in copyFrame??");
|
||||
void CScreencopyFrame::copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer_) {
|
||||
if (!good()) {
|
||||
LOGM(ERR, "No frame in copyFrame??");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_pCompositor->monitorExists(PFRAME->pMonitor)) {
|
||||
Debug::log(ERR, "client requested sharing of a monitor that is gone");
|
||||
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
if (!g_pCompositor->monitorExists(pMonitor)) {
|
||||
LOGM(ERR, "Client requested sharing of a monitor that is gone");
|
||||
resource->sendFailed();
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PBUFFER = CWLBufferResource::fromResource(buffer);
|
||||
const auto PBUFFER = CWLBufferResource::fromResource(buffer_);
|
||||
if (!PBUFFER) {
|
||||
Debug::log(ERR, "[sc] invalid buffer in {:x}", (uintptr_t)PFRAME);
|
||||
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
|
||||
removeFrame(PFRAME);
|
||||
LOGM(ERR, "Invalid buffer in {:x}", (uintptr_t)this);
|
||||
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
PBUFFER->buffer->lock();
|
||||
|
||||
if (PBUFFER->buffer->size != PFRAME->box.size()) {
|
||||
Debug::log(ERR, "[sc] invalid dimensions in {:x}", (uintptr_t)PFRAME);
|
||||
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
|
||||
removeFrame(PFRAME);
|
||||
if (PBUFFER->buffer->size != box.size()) {
|
||||
LOGM(ERR, "Invalid dimensions in {:x}", (uintptr_t)this);
|
||||
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PFRAME->buffer) {
|
||||
Debug::log(ERR, "[sc] buffer used in {:x}", (uintptr_t)PFRAME);
|
||||
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
||||
removeFrame(PFRAME);
|
||||
if (buffer) {
|
||||
LOGM(ERR, "Buffer used in {:x}", (uintptr_t)this);
|
||||
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) {
|
||||
PFRAME->bufferDMA = true;
|
||||
bufferDMA = true;
|
||||
|
||||
if (attrs.format != PFRAME->dmabufFormat) {
|
||||
Debug::log(ERR, "[sc] invalid buffer dma format in {:x}", (uintptr_t)PFRAME);
|
||||
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
removeFrame(PFRAME);
|
||||
if (attrs.format != dmabufFormat) {
|
||||
LOGM(ERR, "Invalid buffer dma format in {:x}", (uintptr_t)pFrame);
|
||||
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
} else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) {
|
||||
if (attrs.format != PFRAME->shmFormat) {
|
||||
Debug::log(ERR, "[sc] invalid buffer shm format in {:x}", (uintptr_t)PFRAME);
|
||||
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
removeFrame(PFRAME);
|
||||
if (attrs.format != shmFormat) {
|
||||
LOGM(ERR, "Invalid buffer shm format in {:x}", (uintptr_t)pFrame);
|
||||
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
} else if ((int)attrs.stride != PFRAME->shmStride) {
|
||||
Debug::log(ERR, "[sc] invalid buffer shm stride in {:x}", (uintptr_t)PFRAME);
|
||||
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
|
||||
removeFrame(PFRAME);
|
||||
} else if ((int)attrs.stride != shmStride) {
|
||||
LOGM(ERR, "Invalid buffer shm stride in {:x}", (uintptr_t)pFrame);
|
||||
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Debug::log(ERR, "[sc] invalid buffer type in {:x}", (uintptr_t)PFRAME);
|
||||
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
|
||||
removeFrame(PFRAME);
|
||||
LOGM(ERR, "Invalid buffer type in {:x}", (uintptr_t)pFrame);
|
||||
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
|
||||
PROTO::screencopy->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
PFRAME->buffer = PBUFFER->buffer;
|
||||
buffer = PBUFFER->buffer;
|
||||
|
||||
m_vFramesAwaitingWrite.emplace_back(PFRAME);
|
||||
PROTO::screencopy->m_vFramesAwaitingWrite.emplace_back(self);
|
||||
|
||||
g_pHyprRenderer->m_bDirectScanoutBlocked = true;
|
||||
if (PFRAME->overlayCursor && !PFRAME->lockedSWCursors) {
|
||||
PFRAME->lockedSWCursors = true;
|
||||
if (overlayCursor && !lockedSWCursors) {
|
||||
lockedSWCursors = true;
|
||||
// TODO: make it per-monitor
|
||||
if (!m_bTimerArmed) {
|
||||
if (!PROTO::screencopy->m_bTimerArmed) {
|
||||
for (auto& m : g_pCompositor->m_vMonitors) {
|
||||
g_pPointerManager->lockSoftwareForMonitor(m);
|
||||
}
|
||||
m_bTimerArmed = true;
|
||||
Debug::log(LOG, "[screencopy] Locking sw cursors due to screensharing");
|
||||
PROTO::screencopy->m_bTimerArmed = true;
|
||||
LOGM(LOG, "Locking sw cursors due to screensharing");
|
||||
}
|
||||
m_pSoftwareCursorTimer->updateTimeout(std::chrono::seconds(1));
|
||||
PROTO::screencopy->m_pSoftwareCursorTimer->updateTimeout(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
if (!PFRAME->withDamage)
|
||||
g_pHyprRenderer->damageMonitor(PFRAME->pMonitor);
|
||||
if (!withDamage)
|
||||
g_pHyprRenderer->damageMonitor(pMonitor);
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::onOutputCommit(CMonitor* pMonitor) {
|
||||
m_pLastMonitorBackBuffer = pMonitor->output->state->state().buffer;
|
||||
shareAllFrames(pMonitor);
|
||||
m_pLastMonitorBackBuffer.reset();
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::shareAllFrames(CMonitor* pMonitor) {
|
||||
if (m_vFramesAwaitingWrite.empty())
|
||||
return; // nothing to share
|
||||
|
||||
std::vector<SScreencopyFrame*> framesToRemove;
|
||||
|
||||
// share frame if correct output
|
||||
for (auto& f : m_vFramesAwaitingWrite) {
|
||||
if (!f->pMonitor || !f->buffer) {
|
||||
framesToRemove.push_back(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f->pMonitor != pMonitor)
|
||||
continue;
|
||||
|
||||
shareFrame(f);
|
||||
|
||||
f->client->lastFrame.reset();
|
||||
++f->client->frameCounter;
|
||||
|
||||
framesToRemove.push_back(f);
|
||||
}
|
||||
|
||||
for (auto& f : framesToRemove) {
|
||||
removeFrame(f);
|
||||
}
|
||||
|
||||
if (m_vFramesAwaitingWrite.empty()) {
|
||||
g_pHyprRenderer->m_bDirectScanoutBlocked = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::shareFrame(SScreencopyFrame* frame) {
|
||||
if (!frame->buffer)
|
||||
void CScreencopyFrame::share() {
|
||||
if (!buffer || !pMonitor)
|
||||
return;
|
||||
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (frame->bufferDMA) {
|
||||
if (!copyFrameDmabuf(frame)) {
|
||||
Debug::log(ERR, "[sc] dmabuf copy failed in {:x}", (uintptr_t)frame);
|
||||
zwlr_screencopy_frame_v1_send_failed(frame->resource);
|
||||
if (bufferDMA) {
|
||||
if (!copyDmabuf()) {
|
||||
LOGM(ERR, "Dmabuf copy failed in {:x}", (uintptr_t)this);
|
||||
resource->sendFailed();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!copyFrameShm(frame, &now)) {
|
||||
Debug::log(ERR, "[sc] shm copy failed in {:x}", (uintptr_t)frame);
|
||||
zwlr_screencopy_frame_v1_send_failed(frame->resource);
|
||||
if (!copyShm()) {
|
||||
LOGM(ERR, "Shm copy failed in {:x}", (uintptr_t)this);
|
||||
resource->sendFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
zwlr_screencopy_frame_v1_send_flags(frame->resource, flags);
|
||||
sendFrameDamage(frame);
|
||||
resource->sendFlags((zwlrScreencopyFrameV1Flags)0);
|
||||
if (withDamage) {
|
||||
// TODO: add a damage ring for this.
|
||||
resource->sendDamage(0, 0, buffer->size.x, buffer->size.y);
|
||||
}
|
||||
|
||||
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
|
||||
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
|
||||
zwlr_screencopy_frame_v1_send_ready(frame->resource, tvSecHi, tvSecLo, now.tv_nsec);
|
||||
resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec);
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::sendFrameDamage(SScreencopyFrame* frame) {
|
||||
if (!frame->withDamage)
|
||||
return;
|
||||
bool CScreencopyFrame::copyDmabuf() {
|
||||
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
|
||||
|
||||
// TODO:
|
||||
// add a damage ring for this.
|
||||
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
|
||||
|
||||
// for (auto& RECT : frame->pMonitor->lastFrameDamage.getRects()) {
|
||||
if (!g_pHyprRenderer->beginRender(pMonitor, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock(), nullptr, true)) {
|
||||
LOGM(ERR, "Can't copy: failed to begin rendering to dma frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if (frame->buffer->width < 1 || frame->buffer->height < 1 || frame->buffer->width - RECT.x1 < 1 || frame->buffer->height - RECT.y1 < 1) {
|
||||
// Debug::log(ERR, "[sc] Failed to send damage");
|
||||
// break;
|
||||
// }
|
||||
CBox monbox = CBox{0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y}
|
||||
.translate({-box.x, -box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh.
|
||||
.transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y);
|
||||
g_pHyprOpenGL->setMonitorTransformEnabled(true);
|
||||
g_pHyprOpenGL->setRenderModifEnabled(false);
|
||||
g_pHyprOpenGL->renderTexture(TEXTURE, &monbox, 1);
|
||||
g_pHyprOpenGL->setRenderModifEnabled(true);
|
||||
g_pHyprOpenGL->setMonitorTransformEnabled(false);
|
||||
|
||||
// zwlr_screencopy_frame_v1_send_damage(frame->resource, std::clamp(RECT.x1, 0, frame->buffer->width), std::clamp(RECT.y1, 0, frame->buffer->height),
|
||||
// std::clamp(RECT.x2 - RECT.x1, 0, frame->buffer->width - RECT.x1), std::clamp(RECT.y2 - RECT.y1, 0, frame->buffer->height - RECT.y1));
|
||||
// }
|
||||
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
|
||||
g_pHyprRenderer->endRender();
|
||||
|
||||
zwlr_screencopy_frame_v1_send_damage(frame->resource, 0, 0, frame->buffer->size.x, frame->buffer->size.y);
|
||||
LOGM(TRACE, "Copied frame via dma");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) {
|
||||
auto TEXTURE = makeShared<CTexture>(m_pLastMonitorBackBuffer);
|
||||
bool CScreencopyFrame::copyShm() {
|
||||
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
|
||||
|
||||
auto shm = frame->buffer->shm();
|
||||
auto [pixelData, fmt, bufLen] = frame->buffer->beginDataPtr(0); // no need for end, cuz it's shm
|
||||
auto shm = buffer->shm();
|
||||
auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm
|
||||
|
||||
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
CFramebuffer fb;
|
||||
fb.alloc(frame->box.w, frame->box.h, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : frame->pMonitor->output->state->state().drmFormat);
|
||||
fb.alloc(box.w, box.h, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : pMonitor->output->state->state().drmFormat);
|
||||
|
||||
if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb, true)) {
|
||||
Debug::log(ERR, "Screencopy: can't copy: failed to begin rendering");
|
||||
if (!g_pHyprRenderer->beginRender(pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb, true)) {
|
||||
LOGM(ERR, "Can't copy: failed to begin rendering");
|
||||
return false;
|
||||
}
|
||||
|
||||
CBox monbox = CBox{0, 0, frame->pMonitor->vecTransformedSize.x, frame->pMonitor->vecTransformedSize.y}.translate({-frame->box.x, -frame->box.y});
|
||||
CBox monbox = CBox{0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}.translate({-box.x, -box.y});
|
||||
g_pHyprOpenGL->setMonitorTransformEnabled(true);
|
||||
g_pHyprOpenGL->setRenderModifEnabled(false);
|
||||
g_pHyprOpenGL->renderTexture(TEXTURE, &monbox, 1);
|
||||
|
@ -502,7 +269,7 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec*
|
|||
|
||||
const auto PFORMAT = FormatUtils::getPixelFormatFromDRM(shm.format);
|
||||
if (!PFORMAT) {
|
||||
Debug::log(ERR, "Screencopy: can't copy: failed to find a pixel format");
|
||||
LOGM(ERR, "Can't copy: failed to find a pixel format");
|
||||
g_pHyprRenderer->endRender();
|
||||
return false;
|
||||
}
|
||||
|
@ -513,53 +280,164 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec*
|
|||
g_pHyprRenderer->endRender();
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
g_pHyprOpenGL->m_RenderData.pMonitor = frame->pMonitor;
|
||||
g_pHyprOpenGL->m_RenderData.pMonitor = pMonitor;
|
||||
fb.bind();
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
const auto drmFmt = FormatUtils::getPixelFormatFromDRM(shm.format);
|
||||
uint32_t packStride = FormatUtils::minStride(drmFmt, frame->box.w);
|
||||
uint32_t packStride = FormatUtils::minStride(drmFmt, box.w);
|
||||
|
||||
if (packStride == (uint32_t)shm.stride) {
|
||||
glReadPixels(0, 0, frame->box.w, frame->box.h, glFormat, PFORMAT->glType, pixelData);
|
||||
glReadPixels(0, 0, box.w, box.h, glFormat, PFORMAT->glType, pixelData);
|
||||
} else {
|
||||
for (size_t i = 0; i < frame->box.h; ++i) {
|
||||
for (size_t i = 0; i < box.h; ++i) {
|
||||
uint32_t y = i;
|
||||
glReadPixels(0, y, frame->box.w, 1, glFormat, PFORMAT->glType, ((unsigned char*)pixelData) + i * shm.stride);
|
||||
glReadPixels(0, y, box.w, 1, glFormat, PFORMAT->glType, ((unsigned char*)pixelData) + i * shm.stride);
|
||||
}
|
||||
}
|
||||
|
||||
g_pHyprOpenGL->m_RenderData.pMonitor = nullptr;
|
||||
|
||||
Debug::log(TRACE, "Screencopy: copied frame via shm");
|
||||
LOGM(TRACE, "Copied frame via shm");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) {
|
||||
auto TEXTURE = makeShared<CTexture>(m_pLastMonitorBackBuffer);
|
||||
bool CScreencopyFrame::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
|
||||
CScreencopyClient::~CScreencopyClient() {
|
||||
g_pHookSystem->unhook(tickCallback);
|
||||
}
|
||||
|
||||
if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer.lock(), nullptr, true)) {
|
||||
Debug::log(ERR, "Screencopy: can't copy: failed to begin rendering to dma frame");
|
||||
return false;
|
||||
CScreencopyClient::CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); });
|
||||
resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); });
|
||||
resource->setCaptureOutput(
|
||||
[this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output) { this->captureOutput(frame, overlayCursor, output, {}); });
|
||||
resource->setCaptureOutputRegion([this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output, int32_t x, int32_t y, int32_t w,
|
||||
int32_t h) { this->captureOutput(frame, overlayCursor, output, {x, y, w, h}); });
|
||||
|
||||
lastMeasure.reset();
|
||||
lastFrame.reset();
|
||||
tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); });
|
||||
}
|
||||
|
||||
void CScreencopyClient::captureOutput(uint32_t frame, int32_t overlayCursor_, wl_resource* output, CBox box) {
|
||||
const auto FRAME = PROTO::screencopy->m_vFrames.emplace_back(
|
||||
makeShared<CScreencopyFrame>(makeShared<CZwlrScreencopyFrameV1>(resource->client(), resource->version(), frame), overlayCursor_, output, box));
|
||||
|
||||
if (!FRAME->good()) {
|
||||
LOGM(ERR, "Couldn't alloc frame for sharing! (no memory)");
|
||||
resource->noMemory();
|
||||
PROTO::screencopy->destroyResource(FRAME.get());
|
||||
return;
|
||||
}
|
||||
|
||||
CBox monbox = CBox{0, 0, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y}
|
||||
.translate({-frame->box.x, -frame->box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh.
|
||||
.transform(wlTransformToHyprutils(invertTransform(frame->pMonitor->transform)), frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y);
|
||||
g_pHyprOpenGL->setMonitorTransformEnabled(true);
|
||||
g_pHyprOpenGL->setRenderModifEnabled(false);
|
||||
g_pHyprOpenGL->renderTexture(TEXTURE, &monbox, 1);
|
||||
g_pHyprOpenGL->setRenderModifEnabled(true);
|
||||
g_pHyprOpenGL->setMonitorTransformEnabled(false);
|
||||
|
||||
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
|
||||
g_pHyprRenderer->endRender();
|
||||
|
||||
Debug::log(TRACE, "Screencopy: copied frame via dma");
|
||||
|
||||
return true;
|
||||
FRAME->self = FRAME;
|
||||
FRAME->client = self;
|
||||
}
|
||||
|
||||
void CScreencopyClient::onTick() {
|
||||
if (lastMeasure.getMillis() < 500)
|
||||
return;
|
||||
|
||||
framesInLastHalfSecond = frameCounter;
|
||||
frameCounter = 0;
|
||||
lastMeasure.reset();
|
||||
|
||||
const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0;
|
||||
const bool FRAMEAWAITING = std::ranges::any_of(PROTO::screencopy->m_vFrames, [&](const auto& frame) { return frame->client.get() == this; });
|
||||
|
||||
if (framesInLastHalfSecond > 3 && !sentScreencast) {
|
||||
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)});
|
||||
sentScreencast = true;
|
||||
} else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) {
|
||||
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)});
|
||||
sentScreencast = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CScreencopyClient::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CScreencopyProtocol::CScreencopyProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
m_pSoftwareCursorTimer = makeShared<CEventLoopTimer>(
|
||||
std::nullopt,
|
||||
[this](SP<CEventLoopTimer> self, void* data) {
|
||||
// TODO: make it per-monitor
|
||||
for (auto& m : g_pCompositor->m_vMonitors) {
|
||||
g_pPointerManager->unlockSoftwareForMonitor(m);
|
||||
}
|
||||
m_bTimerArmed = false;
|
||||
|
||||
LOGM(LOG, "Releasing software cursor lock");
|
||||
},
|
||||
nullptr);
|
||||
g_pEventLoopManager->addTimer(m_pSoftwareCursorTimer);
|
||||
}
|
||||
|
||||
void CScreencopyProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto CLIENT = m_vClients.emplace_back(makeShared<CScreencopyClient>(makeShared<CZwlrScreencopyManagerV1>(client, ver, id)));
|
||||
|
||||
if (!CLIENT->good()) {
|
||||
LOGM(LOG, "Failed to bind client! (out of memory)");
|
||||
CLIENT->resource->noMemory();
|
||||
m_vClients.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
CLIENT->self = CLIENT;
|
||||
|
||||
LOGM(LOG, "Bound client successfully!");
|
||||
}
|
||||
|
||||
void CScreencopyProtocol::destroyResource(CScreencopyClient* client) {
|
||||
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
|
||||
std::erase_if(m_vFrames, [&](const auto& other) { return other->client.get() == client; });
|
||||
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other->client.get() == client; });
|
||||
}
|
||||
|
||||
void CScreencopyProtocol::destroyResource(CScreencopyFrame* frame) {
|
||||
std::erase_if(m_vFrames, [&](const auto& other) { return other.get() == frame; });
|
||||
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other.get() == frame; });
|
||||
}
|
||||
|
||||
void CScreencopyProtocol::onOutputCommit(CMonitor* pMonitor) {
|
||||
if (m_vFramesAwaitingWrite.empty()) {
|
||||
g_pHyprRenderer->m_bDirectScanoutBlocked = false;
|
||||
return; // nothing to share
|
||||
}
|
||||
|
||||
std::vector<WP<CScreencopyFrame>> framesToRemove;
|
||||
|
||||
// share frame if correct output
|
||||
for (auto& f : m_vFramesAwaitingWrite) {
|
||||
if (!f->pMonitor || !f->buffer) {
|
||||
framesToRemove.push_back(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f->pMonitor != pMonitor)
|
||||
continue;
|
||||
|
||||
f->share();
|
||||
|
||||
f->client->lastFrame.reset();
|
||||
++f->client->frameCounter;
|
||||
|
||||
framesToRemove.push_back(f);
|
||||
}
|
||||
|
||||
for (auto& f : framesToRemove) {
|
||||
destroyResource(f.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "wlr-screencopy-unstable-v1-protocol.h"
|
||||
#include "wlr-screencopy-unstable-v1.hpp"
|
||||
#include "WaylandProtocol.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
@ -20,87 +21,93 @@ enum eClientOwners {
|
|||
|
||||
class CScreencopyClient {
|
||||
public:
|
||||
CScreencopyClient();
|
||||
CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_);
|
||||
~CScreencopyClient();
|
||||
|
||||
int ref = 0;
|
||||
wl_resource* resource = nullptr;
|
||||
bool good();
|
||||
|
||||
eClientOwners clientOwner = CLIENT_SCREENCOPY;
|
||||
WP<CScreencopyClient> self;
|
||||
eClientOwners clientOwner = CLIENT_SCREENCOPY;
|
||||
|
||||
int frameCounter = 0;
|
||||
int framesInLastHalfSecond = 0;
|
||||
CTimer lastMeasure;
|
||||
CTimer lastFrame;
|
||||
bool sentScreencast = false;
|
||||
|
||||
void onTick();
|
||||
SP<HOOK_CALLBACK_FN> tickCallback;
|
||||
|
||||
bool operator==(const CScreencopyClient& other) const {
|
||||
return resource == other.resource;
|
||||
}
|
||||
};
|
||||
|
||||
struct SScreencopyFrame {
|
||||
wl_resource* resource = nullptr;
|
||||
CScreencopyClient* client = nullptr;
|
||||
|
||||
uint32_t shmFormat = 0;
|
||||
uint32_t dmabufFormat = 0;
|
||||
CBox box = {};
|
||||
int shmStride = 0;
|
||||
|
||||
bool overlayCursor = false;
|
||||
bool withDamage = false;
|
||||
bool lockedSWCursors = false;
|
||||
|
||||
bool bufferDMA = false;
|
||||
|
||||
WP<IHLBuffer> buffer;
|
||||
|
||||
CMonitor* pMonitor = nullptr;
|
||||
PHLWINDOWREF pWindow;
|
||||
|
||||
bool operator==(const SScreencopyFrame& other) const {
|
||||
return resource == other.resource && client == other.client;
|
||||
}
|
||||
};
|
||||
|
||||
class CScreencopyProtocolManager {
|
||||
public:
|
||||
CScreencopyProtocolManager();
|
||||
|
||||
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
|
||||
void removeClient(CScreencopyClient* client, bool force = false);
|
||||
void removeFrame(SScreencopyFrame* frame, bool force = false);
|
||||
void displayDestroy();
|
||||
|
||||
void captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, CBox box = {0, 0, 0, 0});
|
||||
|
||||
void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer);
|
||||
|
||||
void onOutputCommit(CMonitor* pMonitor);
|
||||
CTimer lastFrame;
|
||||
int frameCounter = 0;
|
||||
|
||||
private:
|
||||
wl_global* m_pGlobal = nullptr;
|
||||
std::list<SScreencopyFrame> m_lFrames;
|
||||
std::list<CScreencopyClient> m_lClients;
|
||||
SP<CZwlrScreencopyManagerV1> resource;
|
||||
|
||||
SP<CEventLoopTimer> m_pSoftwareCursorTimer;
|
||||
bool m_bTimerArmed = false;
|
||||
int framesInLastHalfSecond = 0;
|
||||
CTimer lastMeasure;
|
||||
bool sentScreencast = false;
|
||||
|
||||
wl_listener m_liDisplayDestroy;
|
||||
SP<HOOK_CALLBACK_FN> tickCallback;
|
||||
void onTick();
|
||||
|
||||
std::vector<SScreencopyFrame*> m_vFramesAwaitingWrite;
|
||||
void captureOutput(uint32_t frame, int32_t overlayCursor, wl_resource* output, CBox box);
|
||||
|
||||
SP<Aquamarine::IBuffer> m_pLastMonitorBackBuffer;
|
||||
friend class CScreencopyProtocol;
|
||||
};
|
||||
|
||||
void shareAllFrames(CMonitor* pMonitor);
|
||||
void shareFrame(SScreencopyFrame* frame);
|
||||
void sendFrameDamage(SScreencopyFrame* frame);
|
||||
bool copyFrameDmabuf(SScreencopyFrame* frame);
|
||||
bool copyFrameShm(SScreencopyFrame* frame, timespec* now);
|
||||
class CScreencopyFrame {
|
||||
public:
|
||||
CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource, int32_t overlay_cursor, wl_resource* output, CBox box);
|
||||
~CScreencopyFrame();
|
||||
|
||||
bool good();
|
||||
|
||||
SP<CScreencopyFrame> self;
|
||||
WP<CScreencopyClient> client;
|
||||
|
||||
private:
|
||||
SP<CZwlrScreencopyFrameV1> resource;
|
||||
|
||||
CMonitor* pMonitor = nullptr;
|
||||
bool overlayCursor = false;
|
||||
bool withDamage = false;
|
||||
bool lockedSWCursors = false;
|
||||
|
||||
WP<IHLBuffer> buffer;
|
||||
bool bufferDMA = false;
|
||||
uint32_t shmFormat = 0;
|
||||
uint32_t dmabufFormat = 0;
|
||||
int shmStride = 0;
|
||||
CBox box = {};
|
||||
|
||||
void copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer);
|
||||
bool copyDmabuf();
|
||||
bool copyShm();
|
||||
void share();
|
||||
|
||||
friend class CScreencopyProtocol;
|
||||
};
|
||||
|
||||
class CScreencopyProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CScreencopyProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
|
||||
void destroyResource(CScreencopyClient* resource);
|
||||
void destroyResource(CScreencopyFrame* resource);
|
||||
|
||||
void onOutputCommit(CMonitor* pMonitor);
|
||||
|
||||
private:
|
||||
std::vector<SP<CScreencopyFrame>> m_vFrames;
|
||||
std::vector<SP<CScreencopyFrame>> m_vFramesAwaitingWrite;
|
||||
std::vector<SP<CScreencopyClient>> m_vClients;
|
||||
|
||||
SP<CEventLoopTimer> m_pSoftwareCursorTimer;
|
||||
bool m_bTimerArmed = false;
|
||||
|
||||
void shareAllFrames(CMonitor* pMonitor);
|
||||
void shareFrame(CScreencopyFrame* frame);
|
||||
void sendFrameDamage(CScreencopyFrame* frame);
|
||||
bool copyFrameDmabuf(CScreencopyFrame* frame);
|
||||
bool copyFrameShm(CScreencopyFrame* frame, timespec* now);
|
||||
|
||||
friend class CScreencopyFrame;
|
||||
friend class CScreencopyClient;
|
||||
};
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CScreencopyProtocol> screencopy;
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ CSessionLockSurface::CSessionLockSurface(SP<CExtSessionLockSurfaceV1> resource_,
|
|||
resource->setAckConfigure([this](CExtSessionLockSurfaceV1* r, uint32_t serial) { ackdConfigure = true; });
|
||||
|
||||
listeners.surfaceCommit = pSurface->events.commit.registerListener([this](std::any d) {
|
||||
if (!pSurface->current.buffer) {
|
||||
if (!pSurface->current.texture) {
|
||||
LOGM(ERR, "SessionLock attached a null buffer");
|
||||
resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "Null buffer attached");
|
||||
return;
|
||||
|
|
|
@ -73,6 +73,9 @@ bool CKeyboardShortcutsInhibitProtocol::isInhibited() {
|
|||
if (!g_pCompositor->m_pLastFocus)
|
||||
return false;
|
||||
|
||||
if (const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_pLastFocus.lock()); PWINDOW && PWINDOW->m_sWindowData.noShortcutsInhibit.valueOrDefault())
|
||||
return false;
|
||||
|
||||
for (auto& in : m_vInhibitors) {
|
||||
if (in->surface() != g_pCompositor->m_pLastFocus)
|
||||
continue;
|
||||
|
|
|
@ -3,221 +3,125 @@
|
|||
#include "../Compositor.hpp"
|
||||
#include "core/Compositor.hpp"
|
||||
|
||||
#define TEXT_INPUT_VERSION 1
|
||||
#define LOGM PROTO::textInputV1->protoLog
|
||||
|
||||
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->bindManager(client, data, version, id);
|
||||
CTextInputV1::~CTextInputV1() {
|
||||
events.destroy.emit();
|
||||
}
|
||||
|
||||
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->displayDestroy();
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::displayDestroy() {
|
||||
wl_global_destroy(m_pGlobal);
|
||||
}
|
||||
|
||||
CTextInputV1ProtocolManager::CTextInputV1ProtocolManager() {
|
||||
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &zwp_text_input_manager_v1_interface, TEXT_INPUT_VERSION, this, bindManagerInt);
|
||||
|
||||
if (!m_pGlobal) {
|
||||
Debug::log(ERR, "TextInputV1Manager could not start!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_liDisplayDestroy.notify = handleDisplayDestroy;
|
||||
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
|
||||
|
||||
Debug::log(LOG, "TextInputV1Manager started successfully!");
|
||||
}
|
||||
|
||||
static void createTI(wl_client* client, wl_resource* resource, uint32_t id) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->createTI(client, resource, id);
|
||||
}
|
||||
|
||||
static const struct zwp_text_input_manager_v1_interface textInputManagerImpl = {
|
||||
.create_text_input = createTI,
|
||||
};
|
||||
|
||||
void CTextInputV1ProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
const auto RESOURCE = wl_resource_create(client, &zwp_text_input_manager_v1_interface, version, id);
|
||||
wl_resource_set_implementation(RESOURCE, &textInputManagerImpl, this, nullptr);
|
||||
|
||||
Debug::log(LOG, "TextInputV1Manager bound successfully!");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
static void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleActivate(client, resource, seat, surface);
|
||||
}
|
||||
|
||||
static void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleDeactivate(client, resource, seat);
|
||||
}
|
||||
|
||||
static void handleShowInputPanel(wl_client* client, wl_resource* resource) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleShowInputPanel(client, resource);
|
||||
}
|
||||
|
||||
static void handleHideInputPanel(wl_client* client, wl_resource* resource) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleHideInputPanel(client, resource);
|
||||
}
|
||||
|
||||
static void handleReset(wl_client* client, wl_resource* resource) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleReset(client, resource);
|
||||
}
|
||||
|
||||
static void handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetSurroundingText(client, resource, text, cursor, anchor);
|
||||
}
|
||||
|
||||
static void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetContentType(client, resource, hint, purpose);
|
||||
}
|
||||
|
||||
static void handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetCursorRectangle(client, resource, x, y, width, height);
|
||||
}
|
||||
|
||||
static void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetPreferredLanguage(client, resource, language);
|
||||
}
|
||||
|
||||
static void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleCommitState(client, resource, serial);
|
||||
}
|
||||
|
||||
static void handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index) {
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleInvokeAction(client, resource, button, index);
|
||||
}
|
||||
|
||||
static const struct zwp_text_input_v1_interface textInputImpl = {
|
||||
.activate = handleActivate,
|
||||
.deactivate = handleDeactivate,
|
||||
.show_input_panel = handleShowInputPanel,
|
||||
.hide_input_panel = handleHideInputPanel,
|
||||
.reset = handleReset,
|
||||
.set_surrounding_text = handleSetSurroundingText,
|
||||
.set_content_type = handleSetContentType,
|
||||
.set_cursor_rectangle = handleSetCursorRectangle,
|
||||
.set_preferred_language = handleSetPreferredLanguage,
|
||||
.commit_state = handleCommitState,
|
||||
.invoke_action = handleInvokeAction,
|
||||
};
|
||||
|
||||
void CTextInputV1ProtocolManager::removeTI(STextInputV1* pTI) {
|
||||
const auto TI = std::find_if(m_pClients.begin(), m_pClients.end(), [&](const auto& other) { return other->resourceCaller == pTI->resourceCaller; });
|
||||
if (TI == m_pClients.end())
|
||||
CTextInputV1::CTextInputV1(SP<CZwpTextInputV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
// if ((*TI)->resourceImpl)
|
||||
// wl_resource_destroy((*TI)->resourceImpl);
|
||||
resource->setOnDestroy([this](CZwpTextInputV1* pMgr) { PROTO::textInputV1->destroyResource(this); });
|
||||
|
||||
std::erase_if(m_pClients, [&](const auto& other) { return other.get() == pTI; });
|
||||
resource->setActivate([this](CZwpTextInputV1* pMgr, wl_resource* seat, wl_resource* surface) {
|
||||
if (!surface) {
|
||||
LOGM(WARN, "Text-input-v1 PTI{:x}: No surface to activate text input on!", (uintptr_t)this);
|
||||
return;
|
||||
}
|
||||
|
||||
active = true;
|
||||
events.enable.emit(CWLSurfaceResource::fromResource(surface));
|
||||
});
|
||||
|
||||
resource->setDeactivate([this](CZwpTextInputV1* pMgr, wl_resource* seat) {
|
||||
active = false;
|
||||
events.disable.emit();
|
||||
});
|
||||
|
||||
resource->setReset([this](CZwpTextInputV1* pMgr) {
|
||||
pendingSurrounding.isPending = false;
|
||||
pendingContentType.isPending = false;
|
||||
});
|
||||
|
||||
resource->setSetSurroundingText(
|
||||
[this](CZwpTextInputV1* pMgr, const char* text, uint32_t cursor, uint32_t anchor) { pendingSurrounding = {true, std::string(text), cursor, anchor}; });
|
||||
|
||||
resource->setSetContentType([this](CZwpTextInputV1* pMgr, uint32_t hint, uint32_t purpose) {
|
||||
pendingContentType = {true, hint == (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT ? (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE : hint,
|
||||
purpose > (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD ? hint + 1 : hint};
|
||||
});
|
||||
|
||||
resource->setSetCursorRectangle([this](CZwpTextInputV1* pMgr, int32_t x, int32_t y, int32_t width, int32_t height) { cursorRectangle = CBox{x, y, width, height}; });
|
||||
|
||||
resource->setCommitState([this](CZwpTextInputV1* pMgr, uint32_t serial_) {
|
||||
serial = serial_;
|
||||
events.onCommit.emit();
|
||||
});
|
||||
|
||||
// nothing
|
||||
resource->setShowInputPanel([this](CZwpTextInputV1* pMgr) {});
|
||||
resource->setHideInputPanel([this](CZwpTextInputV1* pMgr) {});
|
||||
resource->setSetPreferredLanguage([this](CZwpTextInputV1* pMgr, const char* language) {});
|
||||
resource->setInvokeAction([this](CZwpTextInputV1* pMgr, uint32_t button, uint32_t index) {});
|
||||
}
|
||||
|
||||
STextInputV1* tiFromResource(wl_resource* resource) {
|
||||
ASSERT(wl_resource_instance_of(resource, &zwp_text_input_v1_interface, &textInputImpl));
|
||||
return (STextInputV1*)wl_resource_get_user_data(resource);
|
||||
bool CTextInputV1::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
static void destroyTI(wl_resource* resource) {
|
||||
const auto TI = tiFromResource(resource);
|
||||
|
||||
if (!TI)
|
||||
return;
|
||||
|
||||
if (TI->resourceImpl) {
|
||||
wl_resource_set_user_data(resource, nullptr);
|
||||
}
|
||||
|
||||
TI->pTextInput->tiV1Destroyed();
|
||||
|
||||
g_pProtocolManager->m_pTextInputV1ProtocolManager->removeTI(TI);
|
||||
wl_client* CTextInputV1::client() {
|
||||
return resource->client();
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::createTI(wl_client* client, wl_resource* resource, uint32_t id) {
|
||||
const auto PTI = m_pClients.emplace_back(std::make_unique<STextInputV1>()).get();
|
||||
Debug::log(LOG, "New TI V1 at {:x}", (uintptr_t)PTI);
|
||||
|
||||
PTI->client = client;
|
||||
PTI->resourceCaller = resource;
|
||||
PTI->resourceImpl = wl_resource_create(client, &zwp_text_input_v1_interface, TEXT_INPUT_VERSION, id);
|
||||
|
||||
if (!PTI->resourceImpl) {
|
||||
Debug::log(ERR, "Could not alloc wl_resource for TIV1");
|
||||
removeTI(PTI);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(PTI->resourceImpl, &textInputImpl, PTI, &destroyTI);
|
||||
wl_resource_set_user_data(PTI->resourceImpl, PTI);
|
||||
|
||||
wl_signal_init(&PTI->sEnable);
|
||||
wl_signal_init(&PTI->sDisable);
|
||||
wl_signal_init(&PTI->sDestroy);
|
||||
wl_signal_init(&PTI->sCommit);
|
||||
|
||||
g_pInputManager->m_sIMERelay.onNewTextInput(PTI);
|
||||
void CTextInputV1::enter(SP<CWLSurfaceResource> surface) {
|
||||
resource->sendEnter(surface->getResource()->resource());
|
||||
active = true;
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) {
|
||||
const auto PTI = tiFromResource(resource);
|
||||
if (!surface) {
|
||||
Debug::log(WARN, "Text-input-v1 PTI{:x}: No surface to activate text input on!", (uintptr_t)PTI);
|
||||
return;
|
||||
}
|
||||
PTI->active = true;
|
||||
PTI->pTextInput->onEnabled(CWLSurfaceResource::fromResource(surface));
|
||||
void CTextInputV1::leave() {
|
||||
resource->sendLeave();
|
||||
active = false;
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) {
|
||||
const auto PTI = tiFromResource(resource);
|
||||
PTI->active = false;
|
||||
PTI->pTextInput->onDisabled();
|
||||
void CTextInputV1::preeditCursor(int32_t index) {
|
||||
resource->sendPreeditCursor(index);
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleShowInputPanel(wl_client* client, wl_resource* resource) {
|
||||
void CTextInputV1::preeditStyling(uint32_t index, uint32_t length, zwpTextInputV1PreeditStyle style) {
|
||||
resource->sendPreeditStyling(index, length, style);
|
||||
}
|
||||
|
||||
void CTextInputV1::preeditString(uint32_t serial, const char* text, const char* commit) {
|
||||
resource->sendPreeditString(serial, text, commit);
|
||||
}
|
||||
|
||||
void CTextInputV1::commitString(uint32_t serial, const char* text) {
|
||||
resource->sendCommitString(serial, text);
|
||||
}
|
||||
|
||||
void CTextInputV1::deleteSurroundingText(int32_t index, uint32_t length) {
|
||||
resource->sendDeleteSurroundingText(index, length);
|
||||
}
|
||||
|
||||
CTextInputV1Protocol::CTextInputV1Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleHideInputPanel(wl_client* client, wl_resource* resource) {
|
||||
;
|
||||
void CTextInputV1Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CZwpTextInputManagerV1>(client, ver, id));
|
||||
|
||||
RESOURCE->setOnDestroy([this](CZwpTextInputManagerV1* pMgr) { PROTO::textInputV1->destroyResource(pMgr); });
|
||||
RESOURCE->setCreateTextInput([this](CZwpTextInputManagerV1* pMgr, uint32_t id) {
|
||||
const auto PTI = m_vClients.emplace_back(makeShared<CTextInputV1>(makeShared<CZwpTextInputV1>(pMgr->client(), pMgr->version(), id)));
|
||||
LOGM(LOG, "New TI V1 at {:x}", (uintptr_t)PTI.get());
|
||||
|
||||
if (!PTI->good()) {
|
||||
LOGM(ERR, "Could not alloc wl_resource for TIV1");
|
||||
pMgr->noMemory();
|
||||
PROTO::textInputV1->destroyResource(PTI.get());
|
||||
return;
|
||||
}
|
||||
|
||||
events.newTextInput.emit(WP<CTextInputV1>(PTI));
|
||||
});
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleReset(wl_client* client, wl_resource* resource) {
|
||||
const auto PTI = tiFromResource(resource);
|
||||
PTI->pendingSurrounding.isPending = false;
|
||||
PTI->pendingContentType.isPending = false;
|
||||
void CTextInputV1Protocol::destroyResource(CTextInputV1* client) {
|
||||
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor) {
|
||||
const auto PTI = tiFromResource(resource);
|
||||
PTI->pendingSurrounding = {true, std::string(text), cursor, anchor};
|
||||
void CTextInputV1Protocol::destroyResource(CZwpTextInputManagerV1* client) {
|
||||
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == client; });
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose) {
|
||||
const auto PTI = tiFromResource(resource);
|
||||
PTI->pendingContentType = {true, hint == (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT ? (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE : hint,
|
||||
purpose > (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD ? hint + 1 : hint};
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||
const auto PTI = tiFromResource(resource);
|
||||
PTI->cursorRectangle = CBox{x, y, width, height};
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language) {
|
||||
;
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial) {
|
||||
const auto PTI = tiFromResource(resource);
|
||||
PTI->serial = serial;
|
||||
PTI->pTextInput->onCommit();
|
||||
}
|
||||
|
||||
void CTextInputV1ProtocolManager::handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index) {
|
||||
;
|
||||
}
|
|
@ -1,28 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "text-input-unstable-v1-protocol.h"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "text-input-unstable-v1.hpp"
|
||||
#include "WaylandProtocol.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class CTextInput;
|
||||
|
||||
struct STextInputV1 {
|
||||
wl_client* client = nullptr;
|
||||
wl_resource* resourceCaller = nullptr;
|
||||
class CTextInputV1 {
|
||||
public:
|
||||
CTextInputV1(SP<CZwpTextInputV1> resource);
|
||||
~CTextInputV1();
|
||||
|
||||
wl_resource* resourceImpl = nullptr;
|
||||
void enter(SP<CWLSurfaceResource> surface);
|
||||
void leave();
|
||||
|
||||
CTextInput* pTextInput = nullptr;
|
||||
void preeditCursor(int32_t index);
|
||||
void preeditStyling(uint32_t index, uint32_t length, zwpTextInputV1PreeditStyle style);
|
||||
void preeditString(uint32_t serial, const char* text, const char* commit);
|
||||
void commitString(uint32_t serial, const char* text);
|
||||
void deleteSurroundingText(int32_t index, uint32_t length);
|
||||
|
||||
wl_signal sEnable;
|
||||
wl_signal sDisable;
|
||||
wl_signal sCommit;
|
||||
wl_signal sDestroy;
|
||||
bool good();
|
||||
wl_client* client();
|
||||
|
||||
uint32_t serial = 0;
|
||||
private:
|
||||
SP<CZwpTextInputV1> resource;
|
||||
WP<CTextInputV1> self;
|
||||
|
||||
bool active = false;
|
||||
uint32_t serial = 0;
|
||||
bool active = false;
|
||||
|
||||
struct {
|
||||
CSignal onCommit;
|
||||
CSignal enable;
|
||||
CSignal disable;
|
||||
CSignal destroy;
|
||||
} events;
|
||||
|
||||
struct SPendingSurr {
|
||||
bool isPending = false;
|
||||
|
@ -39,37 +55,29 @@ struct STextInputV1 {
|
|||
|
||||
CBox cursorRectangle = {0, 0, 0, 0};
|
||||
|
||||
bool operator==(const STextInputV1& other) {
|
||||
return other.client == client && other.resourceCaller == resourceCaller && other.resourceImpl == resourceImpl;
|
||||
}
|
||||
friend class CTextInput;
|
||||
friend class CTextInputV1Protocol;
|
||||
};
|
||||
|
||||
class CTextInputV1ProtocolManager {
|
||||
class CTextInputV1Protocol : IWaylandProtocol {
|
||||
public:
|
||||
CTextInputV1ProtocolManager();
|
||||
CTextInputV1Protocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
|
||||
void createTI(wl_client* client, wl_resource* resource, uint32_t id);
|
||||
void removeTI(STextInputV1* pTI);
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
|
||||
void destroyResource(CTextInputV1* resource);
|
||||
void destroyResource(CZwpTextInputManagerV1* client);
|
||||
|
||||
void displayDestroy();
|
||||
|
||||
// handlers for tiv1
|
||||
void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface);
|
||||
void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat);
|
||||
void handleShowInputPanel(wl_client* client, wl_resource* resource);
|
||||
void handleHideInputPanel(wl_client* client, wl_resource* resource);
|
||||
void handleReset(wl_client* client, wl_resource* resource);
|
||||
void handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor);
|
||||
void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose);
|
||||
void handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height);
|
||||
void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language);
|
||||
void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial);
|
||||
void handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index);
|
||||
struct {
|
||||
CSignal newTextInput; // WP<CTextInputV3>
|
||||
} events;
|
||||
|
||||
private:
|
||||
wl_global* m_pGlobal = nullptr;
|
||||
wl_listener m_liDisplayDestroy;
|
||||
std::vector<SP<CZwpTextInputManagerV1>> m_vManagers;
|
||||
std::vector<SP<CTextInputV1>> m_vClients;
|
||||
|
||||
std::vector<std::unique_ptr<STextInputV1>> m_pClients;
|
||||
};
|
||||
friend class CTextInputV1;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CTextInputV1Protocol> textInputV1;
|
||||
};
|
||||
|
|
|
@ -8,356 +8,240 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#define TOPLEVEL_EXPORT_VERSION 2
|
||||
#define LOGM PROTO::toplevelExport->protoLog
|
||||
|
||||
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->bindManager(client, data, version, id);
|
||||
}
|
||||
|
||||
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->displayDestroy();
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::displayDestroy() {
|
||||
wl_global_destroy(m_pGlobal);
|
||||
}
|
||||
|
||||
CToplevelExportProtocolManager::CToplevelExportProtocolManager() {
|
||||
|
||||
#ifndef GLES32
|
||||
Debug::log(WARN, "Toplevel sharing is not supported on LEGACY_RENDERER!");
|
||||
return;
|
||||
#endif
|
||||
|
||||
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &hyprland_toplevel_export_manager_v1_interface, TOPLEVEL_EXPORT_VERSION, this, bindManagerInt);
|
||||
|
||||
if (!m_pGlobal) {
|
||||
Debug::log(ERR, "ToplevelExportManager could not start! Sharing windows will not work!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_liDisplayDestroy.notify = handleDisplayDestroy;
|
||||
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
|
||||
|
||||
Debug::log(LOG, "ToplevelExportManager started successfully!");
|
||||
}
|
||||
|
||||
static void handleCaptureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) {
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromHandle(handle));
|
||||
}
|
||||
|
||||
static void handleCaptureToplevelWithWlr(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* handle) {
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, PROTO::foreignToplevelWlr->windowFromHandleResource(handle));
|
||||
}
|
||||
|
||||
static void handleDestroy(wl_client* client, wl_resource* resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) {
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->copyFrame(client, resource, buffer, ignore_damage);
|
||||
}
|
||||
|
||||
static void handleDestroyFrame(wl_client* client, wl_resource* resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static const struct hyprland_toplevel_export_manager_v1_interface toplevelExportManagerImpl = {
|
||||
.capture_toplevel = handleCaptureToplevel,
|
||||
.destroy = handleDestroy,
|
||||
.capture_toplevel_with_wlr_toplevel_handle = handleCaptureToplevelWithWlr,
|
||||
};
|
||||
|
||||
static const struct hyprland_toplevel_export_frame_v1_interface toplevelFrameImpl = {.copy = handleCopyFrame, .destroy = handleDestroyFrame};
|
||||
|
||||
//
|
||||
static CScreencopyClient* clientFromResource(wl_resource* resource) {
|
||||
ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_manager_v1_interface, &toplevelExportManagerImpl));
|
||||
return (CScreencopyClient*)wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static SScreencopyFrame* frameFromResource(wl_resource* resource) {
|
||||
ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_frame_v1_interface, &toplevelFrameImpl));
|
||||
return (SScreencopyFrame*)wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::removeClient(CScreencopyClient* client, bool force) {
|
||||
if (!force) {
|
||||
if (!client || client->ref <= 0)
|
||||
return;
|
||||
|
||||
if (--client->ref != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
m_lClients.remove(*client); // TODO: this doesn't get cleaned up after sharing app exits???
|
||||
}
|
||||
|
||||
static void handleManagerResourceDestroy(wl_resource* resource) {
|
||||
const auto PCLIENT = clientFromResource(resource);
|
||||
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->removeClient(PCLIENT, true);
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
|
||||
const auto PCLIENT = &m_lClients.emplace_back();
|
||||
|
||||
PCLIENT->clientOwner = CLIENT_TOPLEVEL_EXPORT;
|
||||
PCLIENT->resource = wl_resource_create(client, &hyprland_toplevel_export_manager_v1_interface, version, id);
|
||||
|
||||
if (!PCLIENT->resource) {
|
||||
Debug::log(ERR, "ToplevelExportManager could not bind! (out of memory?)");
|
||||
m_lClients.remove(*PCLIENT);
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
PCLIENT->ref = 1;
|
||||
|
||||
wl_resource_set_implementation(PCLIENT->resource, &toplevelExportManagerImpl, PCLIENT, handleManagerResourceDestroy);
|
||||
|
||||
Debug::log(LOG, "ToplevelExportManager bound successfully!");
|
||||
}
|
||||
|
||||
static void handleFrameResourceDestroy(wl_resource* resource) {
|
||||
const auto PFRAME = frameFromResource(resource);
|
||||
|
||||
g_pProtocolManager->m_pToplevelExportProtocolManager->removeFrame(PFRAME);
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::removeFrame(SScreencopyFrame* frame, bool force) {
|
||||
if (!frame)
|
||||
CToplevelExportClient::CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; });
|
||||
resource->setOnDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); });
|
||||
resource->setDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); });
|
||||
resource->setCaptureToplevel([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, uint32_t handle) {
|
||||
this->captureToplevel(pMgr, frame, overlayCursor, g_pCompositor->getWindowFromHandle(handle));
|
||||
});
|
||||
resource->setCaptureToplevelWithWlrToplevelHandle([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* handle) {
|
||||
this->captureToplevel(pMgr, frame, overlayCursor, PROTO::foreignToplevelWlr->windowFromHandleResource(handle));
|
||||
});
|
||||
|
||||
wl_resource_set_user_data(frame->resource, nullptr);
|
||||
if (frame->buffer && frame->buffer->locked() > 0)
|
||||
frame->buffer->unlock();
|
||||
removeClient(frame->client, force);
|
||||
m_lFrames.remove(*frame);
|
||||
lastMeasure.reset();
|
||||
lastFrame.reset();
|
||||
tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); });
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, PHLWINDOW pWindow) {
|
||||
const auto PCLIENT = clientFromResource(resource);
|
||||
|
||||
void CToplevelExportClient::captureToplevel(CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor_, PHLWINDOW handle) {
|
||||
// create a frame
|
||||
const auto PFRAME = &m_lFrames.emplace_back();
|
||||
PFRAME->overlayCursor = !!overlay_cursor;
|
||||
PFRAME->resource = wl_resource_create(client, &hyprland_toplevel_export_frame_v1_interface, wl_resource_get_version(resource), frame);
|
||||
PFRAME->pWindow = pWindow;
|
||||
const auto FRAME = PROTO::toplevelExport->m_vFrames.emplace_back(
|
||||
makeShared<CToplevelExportFrame>(makeShared<CHyprlandToplevelExportFrameV1>(resource->client(), resource->version(), frame), overlayCursor_, handle));
|
||||
|
||||
if (!FRAME->good()) {
|
||||
LOGM(ERR, "Couldn't alloc frame for sharing! (no memory)");
|
||||
resource->noMemory();
|
||||
PROTO::toplevelExport->destroyResource(FRAME.get());
|
||||
return;
|
||||
}
|
||||
|
||||
FRAME->self = FRAME;
|
||||
FRAME->client = self;
|
||||
}
|
||||
|
||||
void CToplevelExportClient::onTick() {
|
||||
if (lastMeasure.getMillis() < 500)
|
||||
return;
|
||||
|
||||
framesInLastHalfSecond = frameCounter;
|
||||
frameCounter = 0;
|
||||
lastMeasure.reset();
|
||||
|
||||
const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0;
|
||||
const bool FRAMEAWAITING = std::ranges::any_of(PROTO::toplevelExport->m_vFrames, [&](const auto& frame) { return frame->client.get() == this; });
|
||||
|
||||
if (framesInLastHalfSecond > 3 && !sentScreencast) {
|
||||
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)});
|
||||
sentScreencast = true;
|
||||
} else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) {
|
||||
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)});
|
||||
sentScreencast = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CToplevelExportClient::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CToplevelExportFrame::~CToplevelExportFrame() {
|
||||
if (buffer && buffer->locked())
|
||||
buffer->unlock();
|
||||
}
|
||||
|
||||
CToplevelExportFrame::CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
overlayCursor = !!overlayCursor_;
|
||||
|
||||
if (!pWindow) {
|
||||
Debug::log(ERR, "Client requested sharing of window handle {:x} which does not exist!", pWindow);
|
||||
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
LOGM(ERR, "Client requested sharing of window handle {:x} which does not exist!", pWindow);
|
||||
resource->sendFailed();
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pWindow->m_bIsMapped || pWindow->isHidden()) {
|
||||
Debug::log(ERR, "Client requested sharing of window handle {:x} which is not shareable!", pWindow);
|
||||
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
LOGM(ERR, "Client requested sharing of window handle {:x} which is not shareable!", pWindow);
|
||||
resource->sendFailed();
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PFRAME->resource) {
|
||||
Debug::log(ERR, "Couldn't alloc frame for sharing! (no memory)");
|
||||
m_lFrames.remove(*PFRAME);
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(PFRAME->resource, &toplevelFrameImpl, PFRAME, handleFrameResourceDestroy);
|
||||
|
||||
PFRAME->client = PCLIENT;
|
||||
PCLIENT->ref++;
|
||||
resource->setOnDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); });
|
||||
resource->setDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); });
|
||||
resource->setCopy([this](CHyprlandToplevelExportFrameV1* pFrame, wl_resource* res, int32_t ignoreDamage) { this->copy(pFrame, res, ignoreDamage); });
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
PFRAME->shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PMONITOR);
|
||||
if (PFRAME->shmFormat == DRM_FORMAT_INVALID) {
|
||||
Debug::log(ERR, "No format supported by renderer in capture toplevel");
|
||||
hyprland_toplevel_export_frame_v1_send_failed(resource);
|
||||
removeFrame(PFRAME);
|
||||
shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PMONITOR);
|
||||
if (shmFormat == DRM_FORMAT_INVALID) {
|
||||
LOGM(ERR, "No format supported by renderer in capture toplevel");
|
||||
resource->sendFailed();
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(PFRAME->shmFormat);
|
||||
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(shmFormat);
|
||||
if (!PSHMINFO) {
|
||||
Debug::log(ERR, "No pixel format supported by renderer in capture toplevel");
|
||||
hyprland_toplevel_export_frame_v1_send_failed(resource);
|
||||
removeFrame(PFRAME);
|
||||
LOGM(ERR, "No pixel format supported by renderer in capture toplevel");
|
||||
resource->sendFailed();
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
PFRAME->dmabufFormat = PMONITOR->output->state->state().drmFormat;
|
||||
dmabufFormat = PMONITOR->output->state->state().drmFormat;
|
||||
|
||||
PFRAME->box = {0, 0, (int)(pWindow->m_vRealSize.value().x * PMONITOR->scale), (int)(pWindow->m_vRealSize.value().y * PMONITOR->scale)};
|
||||
box = {0, 0, (int)(pWindow->m_vRealSize.value().x * PMONITOR->scale), (int)(pWindow->m_vRealSize.value().y * PMONITOR->scale)};
|
||||
|
||||
PFRAME->box.transform(wlTransformToHyprutils(PMONITOR->transform), PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y).round();
|
||||
box.transform(wlTransformToHyprutils(PMONITOR->transform), PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y).round();
|
||||
|
||||
PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w);
|
||||
shmStride = FormatUtils::minStride(PSHMINFO, box.w);
|
||||
|
||||
hyprland_toplevel_export_frame_v1_send_buffer(PFRAME->resource, FormatUtils::drmToShm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride);
|
||||
resource->sendBuffer(FormatUtils::drmToShm(shmFormat), box.width, box.height, shmStride);
|
||||
|
||||
if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) {
|
||||
hyprland_toplevel_export_frame_v1_send_linux_dmabuf(PFRAME->resource, PFRAME->dmabufFormat, PFRAME->box.width, PFRAME->box.height);
|
||||
if (dmabufFormat != DRM_FORMAT_INVALID) {
|
||||
resource->sendLinuxDmabuf(dmabufFormat, box.width, box.height);
|
||||
}
|
||||
|
||||
hyprland_toplevel_export_frame_v1_send_buffer_done(PFRAME->resource);
|
||||
resource->sendBufferDone();
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) {
|
||||
const auto PFRAME = frameFromResource(resource);
|
||||
|
||||
if (!PFRAME) {
|
||||
Debug::log(ERR, "No frame in copyFrame??");
|
||||
void CToplevelExportFrame::copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resource* buffer_, int32_t ignoreDamage) {
|
||||
if (!good()) {
|
||||
LOGM(ERR, "No frame in copyFrame??");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PWINDOW = PFRAME->pWindow.lock();
|
||||
|
||||
if (!validMapped(PWINDOW)) {
|
||||
Debug::log(ERR, "Client requested sharing of window handle {:x} which is gone!", (uintptr_t)PWINDOW.get());
|
||||
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
if (!validMapped(pWindow)) {
|
||||
LOGM(ERR, "Client requested sharing of window handle {:x} which is gone!", pWindow);
|
||||
resource->sendFailed();
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden()) {
|
||||
Debug::log(ERR, "Client requested sharing of window handle {:x} which is not shareable (2)!", PWINDOW);
|
||||
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
|
||||
removeFrame(PFRAME);
|
||||
if (!pWindow->m_bIsMapped || pWindow->isHidden()) {
|
||||
LOGM(ERR, "Client requested sharing of window handle {:x} which is not shareable (2)!", pWindow);
|
||||
resource->sendFailed();
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PBUFFER = CWLBufferResource::fromResource(buffer);
|
||||
const auto PBUFFER = CWLBufferResource::fromResource(buffer_);
|
||||
if (!PBUFFER) {
|
||||
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
|
||||
removeFrame(PFRAME);
|
||||
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
PBUFFER->buffer->lock();
|
||||
|
||||
if (PBUFFER->buffer->size != PFRAME->box.size()) {
|
||||
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
|
||||
removeFrame(PFRAME);
|
||||
if (PBUFFER->buffer->size != box.size()) {
|
||||
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PFRAME->buffer) {
|
||||
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
||||
removeFrame(PFRAME);
|
||||
if (buffer) {
|
||||
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) {
|
||||
PFRAME->bufferDMA = true;
|
||||
bufferDMA = true;
|
||||
|
||||
if (attrs.format != PFRAME->dmabufFormat) {
|
||||
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
removeFrame(PFRAME);
|
||||
if (attrs.format != dmabufFormat) {
|
||||
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
} else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) {
|
||||
if (attrs.format != PFRAME->shmFormat) {
|
||||
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
removeFrame(PFRAME);
|
||||
if (attrs.format != shmFormat) {
|
||||
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
} else if ((int)attrs.stride != PFRAME->shmStride) {
|
||||
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
|
||||
removeFrame(PFRAME);
|
||||
} else if ((int)attrs.stride != shmStride) {
|
||||
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
|
||||
removeFrame(PFRAME);
|
||||
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
|
||||
PROTO::toplevelExport->destroyResource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
PFRAME->buffer = PBUFFER->buffer;
|
||||
buffer = PBUFFER->buffer;
|
||||
|
||||
m_vFramesAwaitingWrite.emplace_back(PFRAME);
|
||||
PROTO::toplevelExport->m_vFramesAwaitingWrite.emplace_back(self);
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::onOutputCommit(CMonitor* pMonitor) {
|
||||
if (m_vFramesAwaitingWrite.empty())
|
||||
return; // nothing to share
|
||||
|
||||
std::vector<SScreencopyFrame*> framesToRemove;
|
||||
|
||||
// share frame if correct output
|
||||
for (auto& f : m_vFramesAwaitingWrite) {
|
||||
const auto PWINDOW = f->pWindow.lock();
|
||||
|
||||
if (!validMapped(PWINDOW)) {
|
||||
framesToRemove.push_back(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pMonitor != g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID))
|
||||
continue;
|
||||
|
||||
CBox geometry = {PWINDOW->m_vRealPosition.value().x, PWINDOW->m_vRealPosition.value().y, PWINDOW->m_vRealSize.value().x, PWINDOW->m_vRealSize.value().y};
|
||||
|
||||
if (geometry.intersection({pMonitor->vecPosition, pMonitor->vecSize}).empty())
|
||||
continue;
|
||||
|
||||
shareFrame(f);
|
||||
|
||||
f->client->lastFrame.reset();
|
||||
++f->client->frameCounter;
|
||||
|
||||
framesToRemove.push_back(f);
|
||||
}
|
||||
|
||||
for (auto& f : framesToRemove) {
|
||||
removeFrame(f);
|
||||
}
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::shareFrame(SScreencopyFrame* frame) {
|
||||
if (!frame->buffer || !validMapped(frame->pWindow))
|
||||
void CToplevelExportFrame::share() {
|
||||
if (!buffer || !validMapped(pWindow))
|
||||
return;
|
||||
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (frame->bufferDMA) {
|
||||
if (!copyFrameDmabuf(frame, &now)) {
|
||||
hyprland_toplevel_export_frame_v1_send_failed(frame->resource);
|
||||
if (bufferDMA) {
|
||||
if (!copyDmabuf(&now)) {
|
||||
resource->sendFailed();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!copyFrameShm(frame, &now)) {
|
||||
hyprland_toplevel_export_frame_v1_send_failed(frame->resource);
|
||||
if (!copyShm(&now)) {
|
||||
resource->sendFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hyprland_toplevel_export_frame_v1_send_flags(frame->resource, flags);
|
||||
sendDamage(frame);
|
||||
resource->sendFlags((hyprlandToplevelExportFrameV1Flags)0);
|
||||
|
||||
if (!ignoreDamage) {
|
||||
resource->sendDamage(0, 0, box.width, box.height);
|
||||
}
|
||||
|
||||
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
|
||||
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
|
||||
hyprland_toplevel_export_frame_v1_send_ready(frame->resource, tvSecHi, tvSecLo, now.tv_nsec);
|
||||
resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec);
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::sendDamage(SScreencopyFrame* frame) {
|
||||
// TODO: send proper dmg
|
||||
hyprland_toplevel_export_frame_v1_send_damage(frame->resource, 0, 0, frame->box.width, frame->box.height);
|
||||
}
|
||||
|
||||
bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) {
|
||||
auto shm = frame->buffer->shm();
|
||||
auto [pixelData, fmt, bufLen] = frame->buffer->beginDataPtr(0); // no need for end, cuz it's shm
|
||||
bool CToplevelExportFrame::copyShm(timespec* now) {
|
||||
auto shm = buffer->shm();
|
||||
auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm
|
||||
|
||||
// render the client
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(frame->pWindow->m_iMonitorID);
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
||||
CRegion fakeDamage{0, 0, PMONITOR->vecPixelSize.x * 10, PMONITOR->vecPixelSize.y * 10};
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
@ -365,7 +249,7 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
|
|||
CFramebuffer outFB;
|
||||
outFB.alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : PMONITOR->output->state->state().drmFormat);
|
||||
|
||||
if (frame->overlayCursor) {
|
||||
if (overlayCursor) {
|
||||
g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock());
|
||||
g_pPointerManager->damageCursor(PMONITOR->self.lock());
|
||||
}
|
||||
|
@ -376,12 +260,12 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
|
|||
g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0));
|
||||
|
||||
// render client at 0,0
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(frame->pWindow.lock()); // block the feedback to avoid spamming the surface if it's visible
|
||||
g_pHyprRenderer->renderWindow(frame->pWindow.lock(), PMONITOR, now, false, RENDER_PASS_ALL, true, true);
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
|
||||
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
|
||||
|
||||
if (frame->overlayCursor)
|
||||
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - frame->pWindow->m_vRealPosition.value());
|
||||
if (overlayCursor)
|
||||
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition.value());
|
||||
|
||||
const auto PFORMAT = FormatUtils::getPixelFormatFromDRM(shm.format);
|
||||
if (!PFORMAT) {
|
||||
|
@ -403,9 +287,9 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
|
|||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
auto glFormat = PFORMAT->flipRB ? GL_BGRA_EXT : GL_RGBA;
|
||||
glReadPixels(0, 0, frame->box.width, frame->box.height, glFormat, PFORMAT->glType, pixelData);
|
||||
glReadPixels(0, 0, box.width, box.height, glFormat, PFORMAT->glType, pixelData);
|
||||
|
||||
if (frame->overlayCursor) {
|
||||
if (overlayCursor) {
|
||||
g_pPointerManager->unlockSoftwareForMonitor(PMONITOR->self.lock());
|
||||
g_pPointerManager->damageCursor(PMONITOR->self.lock());
|
||||
}
|
||||
|
@ -413,31 +297,112 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CToplevelExportProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame, timespec* now) {
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(frame->pWindow->m_iMonitorID);
|
||||
bool CToplevelExportFrame::copyDmabuf(timespec* now) {
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
||||
|
||||
CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX};
|
||||
|
||||
if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer.lock()))
|
||||
if (overlayCursor) {
|
||||
g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock());
|
||||
g_pPointerManager->damageCursor(PMONITOR->self.lock());
|
||||
}
|
||||
|
||||
if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock()))
|
||||
return false;
|
||||
|
||||
g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0));
|
||||
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(frame->pWindow.lock()); // block the feedback to avoid spamming the surface if it's visible
|
||||
g_pHyprRenderer->renderWindow(frame->pWindow.lock(), PMONITOR, now, false, RENDER_PASS_ALL, true, true);
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
|
||||
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
|
||||
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
|
||||
|
||||
if (frame->overlayCursor)
|
||||
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - frame->pWindow->m_vRealPosition.value());
|
||||
if (overlayCursor)
|
||||
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition.value());
|
||||
|
||||
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
|
||||
g_pHyprRenderer->endRender();
|
||||
|
||||
if (overlayCursor) {
|
||||
g_pPointerManager->unlockSoftwareForMonitor(PMONITOR->self.lock());
|
||||
g_pPointerManager->damageCursor(PMONITOR->self.lock());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::onWindowUnmap(PHLWINDOW pWindow) {
|
||||
for (auto& f : m_lFrames) {
|
||||
if (f.pWindow.lock() == pWindow)
|
||||
f.pWindow.reset();
|
||||
bool CToplevelExportFrame::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CToplevelExportProtocol::CToplevelExportProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CToplevelExportProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto CLIENT = m_vClients.emplace_back(makeShared<CToplevelExportClient>(makeShared<CHyprlandToplevelExportManagerV1>(client, ver, id)));
|
||||
|
||||
if (!CLIENT->good()) {
|
||||
LOGM(LOG, "Failed to bind client! (out of memory)");
|
||||
wl_client_post_no_memory(client);
|
||||
m_vClients.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
CLIENT->self = CLIENT;
|
||||
|
||||
LOGM(LOG, "Bound client successfully!");
|
||||
}
|
||||
|
||||
void CToplevelExportProtocol::destroyResource(CToplevelExportClient* client) {
|
||||
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
|
||||
std::erase_if(m_vFrames, [&](const auto& other) { return other->client.get() == client; });
|
||||
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other->client.get() == client; });
|
||||
}
|
||||
|
||||
void CToplevelExportProtocol::destroyResource(CToplevelExportFrame* frame) {
|
||||
std::erase_if(m_vFrames, [&](const auto& other) { return other.get() == frame; });
|
||||
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other.get() == frame; });
|
||||
}
|
||||
|
||||
void CToplevelExportProtocol::onOutputCommit(CMonitor* pMonitor) {
|
||||
if (m_vFramesAwaitingWrite.empty())
|
||||
return; // nothing to share
|
||||
|
||||
std::vector<WP<CToplevelExportFrame>> framesToRemove;
|
||||
|
||||
// share frame if correct output
|
||||
for (auto& f : m_vFramesAwaitingWrite) {
|
||||
if (!f->pWindow || !validMapped(f->pWindow)) {
|
||||
framesToRemove.push_back(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto PWINDOW = f->pWindow;
|
||||
|
||||
if (pMonitor != g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID))
|
||||
continue;
|
||||
|
||||
CBox geometry = {PWINDOW->m_vRealPosition.value().x, PWINDOW->m_vRealPosition.value().y, PWINDOW->m_vRealSize.value().x, PWINDOW->m_vRealSize.value().y};
|
||||
|
||||
if (geometry.intersection({pMonitor->vecPosition, pMonitor->vecSize}).empty())
|
||||
continue;
|
||||
|
||||
f->share();
|
||||
|
||||
f->client->lastFrame.reset();
|
||||
++f->client->frameCounter;
|
||||
|
||||
framesToRemove.push_back(f);
|
||||
}
|
||||
|
||||
for (auto& f : framesToRemove) {
|
||||
destroyResource(f.get());
|
||||
}
|
||||
}
|
||||
|
||||
void CToplevelExportProtocol::onWindowUnmap(PHLWINDOW pWindow) {
|
||||
for (auto& f : m_vFrames) {
|
||||
if (f->pWindow == pWindow)
|
||||
f->pWindow.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "hyprland-toplevel-export-v1-protocol.h"
|
||||
#include "hyprland-toplevel-export-v1.hpp"
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "Screencopy.hpp"
|
||||
|
||||
#include <list>
|
||||
|
@ -10,32 +11,91 @@
|
|||
class CMonitor;
|
||||
class CWindow;
|
||||
|
||||
class CToplevelExportProtocolManager {
|
||||
class CToplevelExportClient {
|
||||
public:
|
||||
CToplevelExportProtocolManager();
|
||||
CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1> resource_);
|
||||
|
||||
bool good();
|
||||
|
||||
WP<CToplevelExportClient> self;
|
||||
eClientOwners clientOwner = CLIENT_TOPLEVEL_EXPORT;
|
||||
|
||||
CTimer lastFrame;
|
||||
int frameCounter = 0;
|
||||
|
||||
private:
|
||||
SP<CHyprlandToplevelExportManagerV1> resource;
|
||||
|
||||
int framesInLastHalfSecond = 0;
|
||||
CTimer lastMeasure;
|
||||
bool sentScreencast = false;
|
||||
|
||||
SP<HOOK_CALLBACK_FN> tickCallback;
|
||||
void onTick();
|
||||
|
||||
void captureToplevel(CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, PHLWINDOW handle);
|
||||
|
||||
friend class CToplevelExportProtocol;
|
||||
};
|
||||
|
||||
class CToplevelExportFrame {
|
||||
public:
|
||||
CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor, PHLWINDOW pWindow);
|
||||
~CToplevelExportFrame();
|
||||
|
||||
bool good();
|
||||
|
||||
SP<CToplevelExportFrame> self;
|
||||
WP<CToplevelExportClient> client;
|
||||
|
||||
private:
|
||||
SP<CHyprlandToplevelExportFrameV1> resource;
|
||||
|
||||
PHLWINDOW pWindow;
|
||||
bool overlayCursor = false;
|
||||
bool ignoreDamage = false;
|
||||
bool lockedSWCursors = false;
|
||||
|
||||
WP<IHLBuffer> buffer;
|
||||
bool bufferDMA = false;
|
||||
uint32_t shmFormat = 0;
|
||||
uint32_t dmabufFormat = 0;
|
||||
int shmStride = 0;
|
||||
CBox box = {};
|
||||
|
||||
void copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resource* buffer, int32_t ignoreDamage);
|
||||
bool copyDmabuf(timespec* now);
|
||||
bool copyShm(timespec* now);
|
||||
void share();
|
||||
|
||||
friend class CToplevelExportProtocol;
|
||||
};
|
||||
|
||||
class CToplevelExportProtocol : IWaylandProtocol {
|
||||
public:
|
||||
CToplevelExportProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
void destroyResource(CToplevelExportClient* client);
|
||||
void destroyResource(CToplevelExportFrame* frame);
|
||||
|
||||
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
|
||||
void captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, PHLWINDOW handle);
|
||||
void removeClient(CScreencopyClient* client, bool force = false);
|
||||
void removeFrame(SScreencopyFrame* frame, bool force = false);
|
||||
void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage);
|
||||
void displayDestroy();
|
||||
void onWindowUnmap(PHLWINDOW pWindow);
|
||||
void onOutputCommit(CMonitor* pMonitor);
|
||||
|
||||
private:
|
||||
wl_global* m_pGlobal = nullptr;
|
||||
std::list<SScreencopyFrame> m_lFrames;
|
||||
std::list<CScreencopyClient> m_lClients;
|
||||
std::vector<SP<CToplevelExportClient>> m_vClients;
|
||||
std::vector<SP<CToplevelExportFrame>> m_vFrames;
|
||||
std::vector<SP<CToplevelExportFrame>> m_vFramesAwaitingWrite;
|
||||
|
||||
wl_listener m_liDisplayDestroy;
|
||||
void shareFrame(CToplevelExportFrame* frame);
|
||||
bool copyFrameDmabuf(CToplevelExportFrame* frame, timespec* now);
|
||||
bool copyFrameShm(CToplevelExportFrame* frame, timespec* now);
|
||||
void sendDamage(CToplevelExportFrame* frame);
|
||||
|
||||
std::vector<SScreencopyFrame*> m_vFramesAwaitingWrite;
|
||||
friend class CToplevelExportClient;
|
||||
friend class CToplevelExportFrame;
|
||||
};
|
||||
|
||||
void shareFrame(SScreencopyFrame* frame);
|
||||
bool copyFrameDmabuf(SScreencopyFrame* frame, timespec* now);
|
||||
bool copyFrameShm(SScreencopyFrame* frame, timespec* now);
|
||||
void sendDamage(SScreencopyFrame* frame);
|
||||
|
||||
friend class CScreencopyClient;
|
||||
};
|
||||
namespace PROTO {
|
||||
inline UP<CToplevelExportProtocol> toplevelExport;
|
||||
};
|
||||
|
|
|
@ -54,13 +54,13 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
|
|||
});
|
||||
|
||||
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
|
||||
if (!surface || !surface->pending.buffer)
|
||||
if (!surface || !surface->pending.texture)
|
||||
return;
|
||||
|
||||
if (surface->pending.viewport.hasSource) {
|
||||
auto& src = surface->pending.viewport.source;
|
||||
|
||||
if (src.w + src.x > surface->pending.buffer->size.x || src.h + src.y > surface->pending.buffer->size.y) {
|
||||
if (src.w + src.x > surface->pending.bufferSize.x || src.h + src.y > surface->pending.bufferSize.y) {
|
||||
resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit");
|
||||
surface->pending.rejected = true;
|
||||
return;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "VirtualPointer.hpp"
|
||||
#include "core/Output.hpp"
|
||||
|
||||
#define LOGM PROTO::virtualPointer->protoLog
|
||||
|
||||
CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_) : resource(resource_) {
|
||||
CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_, WP<CMonitor> boundOutput_) : boundOutput(boundOutput_), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
|
@ -112,10 +113,18 @@ void CVirtualPointerProtocol::bindManager(wl_client* client, void* data, uint32_
|
|||
RESOURCE->setOnDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); });
|
||||
RESOURCE->setDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); });
|
||||
|
||||
RESOURCE->setCreateVirtualPointer([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) { this->onCreatePointer(pMgr, seat, id); });
|
||||
RESOURCE->setCreateVirtualPointer([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) { this->onCreatePointer(pMgr, seat, id, {}); });
|
||||
RESOURCE->setCreateVirtualPointerWithOutput([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, wl_resource* output, uint32_t id) {
|
||||
LOGM(WARN, "TODO: CreateWithOutput is not supported yet. Ignoring for now.");
|
||||
this->onCreatePointer(pMgr, seat, id);
|
||||
if (output) {
|
||||
auto RES = CWLOutputResource::fromResource(output);
|
||||
if (!RES) {
|
||||
this->onCreatePointer(pMgr, seat, id, {});
|
||||
return;
|
||||
}
|
||||
|
||||
this->onCreatePointer(pMgr, seat, id, RES->monitor);
|
||||
} else
|
||||
this->onCreatePointer(pMgr, seat, id, {});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -127,9 +136,9 @@ void CVirtualPointerProtocol::destroyResource(CVirtualPointerV1Resource* pointer
|
|||
std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == pointer; });
|
||||
}
|
||||
|
||||
void CVirtualPointerProtocol::onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) {
|
||||
void CVirtualPointerProtocol::onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id, WP<CMonitor> output) {
|
||||
|
||||
const auto RESOURCE = m_vPointers.emplace_back(makeShared<CVirtualPointerV1Resource>(makeShared<CZwlrVirtualPointerV1>(pMgr->client(), pMgr->version(), id)));
|
||||
const auto RESOURCE = m_vPointers.emplace_back(makeShared<CVirtualPointerV1Resource>(makeShared<CZwlrVirtualPointerV1>(pMgr->client(), pMgr->version(), id), output));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
pMgr->noMemory();
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
#include "wlr-virtual-pointer-unstable-v1.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "../devices/IPointer.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
|
||||
class CVirtualPointerV1Resource {
|
||||
public:
|
||||
CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_);
|
||||
CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_, WP<CMonitor> boundOutput_);
|
||||
~CVirtualPointerV1Resource();
|
||||
|
||||
struct {
|
||||
|
@ -34,10 +35,12 @@ class CVirtualPointerV1Resource {
|
|||
CSignal holdEnd;
|
||||
} events;
|
||||
|
||||
bool good();
|
||||
wl_client* client();
|
||||
bool good();
|
||||
wl_client* client();
|
||||
|
||||
std::string name;
|
||||
std::string name;
|
||||
|
||||
WP<CMonitor> boundOutput;
|
||||
|
||||
private:
|
||||
SP<CZwlrVirtualPointerV1> resource;
|
||||
|
@ -60,7 +63,7 @@ class CVirtualPointerProtocol : public IWaylandProtocol {
|
|||
private:
|
||||
void onManagerResourceDestroy(wl_resource* res);
|
||||
void destroyResource(CVirtualPointerV1Resource* pointer);
|
||||
void onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id);
|
||||
void onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id, WP<CMonitor> output);
|
||||
|
||||
//
|
||||
std::vector<UP<CZwlrVirtualPointerManagerV1>> m_vManagers;
|
||||
|
|
|
@ -6,12 +6,14 @@ static void bindManagerInternal(wl_client* client, void* data, uint32_t ver, uin
|
|||
}
|
||||
|
||||
static void displayDestroyInternal(struct wl_listener* listener, void* data) {
|
||||
((IWaylandProtocol*)data)->onDisplayDestroy();
|
||||
IWaylandProtocolDestroyWrapper* wrap = wl_container_of(listener, wrap, listener);
|
||||
IWaylandProtocol* proto = wrap->parent;
|
||||
proto->onDisplayDestroy();
|
||||
}
|
||||
|
||||
void IWaylandProtocol::onDisplayDestroy() {
|
||||
wl_list_remove(&m_liDisplayDestroy.link);
|
||||
wl_list_init(&m_liDisplayDestroy.link);
|
||||
wl_list_remove(&m_liDisplayDestroy.listener.link);
|
||||
wl_list_init(&m_liDisplayDestroy.listener.link);
|
||||
wl_global_destroy(m_pGlobal);
|
||||
}
|
||||
|
||||
|
@ -23,8 +25,10 @@ IWaylandProtocol::IWaylandProtocol(const wl_interface* iface, const int& ver, co
|
|||
return;
|
||||
}
|
||||
|
||||
m_liDisplayDestroy.notify = displayDestroyInternal;
|
||||
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
|
||||
wl_list_init(&m_liDisplayDestroy.listener.link);
|
||||
m_liDisplayDestroy.listener.notify = displayDestroyInternal;
|
||||
m_liDisplayDestroy.parent = this;
|
||||
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy.listener);
|
||||
|
||||
protoLog(LOG, "Registered global");
|
||||
}
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
|
||||
#define PROTO NProtocols
|
||||
|
||||
class IWaylandProtocol;
|
||||
struct IWaylandProtocolDestroyWrapper {
|
||||
wl_listener listener;
|
||||
IWaylandProtocol* parent = nullptr;
|
||||
};
|
||||
|
||||
class IWaylandProtocol {
|
||||
public:
|
||||
IWaylandProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
@ -26,8 +32,9 @@ class IWaylandProtocol {
|
|||
Debug::log(level, std::format("[{}] ", m_szName) + std::vformat(fmt.get(), std::make_format_args(args...)));
|
||||
};
|
||||
|
||||
IWaylandProtocolDestroyWrapper m_liDisplayDestroy;
|
||||
|
||||
private:
|
||||
std::string m_szName;
|
||||
wl_global* m_pGlobal = nullptr;
|
||||
wl_listener m_liDisplayDestroy;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,6 +8,20 @@
|
|||
|
||||
#define LOGM PROTO::xdgShell->protoLog
|
||||
|
||||
void SXDGPositionerState::setAnchor(xdgPositionerAnchor edges) {
|
||||
anchor.setTop(edges == XDG_POSITIONER_ANCHOR_TOP || edges == XDG_POSITIONER_ANCHOR_TOP_LEFT || edges == XDG_POSITIONER_ANCHOR_TOP_RIGHT);
|
||||
anchor.setLeft(edges == XDG_POSITIONER_ANCHOR_LEFT || edges == XDG_POSITIONER_ANCHOR_TOP_LEFT || edges == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT);
|
||||
anchor.setBottom(edges == XDG_POSITIONER_ANCHOR_BOTTOM || edges == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT || edges == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT);
|
||||
anchor.setRight(edges == XDG_POSITIONER_ANCHOR_RIGHT || edges == XDG_POSITIONER_ANCHOR_TOP_RIGHT || edges == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT);
|
||||
}
|
||||
|
||||
void SXDGPositionerState::setGravity(xdgPositionerGravity edges) {
|
||||
gravity.setTop(edges == XDG_POSITIONER_GRAVITY_TOP || edges == XDG_POSITIONER_GRAVITY_TOP_LEFT || edges == XDG_POSITIONER_GRAVITY_TOP_RIGHT);
|
||||
gravity.setLeft(edges == XDG_POSITIONER_GRAVITY_LEFT || edges == XDG_POSITIONER_GRAVITY_TOP_LEFT || edges == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT);
|
||||
gravity.setBottom(edges == XDG_POSITIONER_GRAVITY_BOTTOM || edges == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT || edges == XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
|
||||
gravity.setRight(edges == XDG_POSITIONER_GRAVITY_RIGHT || edges == XDG_POSITIONER_GRAVITY_TOP_RIGHT || edges == XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
|
||||
}
|
||||
|
||||
CXDGPopupResource::CXDGPopupResource(SP<CXdgPopup> resource_, SP<CXDGSurfaceResource> owner_, SP<CXDGSurfaceResource> surface_, SP<CXDGPositionerResource> positioner) :
|
||||
surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) {
|
||||
if (!good())
|
||||
|
@ -333,12 +347,12 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
|
|||
if (toplevel)
|
||||
toplevel->current = toplevel->pending;
|
||||
|
||||
if (initialCommit && surface->pending.buffer) {
|
||||
if (initialCommit && surface->pending.texture) {
|
||||
resource->error(-1, "Buffer attached before initial commit");
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->current.buffer && !mapped) {
|
||||
if (surface->current.texture && !mapped) {
|
||||
// this forces apps to not draw CSD.
|
||||
if (toplevel)
|
||||
toplevel->setMaximized(true);
|
||||
|
@ -349,10 +363,10 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
|
|||
return;
|
||||
}
|
||||
|
||||
if (!surface->current.buffer && mapped) {
|
||||
if (!surface->current.texture && mapped) {
|
||||
mapped = false;
|
||||
surface->unmap();
|
||||
events.unmap.emit();
|
||||
surface->unmap();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -429,10 +443,6 @@ CXDGSurfaceResource::~CXDGSurfaceResource() {
|
|||
surface->resetRole();
|
||||
}
|
||||
|
||||
eSurfaceRole CXDGSurfaceResource::role() {
|
||||
return SURFACE_ROLE_XDG_SHELL;
|
||||
}
|
||||
|
||||
bool CXDGSurfaceResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
@ -490,9 +500,9 @@ CXDGPositionerResource::CXDGPositionerResource(SP<CXdgPositioner> resource_, SP<
|
|||
|
||||
resource->setSetOffset([this](CXdgPositioner* r, int32_t x, int32_t y) { state.offset = {x, y}; });
|
||||
|
||||
resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.anchor = a; });
|
||||
resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.setAnchor(a); });
|
||||
|
||||
resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.gravity = g; });
|
||||
resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.setGravity(g); });
|
||||
|
||||
resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { state.constraintAdjustment = (uint32_t)a; });
|
||||
|
||||
|
@ -513,125 +523,96 @@ CXDGPositionerRules::CXDGPositionerRules(SP<CXDGPositionerResource> positioner)
|
|||
state = positioner->state;
|
||||
}
|
||||
|
||||
static Vector2D pointForAnchor(const CBox& box, const Vector2D& predictionSize, xdgPositionerAnchor anchor) {
|
||||
switch (anchor) {
|
||||
case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.0 - predictionSize.x / 2.0, 0.0};
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.0 - predictionSize.x / 2.0, box.size().y};
|
||||
case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0.0, box.size().y / 2.0 - predictionSize.y / 2.0};
|
||||
case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F - predictionSize.y / 2.0};
|
||||
case XDG_POSITIONER_ANCHOR_TOP_LEFT: return box.pos();
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0.0, box.size().y};
|
||||
case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return box.pos() + Vector2D{box.size().x, 0.0};
|
||||
case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y};
|
||||
default: return box.pos();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& parentCoord) {
|
||||
|
||||
CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoord) {
|
||||
Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord);
|
||||
|
||||
CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.requestedSize, state.anchor) + state.offset, state.requestedSize};
|
||||
// padding
|
||||
constraint.expand(-4);
|
||||
|
||||
bool success = predictedBox.inside(constraint);
|
||||
auto anchorRect = state.anchorRect.copy().translate(parentCoord);
|
||||
|
||||
if (success)
|
||||
return predictedBox.translate(-parentCoord - constraint.pos());
|
||||
auto width = state.requestedSize.x;
|
||||
auto height = state.requestedSize.y;
|
||||
|
||||
CBox test = predictedBox;
|
||||
auto anchorX = state.anchor.left() ? anchorRect.x : state.anchor.right() ? anchorRect.extent().x : anchorRect.middle().x;
|
||||
auto anchorY = state.anchor.top() ? anchorRect.y : state.anchor.bottom() ? anchorRect.extent().y : anchorRect.middle().y;
|
||||
|
||||
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)) {
|
||||
// attempt to flip
|
||||
const bool flipX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X;
|
||||
const bool flipY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
|
||||
auto countEdges = [constraint](const CBox& test) -> int {
|
||||
int edgeCount = 0;
|
||||
edgeCount += test.x < constraint.x ? 1 : 0;
|
||||
edgeCount += test.x + test.w > constraint.x + constraint.w ? 1 : 0;
|
||||
edgeCount += test.y < constraint.y ? 1 : 0;
|
||||
edgeCount += test.y + test.h > constraint.y + constraint.h ? 1 : 0;
|
||||
return edgeCount;
|
||||
};
|
||||
int edgeCount = countEdges(test);
|
||||
auto calcEffectiveX = [&]() { return state.gravity.left() ? anchorX - width : state.gravity.right() ? anchorX : anchorX - width / 2; };
|
||||
auto calcEffectiveY = [&]() { return state.gravity.top() ? anchorY - height : state.gravity.bottom() ? anchorY : anchorY - height / 2; };
|
||||
|
||||
if (flipX && edgeCount > countEdges(test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0.0})))
|
||||
test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0.0});
|
||||
if (flipY && edgeCount > countEdges(test.copy().translate(Vector2D{0.0, -predictedBox.h - state.anchorRect.h})))
|
||||
test.translate(Vector2D{0.0, -predictedBox.h - state.anchorRect.h});
|
||||
auto effectiveX = calcEffectiveX();
|
||||
auto effectiveY = calcEffectiveY();
|
||||
|
||||
success = test.copy().expand(-1).inside(constraint);
|
||||
// Note: the usage of offset is a guess which maintains compatibility with other compositors that were tested.
|
||||
// It considers the offset when deciding whether or not to flip but does not actually flip the offset, instead
|
||||
// applying it after the flip step.
|
||||
|
||||
if (success)
|
||||
return test.translate(-parentCoord - constraint.pos());
|
||||
}
|
||||
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) {
|
||||
auto flip = (state.gravity.left() && effectiveX + state.offset.x < constraint.x) || (state.gravity.right() && effectiveX + state.offset.x + width > constraint.extent().x);
|
||||
|
||||
// for slide and resize, defines the padding around the edge for the positioned
|
||||
// surface.
|
||||
constexpr int EDGE_PADDING = 4;
|
||||
|
||||
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)) {
|
||||
// attempt to slide
|
||||
const bool slideX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X;
|
||||
const bool slideY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
|
||||
|
||||
//const bool gravityLeft = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT;
|
||||
//const bool gravityTop = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_TOP || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_RIGHT;
|
||||
|
||||
const bool leftEdgeOut = test.x < constraint.x;
|
||||
const bool topEdgeOut = test.y < constraint.y;
|
||||
const bool rightEdgeOut = test.x + test.w > constraint.x + constraint.w;
|
||||
const bool bottomEdgeOut = test.y + test.h > constraint.y + constraint.h;
|
||||
|
||||
// TODO: this isn't truly conformant.
|
||||
if (leftEdgeOut && slideX)
|
||||
test.x = constraint.x + EDGE_PADDING;
|
||||
if (rightEdgeOut && slideX)
|
||||
test.x = std::clamp((double)(constraint.x + constraint.w - test.w), (double)(constraint.x + EDGE_PADDING), (double)INFINITY);
|
||||
if (topEdgeOut && slideY)
|
||||
test.y = constraint.y + EDGE_PADDING;
|
||||
if (bottomEdgeOut && slideY)
|
||||
test.y = std::clamp((double)(constraint.y + constraint.h - test.h), (double)(constraint.y + EDGE_PADDING), (double)INFINITY);
|
||||
|
||||
success = test.copy().expand(-1).inside(constraint);
|
||||
|
||||
if (success)
|
||||
return test.translate(-parentCoord - constraint.pos());
|
||||
}
|
||||
|
||||
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)) {
|
||||
const bool resizeX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X;
|
||||
const bool resizeY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y;
|
||||
|
||||
const bool leftEdgeOut = test.x < constraint.x;
|
||||
const bool topEdgeOut = test.y < constraint.y;
|
||||
const bool rightEdgeOut = test.x + test.w > constraint.x + constraint.w;
|
||||
const bool bottomEdgeOut = test.y + test.h > constraint.y + constraint.h;
|
||||
|
||||
// TODO: this isn't truly conformant.
|
||||
if (leftEdgeOut && resizeX) {
|
||||
test.w = test.x + test.w - constraint.x - EDGE_PADDING;
|
||||
test.x = constraint.x + EDGE_PADDING;
|
||||
if (flip) {
|
||||
state.gravity ^= CEdges::LEFT | CEdges::RIGHT;
|
||||
anchorX = state.anchor.left() ? anchorRect.extent().x : state.anchor.right() ? anchorRect.x : anchorX;
|
||||
effectiveX = calcEffectiveX();
|
||||
}
|
||||
if (rightEdgeOut && resizeX)
|
||||
test.w = constraint.w - (test.x - constraint.w) - EDGE_PADDING;
|
||||
if (topEdgeOut && resizeY) {
|
||||
test.h = test.y + test.h - constraint.y - EDGE_PADDING;
|
||||
test.y = constraint.y + EDGE_PADDING;
|
||||
}
|
||||
if (bottomEdgeOut && resizeY)
|
||||
test.h = constraint.h - (test.y - constraint.y) - EDGE_PADDING;
|
||||
|
||||
success = test.copy().expand(-1).inside(constraint);
|
||||
|
||||
if (success)
|
||||
return test.translate(-parentCoord - constraint.pos());
|
||||
}
|
||||
|
||||
LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place");
|
||||
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) {
|
||||
auto flip = (state.gravity.top() && effectiveY + state.offset.y < constraint.y) || (state.gravity.bottom() && effectiveY + state.offset.y + height > constraint.extent().y);
|
||||
|
||||
return test.translate(-parentCoord - constraint.pos());
|
||||
if (flip) {
|
||||
state.gravity ^= CEdges::TOP | CEdges::BOTTOM;
|
||||
anchorY = state.anchor.top() ? anchorRect.extent().y : state.anchor.bottom() ? anchorRect.y : anchorY;
|
||||
effectiveY = calcEffectiveY();
|
||||
}
|
||||
}
|
||||
|
||||
effectiveX += state.offset.x;
|
||||
effectiveY += state.offset.y;
|
||||
|
||||
// Slide order is important for the case where the window is too large to fit on screen.
|
||||
|
||||
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) {
|
||||
if (effectiveX + width > constraint.extent().x)
|
||||
effectiveX = constraint.extent().x - width;
|
||||
|
||||
if (effectiveX < constraint.x)
|
||||
effectiveX = constraint.x;
|
||||
}
|
||||
|
||||
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) {
|
||||
if (effectiveY + height > constraint.extent().y)
|
||||
effectiveY = constraint.extent().y - height;
|
||||
|
||||
if (effectiveY < constraint.y)
|
||||
effectiveY = constraint.y;
|
||||
}
|
||||
|
||||
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) {
|
||||
if (effectiveX < constraint.x) {
|
||||
auto diff = constraint.x - effectiveX;
|
||||
effectiveX = constraint.x;
|
||||
width -= diff;
|
||||
}
|
||||
|
||||
auto effectiveX2 = effectiveX + width;
|
||||
if (effectiveX2 > constraint.extent().x)
|
||||
width -= effectiveX2 - constraint.extent().x;
|
||||
}
|
||||
|
||||
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) {
|
||||
if (effectiveY < constraint.y) {
|
||||
auto diff = constraint.y - effectiveY;
|
||||
effectiveY = constraint.y;
|
||||
height -= diff;
|
||||
}
|
||||
|
||||
auto effectiveY2 = effectiveY + height;
|
||||
if (effectiveY2 > constraint.extent().y)
|
||||
height -= effectiveY2 - constraint.extent().y;
|
||||
}
|
||||
|
||||
return {effectiveX - parentCoord.x, effectiveY - parentCoord.y, width, height};
|
||||
}
|
||||
|
||||
CXDGWMBase::CXDGWMBase(SP<CXdgWmBase> resource_) : resource(resource_) {
|
||||
|
@ -681,8 +662,9 @@ CXDGWMBase::CXDGWMBase(SP<CXdgWmBase> resource_) : resource(resource_) {
|
|||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
SURF->role = RESOURCE;
|
||||
RESOURCE->self = RESOURCE;
|
||||
RESOURCE->surface = SURF;
|
||||
SURF->role = makeShared<CXDGSurfaceRole>(RESOURCE);
|
||||
|
||||
surfaces.emplace_back(RESOURCE);
|
||||
|
||||
|
@ -779,3 +761,7 @@ void CXDGShellProtocol::onPopupDestroy(WP<CXDGPopupResource> popup) {
|
|||
if (popup->surface)
|
||||
grab->remove(popup->surface->surface.lock());
|
||||
}
|
||||
|
||||
CXDGSurfaceRole::CXDGSurfaceRole(SP<CXDGSurfaceResource> xdg) : xdgSurface(xdg) {
|
||||
;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <hyprutils/math/Edges.hpp>
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "xdg-shell.hpp"
|
||||
#include "../helpers/math/Math.hpp"
|
||||
#include "../helpers/math/Math.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "types/SurfaceRole.hpp"
|
||||
|
||||
|
@ -20,21 +20,24 @@ class CSeatGrab;
|
|||
class CWLSurfaceResource;
|
||||
|
||||
struct SXDGPositionerState {
|
||||
Vector2D requestedSize;
|
||||
CBox anchorRect;
|
||||
xdgPositionerAnchor anchor = XDG_POSITIONER_ANCHOR_NONE;
|
||||
xdgPositionerGravity gravity = XDG_POSITIONER_GRAVITY_NONE;
|
||||
uint32_t constraintAdjustment = 0;
|
||||
Vector2D offset;
|
||||
bool reactive = false;
|
||||
Vector2D parentSize;
|
||||
Vector2D requestedSize;
|
||||
CBox anchorRect;
|
||||
CEdges anchor;
|
||||
CEdges gravity;
|
||||
uint32_t constraintAdjustment = 0;
|
||||
Vector2D offset;
|
||||
bool reactive = false;
|
||||
Vector2D parentSize;
|
||||
|
||||
void setAnchor(xdgPositionerAnchor edges);
|
||||
void setGravity(xdgPositionerGravity edges);
|
||||
};
|
||||
|
||||
class CXDGPositionerRules {
|
||||
public:
|
||||
CXDGPositionerRules(SP<CXDGPositionerResource> positioner);
|
||||
|
||||
CBox getPosition(const CBox& constraint, const Vector2D& parentPos);
|
||||
CBox getPosition(CBox constraint, const Vector2D& parentPos);
|
||||
|
||||
private:
|
||||
SXDGPositionerState state;
|
||||
|
@ -138,15 +141,24 @@ class CXDGToplevelResource {
|
|||
void applyState();
|
||||
};
|
||||
|
||||
class CXDGSurfaceResource : public ISurfaceRole {
|
||||
class CXDGSurfaceRole : public ISurfaceRole {
|
||||
public:
|
||||
CXDGSurfaceRole(SP<CXDGSurfaceResource> xdg);
|
||||
|
||||
virtual eSurfaceRole role() {
|
||||
return SURFACE_ROLE_XDG_SHELL;
|
||||
}
|
||||
|
||||
WP<CXDGSurfaceResource> xdgSurface;
|
||||
};
|
||||
|
||||
class CXDGSurfaceResource {
|
||||
public:
|
||||
CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBase> owner_, SP<CWLSurfaceResource> surface_);
|
||||
~CXDGSurfaceResource();
|
||||
|
||||
static SP<CXDGSurfaceResource> fromResource(wl_resource*);
|
||||
|
||||
virtual eSurfaceRole role();
|
||||
|
||||
bool good();
|
||||
|
||||
WP<CXDGWMBase> owner;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "Compositor.hpp"
|
||||
#include "Output.hpp"
|
||||
#include "Seat.hpp"
|
||||
#include "../types/WLBuffer.hpp"
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
@ -9,6 +10,7 @@
|
|||
#include "../PresentationTime.hpp"
|
||||
#include "../DRMSyncobj.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
#include <cstring>
|
||||
|
||||
#define LOGM PROTO::compositor->protoLog
|
||||
|
||||
|
@ -19,8 +21,6 @@ class CDefaultSurfaceRole : public ISurfaceRole {
|
|||
}
|
||||
};
|
||||
|
||||
SP<CDefaultSurfaceRole> defaultRole = makeShared<CDefaultSurfaceRole>();
|
||||
|
||||
CWLCallbackResource::CWLCallbackResource(SP<CWlCallback> resource_) : resource(resource_) {
|
||||
;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
|
|||
|
||||
resource->setData(this);
|
||||
|
||||
role = defaultRole;
|
||||
role = makeShared<CDefaultSurfaceRole>();
|
||||
|
||||
resource->setDestroy([this](CWlSurface* r) { destroy(); });
|
||||
resource->setOnDestroy([this](CWlSurface* r) { destroy(); });
|
||||
|
@ -75,41 +75,42 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
|
|||
pending.buffer.reset();
|
||||
pending.texture.reset();
|
||||
} else {
|
||||
auto res = CWLBufferResource::fromResource(buffer);
|
||||
pending.buffer = res && res->buffer ? res->buffer.lock() : nullptr;
|
||||
pending.size = res && res->buffer ? res->buffer->size : Vector2D{};
|
||||
pending.texture = res && res->buffer ? res->buffer->texture : nullptr;
|
||||
auto res = CWLBufferResource::fromResource(buffer);
|
||||
pending.buffer = res && res->buffer ? makeShared<CHLBufferReference>(res->buffer.lock(), self.lock()) : nullptr;
|
||||
pending.size = res && res->buffer ? res->buffer->size : Vector2D{};
|
||||
pending.texture = res && res->buffer ? res->buffer->texture : nullptr;
|
||||
pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{};
|
||||
}
|
||||
|
||||
Vector2D oldBufSize = current.buffer ? current.buffer->size : Vector2D{};
|
||||
Vector2D newBufSize = pending.buffer ? pending.buffer->size : Vector2D{};
|
||||
Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{};
|
||||
Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{};
|
||||
|
||||
if (oldBufSize != newBufSize)
|
||||
if (oldBufSize != newBufSize || current.buffer != pending.buffer)
|
||||
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
|
||||
|
||||
bufferReleased = false;
|
||||
});
|
||||
|
||||
resource->setCommit([this](CWlSurface* r) {
|
||||
if (pending.buffer)
|
||||
pending.bufferDamage.intersect(CBox{{}, pending.buffer->size});
|
||||
if (pending.texture)
|
||||
pending.bufferDamage.intersect(CBox{{}, pending.bufferSize});
|
||||
|
||||
if (!pending.buffer)
|
||||
if (!pending.texture)
|
||||
pending.size = {};
|
||||
else if (pending.viewport.hasDestination)
|
||||
pending.size = pending.viewport.destination;
|
||||
else if (pending.viewport.hasSource)
|
||||
pending.size = pending.viewport.source.size();
|
||||
else {
|
||||
Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.buffer->size.y, pending.buffer->size.x} : pending.buffer->size;
|
||||
Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.bufferSize.y, pending.bufferSize.x} : pending.bufferSize;
|
||||
pending.size = tfs / pending.scale;
|
||||
}
|
||||
|
||||
pending.damage.intersect(CBox{{}, pending.size});
|
||||
|
||||
events.precommit.emit();
|
||||
if (pending.rejected)
|
||||
if (pending.rejected) {
|
||||
dropPendingBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stateLocks <= 0)
|
||||
commitPendingState();
|
||||
|
@ -151,12 +152,23 @@ CWLSurfaceResource::~CWLSurfaceResource() {
|
|||
}
|
||||
|
||||
void CWLSurfaceResource::destroy() {
|
||||
if (mapped)
|
||||
if (mapped) {
|
||||
events.unmap.emit();
|
||||
unmap();
|
||||
}
|
||||
events.destroy.emit();
|
||||
releaseBuffers(false);
|
||||
PROTO::compositor->destroyResource(this);
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::dropPendingBuffer() {
|
||||
pending.buffer.reset();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::dropCurrentBuffer() {
|
||||
current.buffer.reset();
|
||||
}
|
||||
|
||||
SP<CWLSurfaceResource> CWLSurfaceResource::fromResource(wl_resource* res) {
|
||||
auto data = (CWLSurfaceResource*)(((CWlSurface*)wl_resource_get_user_data(res))->data());
|
||||
return data ? data->self.lock() : nullptr;
|
||||
|
@ -237,7 +249,7 @@ void CWLSurfaceResource::frame(timespec* now) {
|
|||
}
|
||||
|
||||
void CWLSurfaceResource::resetRole() {
|
||||
role = defaultRole;
|
||||
role = makeShared<CDefaultSurfaceRole>();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data) {
|
||||
|
@ -251,6 +263,8 @@ void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std
|
|||
for (auto& c : n->subsurfaces) {
|
||||
if (c->zIndex >= 0)
|
||||
break;
|
||||
if (c->surface.expired())
|
||||
continue;
|
||||
nodes2.push_back(c->surface.lock());
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +277,7 @@ void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std
|
|||
for (auto& n : nodes) {
|
||||
Vector2D offset = {};
|
||||
if (n->role->role() == SURFACE_ROLE_SUBSURFACE) {
|
||||
auto subsurface = (CWLSubsurfaceResource*)n->role.get();
|
||||
auto subsurface = ((CSubsurfaceRole*)n->role.get())->subsurface.lock();
|
||||
offset = subsurface->posRelativeToParent();
|
||||
}
|
||||
|
||||
|
@ -274,6 +288,8 @@ void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std
|
|||
for (auto& c : n->subsurfaces) {
|
||||
if (c->zIndex < 0)
|
||||
continue;
|
||||
if (c->surface.expired())
|
||||
continue;
|
||||
nodes2.push_back(c->surface.lock());
|
||||
}
|
||||
}
|
||||
|
@ -319,8 +335,6 @@ void CWLSurfaceResource::map() {
|
|||
|
||||
mapped = true;
|
||||
|
||||
events.map.emit();
|
||||
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
frame(&now);
|
||||
|
@ -335,7 +349,16 @@ void CWLSurfaceResource::unmap() {
|
|||
|
||||
mapped = false;
|
||||
|
||||
events.unmap.emit();
|
||||
// release the buffers.
|
||||
// this is necessary for XWayland to function correctly,
|
||||
// as it does not unmap via the traditional commit(null buffer) method, but via the X11 protocol.
|
||||
releaseBuffers();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::releaseBuffers(bool onlyCurrent) {
|
||||
if (!onlyCurrent)
|
||||
dropPendingBuffer();
|
||||
dropCurrentBuffer();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::error(int code, const std::string& str) {
|
||||
|
@ -360,18 +383,18 @@ CBox CWLSurfaceResource::extends() {
|
|||
}
|
||||
|
||||
Vector2D CWLSurfaceResource::sourceSize() {
|
||||
if (!current.buffer)
|
||||
if (!current.texture)
|
||||
return {};
|
||||
|
||||
if (current.viewport.hasSource)
|
||||
return current.viewport.source.size();
|
||||
|
||||
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size;
|
||||
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize;
|
||||
return trc / current.scale;
|
||||
}
|
||||
|
||||
CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
|
||||
if (!current.buffer)
|
||||
if (!current.texture)
|
||||
return {};
|
||||
|
||||
CRegion surfaceDamage = current.damage;
|
||||
|
@ -383,7 +406,7 @@ CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
|
|||
if (current.viewport.hasSource)
|
||||
surfaceDamage.translate(current.viewport.source.pos());
|
||||
|
||||
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size;
|
||||
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);
|
||||
}
|
||||
|
@ -406,23 +429,26 @@ void CWLSurfaceResource::commitPendingState() {
|
|||
pending.damage.clear();
|
||||
pending.bufferDamage.clear();
|
||||
|
||||
if (current.buffer && !bufferReleased) {
|
||||
// without previous dolphin et al are weird vvv
|
||||
//CRegion surfaceDamage =
|
||||
// current.damage.copy().scale(current.scale).transform(current.transform, current.size.x, current.size.y).add(current.bufferDamage).add(previousBufferDamage);
|
||||
current.buffer->update(CBox{{}, {INT32_MAX, INT32_MAX}}); // FIXME: figure this out to not use this hack. QT apps are wonky without this.
|
||||
if (current.texture)
|
||||
current.texture->m_eTransform = wlTransformToHyprutils(current.transform);
|
||||
|
||||
if (current.buffer && current.buffer->buffer) {
|
||||
current.buffer->buffer->update(accumulateCurrentBufferDamage());
|
||||
|
||||
// if the surface is a cursor, update the shm buffer
|
||||
// TODO: don't update the entire texture
|
||||
if (role->role() == SURFACE_ROLE_CURSOR)
|
||||
updateCursorShm();
|
||||
|
||||
// release the buffer if it's synchronous as update() has done everything thats needed
|
||||
// so we can let the app know we're done.
|
||||
if (current.buffer->isSynchronous()) {
|
||||
current.buffer->sendReleaseWithSurface(self.lock());
|
||||
bufferReleased = true;
|
||||
}
|
||||
if (current.buffer->buffer->isSynchronous())
|
||||
dropCurrentBuffer();
|
||||
}
|
||||
|
||||
// TODO: we should _accumulate_ and not replace above if sync
|
||||
if (role->role() == SURFACE_ROLE_SUBSURFACE) {
|
||||
auto subsurface = (CWLSubsurfaceResource*)role.get();
|
||||
auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock();
|
||||
if (subsurface->sync)
|
||||
return;
|
||||
|
||||
|
@ -432,7 +458,7 @@ void CWLSurfaceResource::commitPendingState() {
|
|||
breadthfirst(
|
||||
[](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) {
|
||||
if (surf->role->role() == SURFACE_ROLE_SUBSURFACE) {
|
||||
auto subsurface = (CWLSubsurfaceResource*)surf->role.get();
|
||||
auto subsurface = ((CSubsurfaceRole*)surf->role.get())->subsurface.lock();
|
||||
if (!subsurface->sync)
|
||||
return;
|
||||
}
|
||||
|
@ -442,28 +468,44 @@ void CWLSurfaceResource::commitPendingState() {
|
|||
}
|
||||
|
||||
// for async buffers, we can only release the buffer once we are unrefing it from current.
|
||||
if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) {
|
||||
if (previousBuffer->lockedByBackend) {
|
||||
previousBuffer->hlEvents.backendRelease = previousBuffer->events.backendRelease.registerListener([this, previousBuffer](std::any data) {
|
||||
if (!self.expired()) // could be dead in the dtor
|
||||
previousBuffer->sendReleaseWithSurface(self.lock());
|
||||
else
|
||||
previousBuffer->sendRelease();
|
||||
previousBuffer->hlEvents.backendRelease.reset();
|
||||
bufferReleased = true;
|
||||
});
|
||||
} else
|
||||
previousBuffer->sendReleaseWithSurface(self.lock());
|
||||
|
||||
bufferReleased = true;
|
||||
// if the backend took it, ref it with the lambda. Otherwise, the end of this scope will release it.
|
||||
if (previousBuffer && previousBuffer->buffer && !previousBuffer->buffer->isSynchronous()) {
|
||||
if (previousBuffer->buffer->lockedByBackend && !previousBuffer->buffer->hlEvents.backendRelease) {
|
||||
previousBuffer->buffer->lock();
|
||||
previousBuffer->buffer->unlockOnBufferRelease(self);
|
||||
}
|
||||
}
|
||||
|
||||
lastBuffer = current.buffer ? current.buffer->buffer : WP<IHLBuffer>{};
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::updateCursorShm() {
|
||||
auto buf = current.buffer ? current.buffer->buffer : lastBuffer;
|
||||
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
// TODO: actually use damage
|
||||
auto& shmData = CCursorSurfaceRole::cursorPixelData(self.lock());
|
||||
auto shmAttrs = buf->shm();
|
||||
|
||||
if (!shmAttrs.success) {
|
||||
LOGM(TRACE, "updateCursorShm: ignoring, not a shm buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to end, shm.
|
||||
auto [pixelData, fmt, bufLen] = buf->beginDataPtr(0);
|
||||
|
||||
shmData.resize(bufLen);
|
||||
memcpy(shmData.data(), pixelData, bufLen);
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync) {
|
||||
frame(when);
|
||||
auto FEEDBACK = makeShared<CQueuedPresentationData>(self.lock());
|
||||
FEEDBACK->attachMonitor(pMonitor);
|
||||
FEEDBACK->discarded();
|
||||
FEEDBACK->presented();
|
||||
PROTO::presentation->queueData(FEEDBACK);
|
||||
|
||||
if (!pMonitor || !pMonitor->outTimeline || !syncobj || !needsExplicitSync)
|
||||
|
|
|
@ -84,13 +84,13 @@ class CWLSurfaceResource {
|
|||
} events;
|
||||
|
||||
struct SState {
|
||||
CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */;
|
||||
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
int scale = 1;
|
||||
SP<IHLBuffer> buffer;
|
||||
SP<CTexture> texture;
|
||||
Vector2D offset;
|
||||
Vector2D size;
|
||||
CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */;
|
||||
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
int scale = 1;
|
||||
SP<CHLBufferReference> buffer; // buffer ref will be released once the buffer is no longer locked. For checking if a buffer is attached to this state, check texture.
|
||||
SP<CTexture> texture;
|
||||
Vector2D offset;
|
||||
Vector2D size, bufferSize;
|
||||
struct {
|
||||
bool hasDestination = false;
|
||||
bool hasSource = false;
|
||||
|
@ -116,7 +116,7 @@ class CWLSurfaceResource {
|
|||
std::vector<WP<CMonitor>> enteredOutputs;
|
||||
bool mapped = false;
|
||||
std::vector<WP<CWLSubsurfaceResource>> subsurfaces;
|
||||
WP<ISurfaceRole> role;
|
||||
SP<ISurfaceRole> role;
|
||||
WP<CViewportResource> viewportResource;
|
||||
WP<CDRMSyncobjSurfaceResource> syncobj; // may not be present
|
||||
|
||||
|
@ -134,14 +134,21 @@ class CWLSurfaceResource {
|
|||
SP<CWlSurface> resource;
|
||||
wl_client* pClient = nullptr;
|
||||
|
||||
// tracks whether we should release the buffer
|
||||
bool bufferReleased = false;
|
||||
// this is for cursor dumb copy. Due to our (and wayland's...) architecture,
|
||||
// this stupid-ass hack is used
|
||||
WP<IHLBuffer> lastBuffer;
|
||||
|
||||
int stateLocks = 0;
|
||||
int stateLocks = 0;
|
||||
|
||||
void destroy();
|
||||
void commitPendingState();
|
||||
void bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
|
||||
void destroy();
|
||||
void releaseBuffers(bool onlyCurrent = true);
|
||||
void dropPendingBuffer();
|
||||
void dropCurrentBuffer();
|
||||
void commitPendingState();
|
||||
void bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
|
||||
void updateCursorShm();
|
||||
|
||||
friend class CWLPointerResource;
|
||||
};
|
||||
|
||||
class CWLCompositorResource {
|
||||
|
|
|
@ -469,12 +469,12 @@ void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource
|
|||
if (dragSurface) {
|
||||
dnd.dndSurfaceDestroy = dragSurface->events.destroy.registerListener([this](std::any d) { abortDrag(); });
|
||||
dnd.dndSurfaceCommit = dragSurface->events.commit.registerListener([this](std::any d) {
|
||||
if (dnd.dndSurface->current.buffer && !dnd.dndSurface->mapped) {
|
||||
if (dnd.dndSurface->current.texture && !dnd.dndSurface->mapped) {
|
||||
dnd.dndSurface->map();
|
||||
return;
|
||||
}
|
||||
|
||||
if (dnd.dndSurface->current.buffer <= 0 && dnd.dndSurface->mapped) {
|
||||
if (dnd.dndSurface->current.texture <= 0 && dnd.dndSurface->mapped) {
|
||||
dnd.dndSurface->unmap();
|
||||
return;
|
||||
}
|
||||
|
@ -660,13 +660,13 @@ void CWLDataDeviceProtocol::abortDrag() {
|
|||
}
|
||||
|
||||
void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
|
||||
if (!dnd.dndSurface || !dnd.dndSurface->current.buffer || !dnd.dndSurface->current.buffer->texture)
|
||||
if (!dnd.dndSurface || !dnd.dndSurface->current.texture)
|
||||
return;
|
||||
|
||||
const auto POS = g_pInputManager->getMouseCoordsInternal();
|
||||
|
||||
CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale);
|
||||
g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.buffer->texture, &box, 1.F);
|
||||
g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.texture, &box, 1.F);
|
||||
|
||||
box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F);
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
|
|
|
@ -49,7 +49,7 @@ SP<CWlOutput> CWLOutputResource::getResource() {
|
|||
}
|
||||
|
||||
void CWLOutputResource::updateState() {
|
||||
if (!monitor)
|
||||
if (!monitor || (owner && owner->defunct))
|
||||
return;
|
||||
|
||||
if (resource->version() >= 2)
|
||||
|
@ -83,7 +83,8 @@ void CWLOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver,
|
|||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
RESOURCE->self = RESOURCE;
|
||||
RESOURCE->owner = self;
|
||||
}
|
||||
|
||||
void CWLOutputProtocol::destroyResource(CWLOutputResource* resource) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "../../helpers/signal/Signal.hpp"
|
||||
|
||||
class CMonitor;
|
||||
class CWLOutputProtocol;
|
||||
|
||||
class CWLOutputResource {
|
||||
public:
|
||||
|
@ -20,7 +21,7 @@ class CWLOutputResource {
|
|||
void updateState();
|
||||
|
||||
WP<CMonitor> monitor;
|
||||
|
||||
WP<CWLOutputProtocol> owner;
|
||||
WP<CWLOutputResource> self;
|
||||
|
||||
private:
|
||||
|
@ -40,6 +41,7 @@ class CWLOutputProtocol : public IWaylandProtocol {
|
|||
void sendDone();
|
||||
|
||||
WP<CMonitor> monitor;
|
||||
WP<CWLOutputProtocol> self;
|
||||
|
||||
// will mark the protocol for removal, will be removed when no. of bound outputs is 0 (or when overwritten by a new global)
|
||||
void remove();
|
||||
|
@ -61,5 +63,5 @@ class CWLOutputProtocol : public IWaylandProtocol {
|
|||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline std::unordered_map<std::string, UP<CWLOutputProtocol>> outputs;
|
||||
inline std::unordered_map<std::string, SP<CWLOutputProtocol>> outputs;
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue