diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6d25e027..7739d2b2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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: diff --git a/.github/workflows/nix-build.yml b/.github/workflows/nix-build.yml index 33ee5cac..d0234498 100644 --- a/.github/workflows/nix-build.yml +++ b/.github/workflows/nix-build.yml @@ -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 }}' diff --git a/.github/workflows/nix-ci.yml b/.github/workflows/nix-ci.yml index a66307c4..b25ed9aa 100644 --- a/.github/workflows/nix-ci.yml +++ b/.github/workflows/nix-ci.yml @@ -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 diff --git a/.github/workflows/security-checks.yml b/.github/workflows/security-checks.yml index 564013cf..4f539132 100644 --- a/.github/workflows/security-checks.yml +++ b/.github/workflows/security-checks.yml @@ -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: diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bd4e0d5..fa58b63d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") diff --git a/assets/meson.build b/assets/meson.build index 8c4a60ec..47de3d02 100644 --- a/assets/meson.build +++ b/assets/meson.build @@ -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') diff --git a/example/meson.build b/example/meson.build index ccfc4e00..2fb3a35e 100644 --- a/example/meson.build +++ b/example/meson.build @@ -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') diff --git a/flake.lock b/flake.lock index f61f18a1..5c384d4d 100644 --- a/flake.lock +++ b/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": { diff --git a/hyprctl/main.cpp b/hyprctl/main.cpp index 45fe9142..336d479e 100644 --- a/hyprctl/main.cpp +++ b/hyprctl/main.cpp @@ -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(), ' '); diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index 036609f3..6988547c 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -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 deps = {"meson", "cpio", "cmake"}; for (auto& d : deps) { diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp index 13ea5b12..c16a9d0f 100644 --- a/hyprpm/src/core/PluginManager.hpp +++ b/hyprpm/src/core/PluginManager.hpp @@ -66,6 +66,7 @@ class CPluginManager { private: std::string headerError(const eHeadersErrors err); + std::string headerErrorShort(const eHeadersErrors err); std::string m_szWorkingPluginDirectory = ""; }; diff --git a/meson.build b/meson.build index 4446ea0f..886f4f9c 100644 --- a/meson.build +++ b/meson.build @@ -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') diff --git a/nix/default.nix b/nix/default.nix index 7775b729..e4e12f43 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -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 [ diff --git a/protocols/meson.build b/protocols/meson.build index 5cdeb160..5b807914 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -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) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index c7a0cfb1..a139742c 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -7,6 +7,7 @@ #include "managers/SeatManager.hpp" #include "managers/eventLoop/EventLoopManager.hpp" #include +#include #include #include #include @@ -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> 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 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 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 CCompositor::vectorToLayerSurface(const Vector2D& pos, st } PHLWINDOW CCompositor::getWindowFromSurface(SP 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("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 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 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; } diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 58c5b33d..295935c4 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -69,8 +69,9 @@ class CCompositor { std::unordered_map 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); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 7eb36e72..111f4701 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -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 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 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 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 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 CConfigManager::handleUnbind(const std::string& comma bool windowRuleValid(const std::string& RULE) { static const auto rules = std::unordered_set{ - "fakefullscreen", "float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", + "float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", }; static const auto rulesPrefix = std::vector{ - "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, ' '); diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 51c851ad..32a50134 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -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 m_pConfig; diff --git a/src/debug/CrashReporter.cpp b/src/debug/CrashReporter.cpp index c25212fe..c31975b8 100644 --- a/src/debug/CrashReporter.cpp +++ b/src/debug/CrashReporter.cpp @@ -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"; diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index b81cfeff..d91a1cec 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -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 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); } diff --git a/src/debug/HyprCtl.hpp b/src/debug/HyprCtl.hpp index ccbd40cd..cbacd7cb 100644 --- a/src/debug/HyprCtl.hpp +++ b/src/debug/HyprCtl.hpp @@ -31,6 +31,7 @@ class CHyprCtl { std::vector> m_vCommands; wl_event_source* m_eventSource = nullptr; + std::string m_socketPath; }; inline std::unique_ptr g_pHyprCtl; diff --git a/src/debug/Log.cpp b/src/debug/Log.cpp index c2939831..0def77c0 100644 --- a/src/debug/Log.cpp +++ b/src/debug/Log.cpp @@ -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. diff --git a/src/debug/Log.hpp b/src/debug/Log.hpp index 33f3c9c1..617f451a 100644 --- a/src/debug/Log.hpp +++ b/src/debug/Log.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #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 void log(LogLevel level, std::format_string fmt, Args&&... args) { + std::lock_guard 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(); } }; diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp index 82915a77..afb8fc17 100644 --- a/src/desktop/LayerSurface.cpp +++ b/src/desktop/LayerSurface.cpp @@ -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); diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp index 131a26b5..e48b7400 100644 --- a/src/desktop/Popup.cpp +++ b/src/desktop/Popup.cpp @@ -22,7 +22,7 @@ CPopup::CPopup(SP 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() { diff --git a/src/desktop/Popup.hpp b/src/desktop/Popup.hpp index d045cd41..eea3fb84 100644 --- a/src/desktop/Popup.hpp +++ b/src/desktop/Popup.hpp @@ -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 nodes, std::function fn, void* data); -}; \ No newline at end of file +}; diff --git a/src/desktop/WLSurface.cpp b/src/desktop/WLSurface.cpp index 97335d27..45050e35 100644 --- a/src/desktop/WLSurface.cpp +++ b/src/desktop/WLSurface.cpp @@ -1,6 +1,7 @@ #include "WLSurface.hpp" #include "../Compositor.hpp" #include "../protocols/core/Compositor.hpp" +#include "../protocols/LayerShell.hpp" void CWLSurface::assign(SP 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 CWLSurface::getSurfaceBoxGlobal() { +std::optional CWLSurface::getSurfaceBoxGlobal() const { if (!desktopComponent()) return {}; @@ -181,7 +201,7 @@ void CWLSurface::appendConstraint(WP constraint) { m_pConstraint = constraint; } -SP CWLSurface::constraint() { +SP CWLSurface::constraint() const { return m_pConstraint.lock(); } @@ -202,3 +222,11 @@ SP CWLSurface::fromResource(SP 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; +} diff --git a/src/desktop/WLSurface.hpp b/src/desktop/WLSurface.hpp index 447f4582..a962b882 100644 --- a/src/desktop/WLSurface.hpp +++ b/src/desktop/WLSurface.hpp @@ -33,22 +33,24 @@ class CWLSurface { SP 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 getSurfaceBoxGlobal(); + std::optional getSurfaceBoxGlobal() const; void appendConstraint(WP constraint); - SP constraint(); + SP 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; diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 7497957a..93c208cf 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #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("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("misc:initial_workspace_tracking"); + static auto PINITIALWSTRACKING = CConfigValue("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("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()); diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index 60189fac..11bf662a 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -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 alphaInactive = SAlphaValue{1.f, false}; CWindowOverridableVar alphaFullscreen = SAlphaValue{1.f, false}; - CWindowOverridableVar allowsInput = false; - CWindowOverridableVar dimAround = false; - CWindowOverridableVar decorate = true; - CWindowOverridableVar focusOnActivate = false; - CWindowOverridableVar keepAspectRatio = false; - CWindowOverridableVar nearestNeighbor = false; - CWindowOverridableVar noAnim = false; - CWindowOverridableVar noBorder = false; - CWindowOverridableVar noBlur = false; - CWindowOverridableVar noDim = false; - CWindowOverridableVar noFocus = false; - CWindowOverridableVar noMaxSize = false; - CWindowOverridableVar noRounding = false; - CWindowOverridableVar noShadow = false; - CWindowOverridableVar opaque = false; - CWindowOverridableVar RGBX = false; - CWindowOverridableVar tearing = false; - CWindowOverridableVar xray = false; + CWindowOverridableVar allowsInput = false; + CWindowOverridableVar dimAround = false; + CWindowOverridableVar decorate = true; + CWindowOverridableVar focusOnActivate = false; + CWindowOverridableVar keepAspectRatio = false; + CWindowOverridableVar nearestNeighbor = false; + CWindowOverridableVar noAnim = false; + CWindowOverridableVar noBorder = false; + CWindowOverridableVar noBlur = false; + CWindowOverridableVar noDim = false; + CWindowOverridableVar noFocus = false; + CWindowOverridableVar noMaxSize = false; + CWindowOverridableVar noRounding = false; + CWindowOverridableVar noShadow = false; + CWindowOverridableVar noShortcutsInhibit = false; + CWindowOverridableVar opaque = false; + CWindowOverridableVar RGBX = false; + CWindowOverridableVar syncFullscreen = true; + CWindowOverridableVar tearing = false; + CWindowOverridableVar xray = false; CWindowOverridableVar rounding; CWindowOverridableVar 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); @@ -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&); diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp index 34db914e..a08f1804 100644 --- a/src/desktop/Workspace.cpp +++ b/src/desktop/Workspace.cpp @@ -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; diff --git a/src/desktop/Workspace.hpp b/src/desktop/Workspace.hpp index bb5cd441..3e9ac8a8 100644 --- a/src/desktop/Workspace.hpp +++ b/src/desktop/Workspace.hpp @@ -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; diff --git a/src/devices/IKeyboard.cpp b/src/devices/IKeyboard.cpp index 284a8295..e05cbd04 100644 --- a/src/devices/IKeyboard.cpp +++ b/src/devices/IKeyboard.cpp @@ -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 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 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(); } diff --git a/src/devices/IKeyboard.hpp b/src/devices/IKeyboard.hpp index 1ae6c7bc..ad8eaf7e 100644 --- a/src/devices/IKeyboard.hpp +++ b/src/devices/IKeyboard.hpp @@ -5,6 +5,7 @@ #include "../macros.hpp" #include "../helpers/math/Math.hpp" +#include #include 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 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; diff --git a/src/devices/IPointer.hpp b/src/devices/IPointer.hpp index 5df47c04..760ec8dc 100644 --- a/src/devices/IPointer.hpp +++ b/src/devices/IPointer.hpp @@ -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 self; }; diff --git a/src/devices/Keyboard.cpp b/src/devices/Keyboard.cpp index ae28c07d..0a8f6b57 100644 --- a/src/devices/Keyboard.cpp +++ b/src/devices/Keyboard.cpp @@ -32,13 +32,13 @@ CKeyboard::CKeyboard(SP keeb) : keyboard(keeb) { listeners.key = keeb->events.key.registerListener([this](std::any d) { auto E = std::any_cast(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) { diff --git a/src/devices/VirtualKeyboard.cpp b/src/devices/VirtualKeyboard.cpp index 654ca9f5..548711c2 100644 --- a/src/devices/VirtualKeyboard.cpp +++ b/src/devices/VirtualKeyboard.cpp @@ -31,8 +31,13 @@ CVirtualKeyboard::CVirtualKeyboard(SP keeb_) : keybo }); }); listeners.keymap = keeb_->events.keymap.registerListener([this](std::any d) { - auto E = std::any_cast(d); - xkbKeymap = xkb_keymap_ref(E.keymap); + auto E = std::any_cast(d); + if (xkbKeymap) + xkb_keymap_unref(xkbKeymap); + xkbKeymap = xkb_keymap_ref(E.keymap); + keymapOverridden = true; + updateXKBTranslationState(xkbKeymap); + updateKeymapFD(); keyboardEvents.keymap.emit(d); }); diff --git a/src/devices/VirtualPointer.cpp b/src/devices/VirtualPointer.cpp index faca27dc..7ad18775 100644 --- a/src/devices/VirtualPointer.cpp +++ b/src/devices/VirtualPointer.cpp @@ -20,7 +20,12 @@ CVirtualPointer::CVirtualPointer(SP 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(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 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; } diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index 40b6f17e..b2778062 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -5,6 +5,8 @@ #include "Events.hpp" #include "../debug/HyprCtl.hpp" #include "../config/ConfigValue.hpp" +#include "../protocols/Screencopy.hpp" +#include "../protocols/ToplevelExport.hpp" #include // --------------------------------------------------------- // @@ -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); } } diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 516ac2ae..5d29a3b7 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -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 @@ -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 requestedInternalFSMode, requestedClientFSMode; + std::optional 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("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; diff --git a/src/helpers/BezierCurve.cpp b/src/helpers/BezierCurve.cpp index e79863a3..dd0ff2b0 100644 --- a/src/helpers/BezierCurve.cpp +++ b/src/helpers/BezierCurve.cpp @@ -30,8 +30,10 @@ void CBezierCurve::setup(std::vector* 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::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, diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index a23b9861..1cf1b069 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -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(); diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index a325d858..bbc3a006 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -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("misc:font_family"); diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 7fa6fdbc..f287056f 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -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("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]); diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp index 13834bc7..f638f6a2 100644 --- a/src/layout/DwindleLayout.hpp +++ b/src/layout/DwindleLayout.hpp @@ -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); diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index 528cea72..233933d4 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -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(); diff --git a/src/layout/IHyprLayout.hpp b/src/layout/IHyprLayout.hpp index 74a00d19..4b1b59e3 100644 --- a/src/layout/IHyprLayout.hpp +++ b/src/layout/IHyprLayout.hpp @@ -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 diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp index d7f264e7..be00168f 100644 --- a/src/layout/MasterLayout.cpp +++ b/src/layout/MasterLayout.cpp @@ -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("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("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()); diff --git a/src/layout/MasterLayout.hpp b/src/layout/MasterLayout.hpp index 30d5b3cf..fdb916e5 100644 --- a/src/layout/MasterLayout.hpp +++ b/src/layout/MasterLayout.hpp @@ -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); diff --git a/src/main.cpp b/src/main.cpp index 1ac3ab8b..e85b0a22 100644 --- a/src/main.cpp +++ b/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; } diff --git a/src/managers/CursorManager.cpp b/src/managers/CursorManager.cpp index 3a13d10b..e0d3a62c 100644 --- a/src/managers/CursorManager.cpp +++ b/src/managers/CursorManager.cpp @@ -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((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 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>> 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(); - 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(); @@ -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::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(); + 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; } diff --git a/src/managers/CursorManager.hpp b/src/managers/CursorManager.hpp index 4324dfb4..44ab2e7b 100644 --- a/src/managers/CursorManager.hpp +++ b/src/managers/CursorManager.hpp @@ -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 createDefaultCursor(); - int lastLoadSize = 0; - - bool themeLoaded = false; - std::string themeName = ""; - SP defaultCursor; - std::unordered_map> cursors; + int lastLoadSize = 0; + bool themeLoaded = false; + std::string themeName = ""; + SP defaultCursor; + std::vector>> cursors; } xcursor; }; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index e9dfd1ae..09ba7d50 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -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> getHyprlandLaunchEnv() { static auto PINITIALWSTRACKING = CConfigValue("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("input:follow_mouse"); + static auto PNOWARPS = CConfigValue("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("input:follow_mouse"); + static auto PNOWARPS = CConfigValue("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("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 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("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) { diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index 284280cd..26b42b00 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -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 m_mKeyToCodeCache; + static void changeMouseBindMode(const eMouseBindMode mode); + private: std::deque m_dPressedKeys; @@ -115,7 +118,6 @@ class CKeybindManager { uint32_t m_uLastCode = 0; uint32_t m_uLastMouseCode = 0; - bool m_bIsMouseBindActive = false; std::vector 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); diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index cf10db71..3ba34c11 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -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 @@ -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(data)->self.lock()); + if (!state) + return; + + state->cursorRendered = false; + }); } void CPointerManager::lockSoftwareAll() { @@ -143,18 +152,20 @@ void CPointerManager::setCursorSurface(SP 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 CPointerManager::renderHWCursorBuffer(SPcursorRendered) + 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 CPointerManager::renderHWCursorBuffer(SP(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 dev) { SP 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 dev) { } break; } + case HID_TYPE_POINTER: { + IPointer* POINTER = reinterpret_cast(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 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 CPointerManager::getCurrentCursorTexture() { return currentCursorImage.bufferTex; } - return currentCursorImage.surface->resource()->current.buffer->texture; + return currentCursorImage.surface->resource()->current.texture; } void CPointerManager::attachPointer(SP pointer) { diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index cf4f1a94..4a4c4f61 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -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 cursorFrontBuffer; }; @@ -177,6 +178,7 @@ class CPointerManager { struct { SP monitorAdded; + SP monitorPreRender; } hooks; }; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 387cac8f..635e6223 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -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(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock())); + PROTO::outputs.emplace(pMonitor->szName, makeShared(&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(&wl_output_interface, 4, std::format("WLOutput ({})", M->szName), M->self.lock())); + + auto ref = makeShared(&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(&zwp_pointer_gestures_v1_interface, 3, "PointerGestures"); PROTO::foreignToplevelWlr = std::make_unique(&zwlr_foreign_toplevel_manager_v1_interface, 3, "ForeignToplevelWlr"); PROTO::shortcutsInhibit = std::make_unique(&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1, "ShortcutsInhibit"); + PROTO::textInputV1 = std::make_unique(&zwp_text_input_manager_v1_interface, 1, "TextInputV1"); PROTO::textInputV3 = std::make_unique(&zwp_text_input_manager_v3_interface, 1, "TextInputV3"); PROTO::constraints = std::make_unique(&zwp_pointer_constraints_v1_interface, 1, "PointerConstraints"); PROTO::outputPower = std::make_unique(&zwlr_output_power_manager_v1_interface, 1, "OutputPower"); @@ -140,6 +147,9 @@ CProtocolManager::CProtocolManager() { PROTO::dataWlr = std::make_unique(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr"); PROTO::primarySelection = std::make_unique(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection"); PROTO::xwaylandShell = std::make_unique(&xwayland_shell_v1_interface, 1, "XWaylandShell"); + PROTO::screencopy = std::make_unique(&zwlr_screencopy_manager_v1_interface, 3, "Screencopy"); + PROTO::toplevelExport = std::make_unique(&hyprland_toplevel_export_manager_v1_interface, 2, "ToplevelExport"); + PROTO::globalShortcuts = std::make_unique(&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(&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(); - m_pTextInputV1ProtocolManager = std::make_unique(); - m_pGlobalShortcutsProtocolManager = std::make_unique(); - m_pScreencopyProtocolManager = std::make_unique(); +} + +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(); } diff --git a/src/managers/ProtocolManager.hpp b/src/managers/ProtocolManager.hpp index 91fb82a0..1b6d31b6 100644 --- a/src/managers/ProtocolManager.hpp +++ b/src/managers/ProtocolManager.hpp @@ -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 @@ -12,12 +9,7 @@ class CProtocolManager { public: CProtocolManager(); - - // TODO: rewrite to use the new protocol framework - std::unique_ptr m_pToplevelExportProtocolManager; - std::unique_ptr m_pTextInputV1ProtocolManager; - std::unique_ptr m_pGlobalShortcutsProtocolManager; - std::unique_ptr m_pScreencopyProtocolManager; + ~CProtocolManager(); private: std::unordered_map m_mModeChangeListeners; diff --git a/src/managers/SessionLockManager.cpp b/src/managers/SessionLockManager.cpp index 83ff3ee7..a82432a8 100644 --- a/src/managers/SessionLockManager.cpp +++ b/src/managers/SessionLockManager.cpp @@ -71,7 +71,8 @@ void CSessionLockManager::onNewSessionLock(SP 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; } diff --git a/src/managers/eventLoop/EventLoopManager.cpp b/src/managers/eventLoop/EventLoopManager.cpp index ed7a4f72..c2c088f8 100644 --- a/src/managers/eventLoop/EventLoopManager.cpp +++ b/src/managers/eventLoop/EventLoopManager.cpp @@ -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) { diff --git a/src/managers/input/IdleInhibitor.cpp b/src/managers/input/IdleInhibitor.cpp index a38acdbf..2c335a7e 100644 --- a/src/managers/input/IdleInhibitor.cpp +++ b/src/managers/input/IdleInhibitor.cpp @@ -7,7 +7,7 @@ void CInputManager::newIdleInhibitor(std::any inhibitor) { const auto PINHIBIT = m_vIdleInhibitors.emplace_back(std::make_unique()).get(); PINHIBIT->inhibitor = std::any_cast>(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; } diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 492d2805..8c38893f 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -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(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 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 keyboard) { @@ -992,7 +993,7 @@ void CInputManager::newMouse(SP 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 mauz) { @@ -1264,7 +1265,14 @@ void CInputManager::updateKeyboardsLeds(SP pKeyboard) { if (!pKeyboard) return; - pKeyboard->updateLEDs(); + std::optional leds = pKeyboard->getLEDs(); + + if (!leds.has_value()) + return; + + for (auto& k : m_vKeyboards) { + k->updateLEDs(leds.value()); + } } void CInputManager::onKeyboardKey(std::any event, SP 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; } diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp index f1fe6421..7cfd54b4 100644 --- a/src/managers/input/InputMethodRelay.cpp +++ b/src/managers/input/InputMethodRelay.cpp @@ -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>(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>(ti)); }); + listeners.newTIV1 = PROTO::textInputV1->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(std::any_cast>(ti)); }); listeners.newIME = PROTO::ime->events.newIME.registerListener([this](std::any ime) { onNewIME(std::any_cast>(ime)); }); } @@ -86,11 +88,11 @@ CTextInput* CInputMethodRelay::getFocusedTextInput() { return nullptr; } -void CInputMethodRelay::onNewTextInput(std::any tiv3) { - m_vTextInputs.emplace_back(std::make_unique(std::any_cast>(tiv3))); +void CInputMethodRelay::onNewTextInput(WP tiv3) { + m_vTextInputs.emplace_back(std::make_unique(tiv3)); } -void CInputMethodRelay::onNewTextInput(STextInputV1* pTIV1) { +void CInputMethodRelay::onNewTextInput(WP pTIV1) { m_vTextInputs.emplace_back(std::make_unique(pTIV1)); } diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp index e13a1f1c..5ecc11a2 100644 --- a/src/managers/input/InputMethodRelay.hpp +++ b/src/managers/input/InputMethodRelay.hpp @@ -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); - void onNewTextInput(std::any tiv3); - void onNewTextInput(STextInputV1* pTIV1); + void onNewTextInput(WP tiv3); + void onNewTextInput(WP 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; diff --git a/src/managers/input/Swipe.cpp b/src/managers/input/Swipe.cpp index 775881cd..c0e6c4f0 100644 --- a/src/managers/input/Swipe.cpp +++ b/src/managers/input/Swipe.cpp @@ -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; } } diff --git a/src/managers/input/TextInput.cpp b/src/managers/input/TextInput.cpp index 635a8b01..5bff9fed 100644 --- a/src/managers/input/TextInput.cpp +++ b/src/managers/input/TextInput.cpp @@ -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 ti) : pV1Input(ti) { initCallbacks(); } @@ -16,17 +15,6 @@ CTextInput::CTextInput(WP 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>(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 pSurface) { } bool CTextInput::isV3() { - return !pV1Input; + return pV3Input && !pV1Input; } void CTextInput::enter(SP pSurface) { @@ -174,8 +156,7 @@ void CTextInput::enter(SP 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 CTextInput::focusedSurface() { } wl_client* CTextInput::client() { - return isV3() ? pV3Input->client() : pV1Input->client; + return isV3() ? pV3Input->client() : pV1Input->client(); } void CTextInput::commitStateToIME(SP ime) { @@ -223,13 +203,15 @@ void CTextInput::commitStateToIME(SP 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 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}; -} \ No newline at end of file +} diff --git a/src/managers/input/TextInput.hpp b/src/managers/input/TextInput.hpp index 79e08af9..0f69866e 100644 --- a/src/managers/input/TextInput.hpp +++ b/src/managers/input/TextInput.hpp @@ -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 ti); - CTextInput(STextInputV1* ti); - ~CTextInput(); + CTextInput(WP ti); bool isV3(); void enter(SP pSurface); @@ -43,12 +42,7 @@ class CTextInput { WP pFocusedSurface; int enterLocks = 0; WP pV3Input; - STextInputV1* pV1Input = nullptr; - - DYNLISTENER(textInputEnable); - DYNLISTENER(textInputDisable); - DYNLISTENER(textInputCommit); - DYNLISTENER(textInputDestroy); + WP pV1Input; struct { CHyprSignalListener enable; @@ -58,4 +52,4 @@ class CTextInput { CHyprSignalListener surfaceUnmap; CHyprSignalListener surfaceDestroy; } listeners; -}; \ No newline at end of file +}; diff --git a/src/managers/input/Touch.cpp b/src/managers/input/Touch.cpp index 736a2ae5..40af4b1e 100644 --- a/src/managers/input/Touch.cpp +++ b/src/managers/input/Touch.cpp @@ -6,13 +6,15 @@ #include "../SeatManager.hpp" void CInputManager::onTouchDown(ITouch::SDownEvent e) { + m_bLastInputTouch = true; + static auto PSWIPETOUCH = CConfigValue("gestures:workspace_swipe_touch"); static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); auto* const PGAPSOUT = (CCssGapData*)(PGAPSOUTDATA.ptr())->getData(); // TODO: WORKSPACERULE.gapsOut.value_or() auto gapsOut = *PGAPSOUT; static auto PBORDERSIZE = CConfigValue("general:border_size"); - static auto PSWIPEINVR = CConfigValue("gestures:workspace_swipe_invert"); + static auto PSWIPEINVR = CConfigValue("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. diff --git a/src/meson.build b/src/meson.build index 71854fa4..098d8298 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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'), diff --git a/src/plugins/PluginSystem.cpp b/src/plugins/PluginSystem.cpp index 46f50469..122faafd 100644 --- a/src/plugins/PluginSystem.cpp +++ b/src/plugins/PluginSystem.cpp @@ -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); diff --git a/src/protocols/AlphaModifier.cpp b/src/protocols/AlphaModifier.cpp index 04dcd0a8..38b8c800 100644 --- a/src/protocols/AlphaModifier.cpp +++ b/src/protocols/AlphaModifier.cpp @@ -87,7 +87,7 @@ void CAlphaModifierProtocol::destroyModifier(CAlphaModifier* modifier) { void CAlphaModifierProtocol::onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, SP 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; } -} \ No newline at end of file +} diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp index 41178109..33339554 100644 --- a/src/protocols/DRMSyncobj.cpp +++ b/src/protocols/DRMSyncobj.cpp @@ -47,7 +47,7 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SPpending.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; diff --git a/src/protocols/ForeignToplevelWlr.cpp b/src/protocols/ForeignToplevelWlr.cpp index 2b378386..295834ea 100644 --- a/src/protocols/ForeignToplevelWlr.cpp +++ b/src/protocols/ForeignToplevelWlr.cpp @@ -51,7 +51,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPsetWindowFullscreen(PWINDOW, true, FULLSCREEN_FULL); + g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_FULLSCREEN, true); g_pHyprRenderer->damageWindow(PWINDOW); }); @@ -64,7 +64,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPm_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(SPsetWindowFullscreen(PWINDOW, true, FULLSCREEN_MAXIMIZED); + g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, true); }); resource->setUnsetMaximized([this](CZwlrForeignToplevelHandleV1* p) { @@ -93,7 +93,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPm_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; diff --git a/src/protocols/FractionalScale.cpp b/src/protocols/FractionalScale.cpp index 6d225e99..5bf56c5a 100644 --- a/src/protocols/FractionalScale.cpp +++ b/src/protocols/FractionalScale.cpp @@ -28,7 +28,7 @@ void CFractionalScaleProtocol::onManagerResourceDestroy(wl_resource* res) { void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, SP 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 CFractionalScaleAddon::surf() { return surface.lock(); -} \ No newline at end of file +} diff --git a/src/protocols/GlobalShortcuts.cpp b/src/protocols/GlobalShortcuts.cpp index b376cae3..860004c9 100644 --- a/src/protocols/GlobalShortcuts.cpp +++ b/src/protocols/GlobalShortcuts.cpp @@ -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 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(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(makeShared(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()).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(makeShared(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 CGlobalShortcutsProtocolManager::getAllShortcuts() { +std::vector CGlobalShortcutsProtocol::getAllShortcuts() { std::vector copy; for (auto& c : m_vClients) { for (auto& sh : c->shortcuts) { @@ -143,9 +94,3 @@ std::vector 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; }); - } -} \ No newline at end of file diff --git a/src/protocols/GlobalShortcuts.hpp b/src/protocols/GlobalShortcuts.hpp index 07c484c6..14f6ee0b 100644 --- a/src/protocols/GlobalShortcuts.hpp +++ b/src/protocols/GlobalShortcuts.hpp @@ -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 struct SShortcut { - wl_resource* resource; - std::string id, description, appid; - uint32_t shortcut = 0; + SP resource; + std::string id, description, appid; + uint32_t shortcut = 0; }; -struct SShortcutClient { - wl_client* client = nullptr; - std::vector> shortcuts; -}; - -class CGlobalShortcutsProtocolManager { +class CShortcutClient { public: - CGlobalShortcutsProtocolManager(); + CShortcutClient(SP resource); + + bool good(); + + private: + SP resource; + std::vector> 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 getAllShortcuts(); private: - std::vector> m_vClients; + std::vector> m_vClients; +}; - SShortcutClient* clientFromWlClient(wl_client* client); - - wl_global* m_pGlobal = nullptr; - wl_listener m_liDisplayDestroy; -}; \ No newline at end of file +namespace PROTO { + inline UP globalShortcuts; +}; diff --git a/src/protocols/InputMethodV2.cpp b/src/protocols/InputMethodV2.cpp index def4d837..fd306f09 100644 --- a/src/protocols/InputMethodV2.cpp +++ b/src/protocols/InputMethodV2.cpp @@ -107,14 +107,14 @@ CInputMethodPopupV2::CInputMethodPopupV2(SP 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(); diff --git a/src/protocols/LayerShell.cpp b/src/protocols/LayerShell.cpp index fd66ba5e..17d3b22a 100644 --- a/src/protocols/LayerShell.cpp +++ b/src/protocols/LayerShell.cpp @@ -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 resource_, SPdestroyResource(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 resource_, SPunmap(); events.unmap.emit(); + surface->unmap(); configured = false; return; } @@ -152,6 +154,16 @@ CLayerShellResource::CLayerShellResource(SP resource_, SPsetSetExclusiveEdge([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(RESOURCE); g_pCompositor->m_vLayers.emplace_back(CLayerSurface::create(RESOURCE)); LOGM(LOG, "New wlr_layer_surface {:x}", (uintptr_t)RESOURCE.get()); } + +CLayerShellRole::CLayerShellRole(SP ls) : layerSurface(ls) { + ; +} diff --git a/src/protocols/LayerShell.hpp b/src/protocols/LayerShell.hpp index 6d29248a..801bdfd6 100644 --- a/src/protocols/LayerShell.hpp +++ b/src/protocols/LayerShell.hpp @@ -12,16 +12,26 @@ class CMonitor; class CWLSurfaceResource; +class CLayerShellResource; -class CLayerShellResource : public ISurfaceRole { +class CLayerShellRole : public ISurfaceRole { + public: + CLayerShellRole(SP ls); + + virtual eSurfaceRole role() { + return SURFACE_ROLE_LAYER_SHELL; + } + + WP layerSurface; +}; +class CLayerShellResource { public: CLayerShellResource(SP resource_, SP 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; diff --git a/src/protocols/PresentationTime.cpp b/src/protocols/PresentationTime.cpp index e9472fc3..a2fc270c 100644 --- a/src/protocols/PresentationTime.cpp +++ b/src/protocols/PresentationTime.cpp @@ -60,7 +60,7 @@ void CPresentationFeedback::sendQueued(SP 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; diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index 5d1c9332..a8afba84 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -9,238 +9,39 @@ #include -#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 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( - std::nullopt, - [this](SP 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{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{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 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(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(m_pLastMonitorBackBuffer); +bool CScreencopyFrame::copyShm() { + auto TEXTURE = makeShared(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(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 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(makeShared(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{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{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( + std::nullopt, + [this](SP 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(makeShared(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> 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()); + } } diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp index 4999773b..cbc56edb 100644 --- a/src/protocols/Screencopy.hpp +++ b/src/protocols/Screencopy.hpp @@ -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 #include @@ -20,87 +21,93 @@ enum eClientOwners { class CScreencopyClient { public: - CScreencopyClient(); + CScreencopyClient(SP resource_); ~CScreencopyClient(); - int ref = 0; - wl_resource* resource = nullptr; + bool good(); - eClientOwners clientOwner = CLIENT_SCREENCOPY; + WP self; + eClientOwners clientOwner = CLIENT_SCREENCOPY; - int frameCounter = 0; - int framesInLastHalfSecond = 0; - CTimer lastMeasure; - CTimer lastFrame; - bool sentScreencast = false; - - void onTick(); - SP 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 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 m_lFrames; - std::list m_lClients; + SP resource; - SP m_pSoftwareCursorTimer; - bool m_bTimerArmed = false; + int framesInLastHalfSecond = 0; + CTimer lastMeasure; + bool sentScreencast = false; - wl_listener m_liDisplayDestroy; + SP tickCallback; + void onTick(); - std::vector m_vFramesAwaitingWrite; + void captureOutput(uint32_t frame, int32_t overlayCursor, wl_resource* output, CBox box); - SP 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 resource, int32_t overlay_cursor, wl_resource* output, CBox box); + ~CScreencopyFrame(); + bool good(); + + SP self; + WP client; + + private: + SP resource; + + CMonitor* pMonitor = nullptr; + bool overlayCursor = false; + bool withDamage = false; + bool lockedSWCursors = false; + + WP 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> m_vFrames; + std::vector> m_vFramesAwaitingWrite; + std::vector> m_vClients; + + SP 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; -}; \ No newline at end of file +}; + +namespace PROTO { + inline UP screencopy; +}; diff --git a/src/protocols/SessionLock.cpp b/src/protocols/SessionLock.cpp index 42df5fd6..df97413c 100644 --- a/src/protocols/SessionLock.cpp +++ b/src/protocols/SessionLock.cpp @@ -24,7 +24,7 @@ CSessionLockSurface::CSessionLockSurface(SP 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; diff --git a/src/protocols/ShortcutsInhibit.cpp b/src/protocols/ShortcutsInhibit.cpp index 211a7a01..1a0433e6 100644 --- a/src/protocols/ShortcutsInhibit.cpp +++ b/src/protocols/ShortcutsInhibit.cpp @@ -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; diff --git a/src/protocols/TextInputV1.cpp b/src/protocols/TextInputV1.cpp index 7c16ef8c..78e910cb 100644 --- a/src/protocols/TextInputV1.cpp +++ b/src/protocols/TextInputV1.cpp @@ -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 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()).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 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(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(makeShared(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(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) { - ; -} \ No newline at end of file diff --git a/src/protocols/TextInputV1.hpp b/src/protocols/TextInputV1.hpp index 0a1203f0..c85a1f31 100644 --- a/src/protocols/TextInputV1.hpp +++ b/src/protocols/TextInputV1.hpp @@ -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 class CTextInput; -struct STextInputV1 { - wl_client* client = nullptr; - wl_resource* resourceCaller = nullptr; +class CTextInputV1 { + public: + CTextInputV1(SP resource); + ~CTextInputV1(); - wl_resource* resourceImpl = nullptr; + void enter(SP 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 resource; + WP 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 + } events; private: - wl_global* m_pGlobal = nullptr; - wl_listener m_liDisplayDestroy; + std::vector> m_vManagers; + std::vector> m_vClients; - std::vector> m_pClients; -}; \ No newline at end of file + friend class CTextInputV1; +}; + +namespace PROTO { + inline UP textInputV1; +}; diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index 0e78e5f7..fb3fde2b 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -8,356 +8,240 @@ #include -#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 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(makeShared(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{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{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 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 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(makeShared(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> 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(); } } diff --git a/src/protocols/ToplevelExport.hpp b/src/protocols/ToplevelExport.hpp index c3620d41..638b69f0 100644 --- a/src/protocols/ToplevelExport.hpp +++ b/src/protocols/ToplevelExport.hpp @@ -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 @@ -10,32 +11,91 @@ class CMonitor; class CWindow; -class CToplevelExportProtocolManager { +class CToplevelExportClient { public: - CToplevelExportProtocolManager(); + CToplevelExportClient(SP resource_); + + bool good(); + + WP self; + eClientOwners clientOwner = CLIENT_TOPLEVEL_EXPORT; + + CTimer lastFrame; + int frameCounter = 0; + + private: + SP resource; + + int framesInLastHalfSecond = 0; + CTimer lastMeasure; + bool sentScreencast = false; + + SP tickCallback; + void onTick(); + + void captureToplevel(CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, PHLWINDOW handle); + + friend class CToplevelExportProtocol; +}; + +class CToplevelExportFrame { + public: + CToplevelExportFrame(SP resource_, int32_t overlayCursor, PHLWINDOW pWindow); + ~CToplevelExportFrame(); + + bool good(); + + SP self; + WP client; + + private: + SP resource; + + PHLWINDOW pWindow; + bool overlayCursor = false; + bool ignoreDamage = false; + bool lockedSWCursors = false; + + WP 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 m_lFrames; - std::list m_lClients; + std::vector> m_vClients; + std::vector> m_vFrames; + std::vector> 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 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; -}; \ No newline at end of file +namespace PROTO { + inline UP toplevelExport; +}; diff --git a/src/protocols/Viewporter.cpp b/src/protocols/Viewporter.cpp index 03c5775e..78f3039f 100644 --- a/src/protocols/Viewporter.cpp +++ b/src/protocols/Viewporter.cpp @@ -54,13 +54,13 @@ CViewportResource::CViewportResource(SP resource_, SPevents.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; diff --git a/src/protocols/VirtualPointer.cpp b/src/protocols/VirtualPointer.cpp index bdeec32d..8626241a 100644 --- a/src/protocols/VirtualPointer.cpp +++ b/src/protocols/VirtualPointer.cpp @@ -1,8 +1,9 @@ #include "VirtualPointer.hpp" +#include "core/Output.hpp" #define LOGM PROTO::virtualPointer->protoLog -CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP resource_) : resource(resource_) { +CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP resource_, WP 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 output) { - const auto RESOURCE = m_vPointers.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id))); + const auto RESOURCE = m_vPointers.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id), output)); if (!RESOURCE->good()) { pMgr->noMemory(); diff --git a/src/protocols/VirtualPointer.hpp b/src/protocols/VirtualPointer.hpp index ee5ee7e2..7ee450dc 100644 --- a/src/protocols/VirtualPointer.hpp +++ b/src/protocols/VirtualPointer.hpp @@ -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 resource_); + CVirtualPointerV1Resource(SP resource_, WP 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 boundOutput; private: SP 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 output); // std::vector> m_vManagers; diff --git a/src/protocols/WaylandProtocol.cpp b/src/protocols/WaylandProtocol.cpp index 23a6721c..954f160d 100644 --- a/src/protocols/WaylandProtocol.cpp +++ b/src/protocols/WaylandProtocol.cpp @@ -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"); } diff --git a/src/protocols/WaylandProtocol.hpp b/src/protocols/WaylandProtocol.hpp index b443e253..4d4e7925 100644 --- a/src/protocols/WaylandProtocol.hpp +++ b/src/protocols/WaylandProtocol.hpp @@ -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; -}; \ No newline at end of file +}; diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index 8276ed55..4aa5d373 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -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 resource_, SP owner_, SP surface_, SP positioner) : surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) { if (!good()) @@ -333,12 +347,12 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SPcurrent = 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 resource_, SPcurrent.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 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 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 resource_) : resource(resource_) { @@ -681,8 +662,9 @@ CXDGWMBase::CXDGWMBase(SP resource_) : resource(resource_) { return; } - RESOURCE->self = RESOURCE; - SURF->role = RESOURCE; + RESOURCE->self = RESOURCE; + RESOURCE->surface = SURF; + SURF->role = makeShared(RESOURCE); surfaces.emplace_back(RESOURCE); @@ -779,3 +761,7 @@ void CXDGShellProtocol::onPopupDestroy(WP popup) { if (popup->surface) grab->remove(popup->surface->surface.lock()); } + +CXDGSurfaceRole::CXDGSurfaceRole(SP xdg) : xdgSurface(xdg) { + ; +} diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp index da551718..81d10613 100644 --- a/src/protocols/XDGShell.hpp +++ b/src/protocols/XDGShell.hpp @@ -4,10 +4,10 @@ #include #include #include +#include #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 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 xdg); + + virtual eSurfaceRole role() { + return SURFACE_ROLE_XDG_SHELL; + } + + WP xdgSurface; +}; + +class CXDGSurfaceResource { public: CXDGSurfaceResource(SP resource_, SP owner_, SP surface_); ~CXDGSurfaceResource(); static SP fromResource(wl_resource*); - virtual eSurfaceRole role(); - bool good(); WP owner; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 5cd6005a..4a6fa40f 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -1,5 +1,6 @@ #include "Compositor.hpp" #include "Output.hpp" +#include "Seat.hpp" #include "../types/WLBuffer.hpp" #include #include @@ -9,6 +10,7 @@ #include "../PresentationTime.hpp" #include "../DRMSyncobj.hpp" #include "../../render/Renderer.hpp" +#include #define LOGM PROTO::compositor->protoLog @@ -19,8 +21,6 @@ class CDefaultSurfaceRole : public ISurfaceRole { } }; -SP defaultRole = makeShared(); - CWLCallbackResource::CWLCallbackResource(SP resource_) : resource(resource_) { ; } @@ -63,7 +63,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(reso resource->setData(this); - role = defaultRole; + role = makeShared(); resource->setDestroy([this](CWlSurface* r) { destroy(); }); resource->setOnDestroy([this](CWlSurface* r) { destroy(); }); @@ -75,41 +75,42 @@ CWLSurfaceResource::CWLSurfaceResource(SP 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(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::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(); } void CWLSurfaceResource::bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data) { @@ -251,6 +263,8 @@ void CWLSurfaceResource::bfHelper(std::vector> 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> 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> 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 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{}; +} + +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(self.lock()); FEEDBACK->attachMonitor(pMonitor); - FEEDBACK->discarded(); + FEEDBACK->presented(); PROTO::presentation->queueData(FEEDBACK); if (!pMonitor || !pMonitor->outTimeline || !syncobj || !needsExplicitSync) diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index 5e6413c8..460ec755 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -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 buffer; - SP 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 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 texture; + Vector2D offset; + Vector2D size, bufferSize; struct { bool hasDestination = false; bool hasSource = false; @@ -116,7 +116,7 @@ class CWLSurfaceResource { std::vector> enteredOutputs; bool mapped = false; std::vector> subsurfaces; - WP role; + SP role; WP viewportResource; WP syncobj; // may not be present @@ -134,14 +134,21 @@ class CWLSurfaceResource { SP 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 lastBuffer; - int stateLocks = 0; + int stateLocks = 0; - void destroy(); - void commitPendingState(); - void bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data); + void destroy(); + void releaseBuffers(bool onlyCurrent = true); + void dropPendingBuffer(); + void dropCurrentBuffer(); + void commitPendingState(); + void bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data); + void updateCursorShm(); + + friend class CWLPointerResource; }; class CWLCompositorResource { diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index 9634d569..fe3905d0 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -469,12 +469,12 @@ void CWLDataDeviceProtocol::initiateDrag(WP 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); diff --git a/src/protocols/core/Output.cpp b/src/protocols/core/Output.cpp index 4ba23cbe..878d1484 100644 --- a/src/protocols/core/Output.cpp +++ b/src/protocols/core/Output.cpp @@ -49,7 +49,7 @@ SP 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) { diff --git a/src/protocols/core/Output.hpp b/src/protocols/core/Output.hpp index 46e4057b..49c32ec1 100644 --- a/src/protocols/core/Output.hpp +++ b/src/protocols/core/Output.hpp @@ -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 monitor; - + WP owner; WP self; private: @@ -40,6 +41,7 @@ class CWLOutputProtocol : public IWaylandProtocol { void sendDone(); WP monitor; + WP 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> outputs; + inline std::unordered_map> outputs; }; diff --git a/src/protocols/core/Seat.cpp b/src/protocols/core/Seat.cpp index 21a47575..bb6a9d4d 100644 --- a/src/protocols/core/Seat.cpp +++ b/src/protocols/core/Seat.cpp @@ -27,6 +27,9 @@ void CWLTouchResource::sendDown(SP surface, uint32_t timeMs, if (!owner) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + return; + ASSERT(surface->client() == owner->client()); currentSurface = surface; @@ -41,6 +44,9 @@ void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) { if (!owner) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + return; + resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id); fingers--; if (fingers <= 0) { @@ -54,6 +60,9 @@ void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& l if (!owner) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + return; + resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } @@ -61,6 +70,9 @@ void CWLTouchResource::sendFrame() { if (!owner) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + return; + resource->sendFrame(); } @@ -68,6 +80,9 @@ void CWLTouchResource::sendCancel() { if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + return; + resource->sendCancel(); } @@ -75,6 +90,9 @@ void CWLTouchResource::sendShape(int32_t id, const Vector2D& shape) { if (!owner || !currentSurface || resource->version() < 6) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + return; + resource->sendShape(id, wl_fixed_from_double(shape.x), wl_fixed_from_double(shape.y)); } @@ -82,6 +100,9 @@ void CWLTouchResource::sendOrientation(int32_t id, double angle) { if (!owner || !currentSurface || resource->version() < 6) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + return; + resource->sendOrientation(id, wl_fixed_from_double(angle)); } @@ -98,7 +119,19 @@ CWLPointerResource::CWLPointerResource(SP resource_, SPonSetCursor(owner.lock(), serial, surf ? CWLSurfaceResource::fromResource(surf) : nullptr, {hotX, hotY}); + auto surfResource = surf ? CWLSurfaceResource::fromResource(surf) : nullptr; + + if (surfResource && surfResource->role->role() != SURFACE_ROLE_CURSOR && surfResource->role->role() != SURFACE_ROLE_UNASSIGNED) { + r->error(-1, "Cursor surface already has a different role"); + return; + } + + if (surfResource) { + surfResource->role = makeShared(); + surfResource->updateCursorShm(); + } + + g_pSeatManager->onSetCursor(owner.lock(), serial, surfResource, {hotX, hotY}); }); if (g_pSeatManager->state.pointerFocus && g_pSeatManager->state.pointerFocus->client() == resource->client()) @@ -113,6 +146,9 @@ void CWLPointerResource::sendEnter(SP surface, const Vector2 if (!owner || currentSurface == surface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + if (currentSurface) { LOGM(WARN, "requested CWLPointerResource::sendEnter without sendLeave first."); sendLeave(); @@ -130,6 +166,9 @@ void CWLPointerResource::sendLeave() { if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + // release all buttons unless we have a dnd going on in which case // the events shall be lost. if (!PROTO::data->dndActive()) { @@ -151,6 +190,9 @@ void CWLPointerResource::sendMotion(uint32_t timeMs, const Vector2D& local) { if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } @@ -158,6 +200,9 @@ void CWLPointerResource::sendButton(uint32_t timeMs, uint32_t button, wl_pointer if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + if (state == WL_POINTER_BUTTON_STATE_RELEASED && std::find(pressedButtons.begin(), pressedButtons.end(), button) == pressedButtons.end()) { LOGM(ERR, "sendButton release on a non-pressed button"); return; @@ -178,6 +223,9 @@ void CWLPointerResource::sendAxis(uint32_t timeMs, wl_pointer_axis axis, double if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendAxis(timeMs, axis, wl_fixed_from_double(value)); } @@ -185,6 +233,9 @@ void CWLPointerResource::sendFrame() { if (!owner || resource->version() < 5) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendFrame(); } @@ -192,6 +243,9 @@ void CWLPointerResource::sendAxisSource(wl_pointer_axis_source source) { if (!owner || !currentSurface || resource->version() < 5) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendAxisSource(source); } @@ -199,6 +253,9 @@ void CWLPointerResource::sendAxisStop(uint32_t timeMs, wl_pointer_axis axis) { if (!owner || !currentSurface || resource->version() < 5) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendAxisStop(timeMs, axis); } @@ -206,6 +263,9 @@ void CWLPointerResource::sendAxisDiscrete(wl_pointer_axis axis, int32_t discrete if (!owner || !currentSurface || resource->version() < 5) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendAxisDiscrete(axis, discrete); } @@ -213,6 +273,9 @@ void CWLPointerResource::sendAxisValue120(wl_pointer_axis axis, int32_t value120 if (!owner || !currentSurface || resource->version() < 8) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendAxisValue120(axis, value120); } @@ -220,6 +283,9 @@ void CWLPointerResource::sendAxisRelativeDirection(wl_pointer_axis axis, wl_poin if (!owner || !currentSurface || resource->version() < 9) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + return; + resource->sendAxisRelativeDirection(axis, direction); } @@ -250,6 +316,9 @@ void CWLKeyboardResource::sendKeymap(SP keyboard) { if (!keyboard) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + return; + wl_keyboard_keymap_format format = keyboard ? WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 : WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP; int fd; uint32_t size; @@ -275,6 +344,9 @@ void CWLKeyboardResource::sendEnter(SP surface) { if (!owner || currentSurface == surface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + return; + if (currentSurface) { LOGM(WARN, "requested CWLKeyboardResource::sendEnter without sendLeave first."); sendLeave(); @@ -297,6 +369,9 @@ void CWLKeyboardResource::sendLeave() { if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + return; + resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get()); currentSurface.reset(); listeners.destroySurface.reset(); @@ -306,6 +381,9 @@ void CWLKeyboardResource::sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + return; + resource->sendKey(g_pSeatManager->nextSerial(owner.lock()), timeMs, key, state); } @@ -313,6 +391,9 @@ void CWLKeyboardResource::sendMods(uint32_t depressed, uint32_t latched, uint32_ if (!owner || !currentSurface) return; + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + return; + resource->sendModifiers(g_pSeatManager->nextSerial(owner.lock()), depressed, latched, locked, group); } @@ -477,3 +558,10 @@ SP CWLSeatProtocol::seatResourceForClient(wl_client* client) { return nullptr; } + +std::vector& CCursorSurfaceRole::cursorPixelData(SP surface) { + RASSERT(surface->role->role() == SURFACE_ROLE_CURSOR, "cursorPixelData called on a non-cursor surface"); + + auto role = (CCursorSurfaceRole*)surface->role.get(); + return role->cursorShmPixelData; +} diff --git a/src/protocols/core/Seat.hpp b/src/protocols/core/Seat.hpp index 09b36056..755a9c2f 100644 --- a/src/protocols/core/Seat.hpp +++ b/src/protocols/core/Seat.hpp @@ -16,6 +16,7 @@ #include "wayland.hpp" #include "../../helpers/signal/Signal.hpp" #include "../../helpers/math/Math.hpp" +#include "../types/SurfaceRole.hpp" constexpr const char* HL_SEAT_NAME = "Hyprland"; @@ -27,6 +28,20 @@ class CWLKeyboardResource; class CWLTouchResource; class CWLSeatResource; +class CCursorSurfaceRole : public ISurfaceRole { + public: + virtual eSurfaceRole role() { + return SURFACE_ROLE_CURSOR; + } + + // gets the current pixel data from a shm surface + // will assert if the surface is not a cursor + static std::vector& cursorPixelData(SP surface); + + private: + std::vector cursorShmPixelData; +}; + class CWLTouchResource { public: CWLTouchResource(SP resource_, SP owner_); diff --git a/src/protocols/core/Subcompositor.cpp b/src/protocols/core/Subcompositor.cpp index cbc4063a..2a7c06dc 100644 --- a/src/protocols/core/Subcompositor.cpp +++ b/src/protocols/core/Subcompositor.cpp @@ -80,12 +80,14 @@ CWLSubsurfaceResource::CWLSubsurfaceResource(SP resource_, SPevents.commit.registerListener([this](std::any d) { - if (surface->current.buffer && !surface->mapped) { + if (surface->current.texture && !surface->mapped) { surface->map(); + surface->events.map.emit(); return; } - if (!surface->current.buffer && surface->mapped) { + if (!surface->current.texture && surface->mapped) { + surface->events.unmap.emit(); surface->unmap(); return; } @@ -117,7 +119,7 @@ Vector2D CWLSubsurfaceResource::posRelativeToParent() { while (surf->role->role() == SURFACE_ROLE_SUBSURFACE && std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { surfacesVisited.emplace_back(surf); - auto subsurface = (CWLSubsurfaceResource*)parent->role.get(); + auto subsurface = ((CSubsurfaceRole*)parent->role.get())->subsurface.lock(); pos += subsurface->position; surf = subsurface->parent.lock(); } @@ -128,10 +130,6 @@ bool CWLSubsurfaceResource::good() { return resource->resource(); } -eSurfaceRole CWLSubsurfaceResource::role() { - return SURFACE_ROLE_SUBSURFACE; -} - SP CWLSubsurfaceResource::t1Parent() { SP surf = parent.lock(); std::vector> surfacesVisited; @@ -139,7 +137,7 @@ SP CWLSubsurfaceResource::t1Parent() { while (surf->role->role() == SURFACE_ROLE_SUBSURFACE && std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { surfacesVisited.emplace_back(surf); - auto subsurface = (CWLSubsurfaceResource*)parent->role.get(); + auto subsurface = ((CSubsurfaceRole*)parent->role.get())->subsurface.lock(); surf = subsurface->parent.lock(); } return surf; @@ -169,7 +167,7 @@ CWLSubcompositorResource::CWLSubcompositorResource(SP resource SP t1Parent = nullptr; if (PARENT->role->role() == SURFACE_ROLE_SUBSURFACE) { - auto subsurface = (CWLSubsurfaceResource*)PARENT->role.get(); + auto subsurface = ((CSubsurfaceRole*)PARENT->role.get())->subsurface.lock(); t1Parent = subsurface->t1Parent(); } else t1Parent = PARENT; @@ -189,7 +187,7 @@ CWLSubcompositorResource::CWLSubcompositorResource(SP resource } RESOURCE->self = RESOURCE; - SURF->role = RESOURCE; + SURF->role = makeShared(RESOURCE); PARENT->subsurfaces.emplace_back(RESOURCE); LOGM(LOG, "New wl_subsurface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); @@ -223,3 +221,7 @@ void CWLSubcompositorProtocol::destroyResource(CWLSubcompositorResource* resourc void CWLSubcompositorProtocol::destroyResource(CWLSubsurfaceResource* resource) { std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); } + +CSubsurfaceRole::CSubsurfaceRole(SP sub) : subsurface(sub) { + ; +} diff --git a/src/protocols/core/Subcompositor.hpp b/src/protocols/core/Subcompositor.hpp index 824f0ffc..2e6b10bb 100644 --- a/src/protocols/core/Subcompositor.hpp +++ b/src/protocols/core/Subcompositor.hpp @@ -16,15 +16,26 @@ #include "../types/SurfaceRole.hpp" class CWLSurfaceResource; +class CWLSubsurfaceResource; -class CWLSubsurfaceResource : public ISurfaceRole { +class CSubsurfaceRole : public ISurfaceRole { + public: + CSubsurfaceRole(SP sub); + + virtual eSurfaceRole role() { + return SURFACE_ROLE_SUBSURFACE; + } + + WP subsurface; +}; + +class CWLSubsurfaceResource { public: CWLSubsurfaceResource(SP resource_, SP surface_, SP parent_); ~CWLSubsurfaceResource(); Vector2D posRelativeToParent(); bool good(); - virtual eSurfaceRole role(); SP t1Parent(); bool sync = false; diff --git a/src/protocols/types/Buffer.cpp b/src/protocols/types/Buffer.cpp index 0217f7e2..40f2eaf8 100644 --- a/src/protocols/types/Buffer.cpp +++ b/src/protocols/types/Buffer.cpp @@ -1,5 +1,10 @@ #include "Buffer.hpp" +IHLBuffer::~IHLBuffer() { + if (locked() && resource) + sendRelease(); +} + void IHLBuffer::sendRelease() { resource->sendRelease(); } @@ -8,3 +13,54 @@ void IHLBuffer::sendReleaseWithSurface(SP surf) { if (resource && resource->good()) resource->sendReleaseWithSurface(surf); } + +void IHLBuffer::lock() { + nLocks++; +} + +void IHLBuffer::unlock() { + nLocks--; + + ASSERT(nLocks >= 0); + + if (nLocks == 0) + sendRelease(); +} + +void IHLBuffer::unlockWithSurface(SP surf) { + nLocks--; + + ASSERT(nLocks >= 0); + + if (nLocks == 0) + sendReleaseWithSurface(surf); +} + +bool IHLBuffer::locked() { + return nLocks > 0; +} + +void IHLBuffer::unlockOnBufferRelease(WP surf) { + unlockSurface = surf; + hlEvents.backendRelease = events.backendRelease.registerListener([this](std::any data) { + if (unlockSurface.expired()) + unlock(); + else + unlockWithSurface(unlockSurface.lock()); + hlEvents.backendRelease.reset(); + }); +} + +CHLBufferReference::CHLBufferReference(SP buffer_, SP surface_) : buffer(buffer_), surface(surface_) { + buffer->lock(); +} + +CHLBufferReference::~CHLBufferReference() { + if (buffer.expired()) + return; + + if (surface) + buffer->unlockWithSurface(surface.lock()); + else + buffer->unlock(); +} diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index ba8278f3..d2157181 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -8,9 +8,7 @@ class IHLBuffer : public Aquamarine::IBuffer { public: - virtual ~IHLBuffer() { - ; - } + virtual ~IHLBuffer(); virtual Aquamarine::eBufferCapability caps() = 0; virtual Aquamarine::eBufferType type() = 0; virtual void update(const CRegion& damage) = 0; @@ -18,6 +16,12 @@ class IHLBuffer : public Aquamarine::IBuffer { virtual bool good() = 0; virtual void sendRelease(); virtual void sendReleaseWithSurface(SP); + virtual void lock(); + virtual void unlock(); + virtual void unlockWithSurface(SP surf); + virtual bool locked(); + + void unlockOnBufferRelease(WP surf /* optional */); SP texture; bool opaque = false; @@ -26,4 +30,22 @@ class IHLBuffer : public Aquamarine::IBuffer { struct { CHyprSignalListener backendRelease; } hlEvents; + + private: + int nLocks = 0; + + WP unlockSurface; +}; + +// for ref-counting. Releases in ~dtor +// surface optional +class CHLBufferReference { + public: + CHLBufferReference(SP buffer, SP surface); + ~CHLBufferReference(); + + WP buffer; + + private: + WP surface; }; diff --git a/src/protocols/types/SurfaceRole.hpp b/src/protocols/types/SurfaceRole.hpp index faaf70ee..64586f01 100644 --- a/src/protocols/types/SurfaceRole.hpp +++ b/src/protocols/types/SurfaceRole.hpp @@ -6,6 +6,7 @@ enum eSurfaceRole { SURFACE_ROLE_LAYER_SHELL, SURFACE_ROLE_EASTER_EGG, SURFACE_ROLE_SUBSURFACE, + SURFACE_ROLE_CURSOR, }; class ISurfaceRole { diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index b2b046ce..ea388df0 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -101,7 +101,7 @@ void CHyprOpenGLImpl::initEGL(bool gbm) { attrs.push_back(EGL_NONE); - m_pEglDisplay = m_sProc.eglGetPlatformDisplayEXT(gbm ? EGL_PLATFORM_GBM_KHR : EGL_PLATFORM_DEVICE_EXT, gbm ? m_pGbmDevice : nullptr, attrs.data()); + m_pEglDisplay = m_sProc.eglGetPlatformDisplayEXT(gbm ? EGL_PLATFORM_GBM_KHR : EGL_PLATFORM_DEVICE_EXT, gbm ? m_pGbmDevice : m_pEglDevice, attrs.data()); if (m_pEglDisplay == EGL_NO_DISPLAY) RASSERT(false, "EGL: failed to create a platform display"); @@ -130,18 +130,39 @@ void CHyprOpenGLImpl::initEGL(bool gbm) { attrs.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT); } + auto attrsNoVer = attrs; + +#ifndef GLES2 attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); attrs.push_back(3); attrs.push_back(EGL_CONTEXT_MINOR_VERSION); attrs.push_back(2); - attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG); - attrs.push_back(ISDEBUG ? EGL_TRUE : EGL_FALSE); +#else + attrs.push_back(EGL_CONTEXT_CLIENT_VERSION); + attrs.push_back(2); +#endif attrs.push_back(EGL_NONE); m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); - if (m_pEglContext == EGL_NO_CONTEXT) - RASSERT(false, "EGL: failed to create a context"); + if (m_pEglContext == EGL_NO_CONTEXT) { +#ifdef GLES2 + RASSERT(false, "EGL: failed to create a context with GLES2.0"); +#endif + Debug::log(WARN, "EGL: Failed to create a context with GLES3.2, retrying 3.0"); + + attrs = attrsNoVer; + attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); + attrs.push_back(3); + attrs.push_back(EGL_CONTEXT_MINOR_VERSION); + attrs.push_back(0); + attrs.push_back(EGL_NONE); + + m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); + + if (m_pEglContext == EGL_NO_CONTEXT) + RASSERT(false, "EGL: failed to create a context with either GLES3.2 or 3.0"); + } if (m_sExts.IMG_context_priority) { EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; @@ -155,6 +176,60 @@ void CHyprOpenGLImpl::initEGL(bool gbm) { eglMakeCurrent(m_pEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_pEglContext); } +static bool drmDeviceHasName(const drmDevice* device, const std::string& name) { + for (size_t i = 0; i < DRM_NODE_MAX; i++) { + if (!(device->available_nodes & (1 << i))) + continue; + + if (device->nodes[i] == name) + return true; + } + return false; +} + +EGLDeviceEXT CHyprOpenGLImpl::eglDeviceFromDRMFD(int drmFD) { + EGLint nDevices = 0; + if (!m_sProc.eglQueryDevicesEXT(0, nullptr, &nDevices)) { + Debug::log(ERR, "eglDeviceFromDRMFD: eglQueryDevicesEXT failed"); + return EGL_NO_DEVICE_EXT; + } + + if (nDevices <= 0) { + Debug::log(ERR, "eglDeviceFromDRMFD: no devices"); + return EGL_NO_DEVICE_EXT; + } + + std::vector devices; + devices.resize(nDevices); + + if (!m_sProc.eglQueryDevicesEXT(nDevices, devices.data(), &nDevices)) { + Debug::log(ERR, "eglDeviceFromDRMFD: eglQueryDevicesEXT failed (2)"); + return EGL_NO_DEVICE_EXT; + } + + drmDevice* drmDev = nullptr; + if (int ret = drmGetDevice(drmFD, &drmDev); ret < 0) { + Debug::log(ERR, "eglDeviceFromDRMFD: drmGetDevice failed"); + return EGL_NO_DEVICE_EXT; + } + + for (auto& d : devices) { + auto devName = m_sProc.eglQueryDeviceStringEXT(d, EGL_DRM_DEVICE_FILE_EXT); + if (!devName) + continue; + + if (drmDeviceHasName(drmDev, devName)) { + Debug::log(LOG, "eglDeviceFromDRMFD: Using device {}", devName); + drmFreeDevice(&drmDev); + return d; + } + } + + drmFreeDevice(&drmDev); + Debug::log(LOG, "eglDeviceFromDRMFD: No drm devices found"); + return EGL_NO_DEVICE_EXT; +} + CHyprOpenGLImpl::CHyprOpenGLImpl() { const std::string EGLEXTENSIONS = (const char*)eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); @@ -199,22 +274,33 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { RASSERT(eglBindAPI(EGL_OPENGL_ES_API) != EGL_FALSE, "Couldn't bind to EGL's opengl ES API. This means your gpu driver f'd up. This is not a hyprland issue."); - // if (m_sProc.eglQueryDevicesEXT) { - // // TODO: - // } + bool success = false; + if (EGLEXTENSIONS.contains("EXT_platform_device") || !m_sProc.eglQueryDevicesEXT || !m_sProc.eglQueryDeviceStringEXT) { + m_pEglDevice = eglDeviceFromDRMFD(m_iDRMFD); - if (EGLEXTENSIONS.contains("KHR_platform_gbm")) { - m_iGBMFD = openRenderNode(m_iDRMFD); - if (m_iGBMFD < 0) - RASSERT(false, "Couldn't open a gbm fd"); + if (m_pEglDevice != EGL_NO_DEVICE_EXT) { + success = true; + initEGL(false); + } + } - m_pGbmDevice = gbm_create_device(m_iGBMFD); - if (!m_pGbmDevice) - RASSERT(false, "Couldn't open a gbm device"); + if (!success) { + Debug::log(WARN, "EGL: EXT_platform_device or EGL_EXT_device_query not supported, using gbm"); + if (EGLEXTENSIONS.contains("KHR_platform_gbm")) { + success = true; + m_iGBMFD = openRenderNode(m_iDRMFD); + if (m_iGBMFD < 0) + RASSERT(false, "Couldn't open a gbm fd"); - initEGL(true); - } else - RASSERT(false, "EGL does not support KHR_platform_gbm, this is an issue with your gpu driver."); + m_pGbmDevice = gbm_create_device(m_iGBMFD); + if (!m_pGbmDevice) + RASSERT(false, "Couldn't open a gbm device"); + + initEGL(true); + } + } + + RASSERT(success, "EGL does not support KHR_platform_gbm or EXT_platform_device, this is an issue with your gpu driver."); auto* const EXTENSIONS = (const char*)glGetString(GL_EXTENSIONS); RASSERT(EXTENSIONS, "Couldn't retrieve openGL extensions!"); @@ -259,6 +345,22 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { m_tGlobalTimer.reset(); } +CHyprOpenGLImpl::~CHyprOpenGLImpl() { + if (m_pEglDisplay && m_pEglContext != EGL_NO_CONTEXT) + eglDestroyContext(m_pEglDisplay, m_pEglContext); + + if (m_pEglDisplay) + eglTerminate(m_pEglDisplay); + + eglReleaseThread(); + + if (m_pGbmDevice) + gbm_device_destroy(m_pGbmDevice); + + if (m_iGBMFD >= 0) + close(m_iGBMFD); +} + std::optional> CHyprOpenGLImpl::getModsForFormat(EGLint format) { // TODO: return std::expected when clang supports it @@ -1277,9 +1379,13 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP tex, CBox* pB static auto PDIMINACTIVE = CConfigValue("decoration:dim_inactive"); static auto PDT = CConfigValue("debug:damage_tracking"); - // get transform - const auto TRANSFORM = wlTransformToHyprutils(invertTransform(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform)); - float matrix[9]; + // get the needed transform for this texture + const bool TRANSFORMS_MATCH = wlTransformToHyprutils(m_RenderData.pMonitor->transform) == tex->m_eTransform; // FIXME: combine them properly!!! + eTransform TRANSFORM = HYPRUTILS_TRANSFORM_NORMAL; + if (m_bEndFrame || TRANSFORMS_MATCH) + TRANSFORM = wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)); + + float matrix[9]; projectBox(matrix, newBox, TRANSFORM, newBox.rot, m_RenderData.monitorProjection.data()); float glMatrix[9]; @@ -2499,7 +2605,7 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const textW /= PANGO_SCALE; textH /= PANGO_SCALE; - cairo_move_to(CAIRO, (size.x - textW) / 2.0, size.y - textH * 2 + offsetY); + cairo_move_to(CAIRO, (size.x - textW) / 2.0, size.y - textH - offsetY); pango_cairo_show_layout(CAIRO, layoutText); pango_font_description_free(pangoFD); @@ -2508,16 +2614,50 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const cairo_surface_flush(CAIROSURFACE); } +void CHyprOpenGLImpl::createBackgroundTexture(const std::string& texPath) { + const auto CAIROSURFACE = cairo_image_surface_create_from_png(texPath.c_str()); + const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROSURFACE); + + m_pBackgroundTexture = makeShared(); + + m_pBackgroundTexture->allocate(); + m_pBackgroundTexture->m_vSize = {cairo_image_surface_get_width(CAIROSURFACE), cairo_image_surface_get_height(CAIROSURFACE)}; + + const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? +#ifdef GLES2 + GL_RGB32F_EXT : +#else + GL_RGB32F : +#endif + GL_RGBA; + const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; + const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; + + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + glBindTexture(GL_TEXTURE_2D, m_pBackgroundTexture->m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#ifndef GLES2 + if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + } +#endif + glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, m_pBackgroundTexture->m_vSize.x, m_pBackgroundTexture->m_vSize.y, 0, glFormat, glType, DATA); + + cairo_surface_destroy(CAIROSURFACE); +} + void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!"); - static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); - static auto PNOSPLASH = CConfigValue("misc:disable_splash_rendering"); - static auto PFORCEWALLPAPER = CConfigValue("misc:force_default_wallpaper"); + Debug::log(LOG, "Creating a texture for BGTex"); - const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); + static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); + static auto PNOSPLASH = CConfigValue("misc:disable_splash_rendering"); + static auto PFORCEWALLPAPER = CConfigValue("misc:force_default_wallpaper"); - static std::string texPath = ""; + const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); if (*PRENDERTEX) return; @@ -2527,12 +2667,14 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { PFB->release(); PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat); - Debug::log(LOG, "Allocated texture for BGTex"); - // TODO: use relative paths to the installation - // or configure the paths at build time - if (texPath.empty()) { - texPath = "/usr/share/hyprland/wall"; + if (!m_pBackgroundTexture) { + std::string texPath = ""; +#ifndef DATAROOTDIR + texPath = "/usr/share/hypr/wall"; +#else + texPath = std::format("{}{}", DATAROOTDIR, "/hypr/wall"); +#endif // get the adequate tex if (FORCEWALLPAPER == -1) { @@ -2553,84 +2695,77 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { if (!std::filesystem::exists(texPath)) return; // the texture will be empty, oh well. We'll clear with a solid color anyways. } + + createBackgroundTexture(texPath); } // create a new one with cairo SP tex = makeShared(); - const auto CAIROISURFACE = cairo_image_surface_create_from_png(texPath.c_str()); - const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROISURFACE); - tex->allocate(); - const Vector2D IMAGESIZE = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)}; - // calc the target box - const double MONRATIO = m_RenderData.pMonitor->vecTransformedSize.x / m_RenderData.pMonitor->vecTransformedSize.y; - const double WPRATIO = IMAGESIZE.x / IMAGESIZE.y; - - Vector2D origin; - double scale; - - if (MONRATIO > WPRATIO) { - scale = m_RenderData.pMonitor->vecTransformedSize.x / IMAGESIZE.x; - - origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - IMAGESIZE.y * scale) / 2.0; - } else { - scale = m_RenderData.pMonitor->vecTransformedSize.y / IMAGESIZE.y; - - origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - IMAGESIZE.x * scale) / 2.0; - } - - const Vector2D scaledSize = IMAGESIZE * scale; - - const auto CAIROSURFACE = cairo_image_surface_create(CAIROFORMAT, scaledSize.x, scaledSize.y); - const auto CAIRO = cairo_create(CAIROSURFACE); + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); + const auto CAIRO = cairo_create(CAIROSURFACE); cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_GOOD); - cairo_scale(CAIRO, scale, scale); - cairo_rectangle(CAIRO, 0, 0, 100, 100); - cairo_set_source_surface(CAIRO, CAIROISURFACE, 0, 0); + cairo_save(CAIRO); + cairo_set_source_rgba(CAIRO, 0, 0, 0, 0); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_SOURCE); cairo_paint(CAIRO); + cairo_restore(CAIRO); if (!*PNOSPLASH) - renderSplash(CAIRO, CAIROSURFACE, origin.y * WPRATIO / MONRATIO * scale, IMAGESIZE); + renderSplash(CAIRO, CAIROSURFACE, 0.02 * pMonitor->vecPixelSize.y, pMonitor->vecPixelSize); cairo_surface_flush(CAIROSURFACE); - CBox box = {origin.x, origin.y, IMAGESIZE.x * scale, IMAGESIZE.y * scale}; - tex->m_vSize = IMAGESIZE * scale; + tex->m_vSize = pMonitor->vecPixelSize; // copy the data to an OpenGL texture we have - const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? -#ifdef GLES2 - GL_RGB32F_EXT : -#else - GL_RGB32F : -#endif - GL_RGBA; - const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; - const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; + const GLint glFormat = GL_RGBA; + const GLint glType = GL_UNSIGNED_BYTE; const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); #ifndef GLES2 - if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); - } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); #endif - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA); + glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA); cairo_surface_destroy(CAIROSURFACE); - cairo_surface_destroy(CAIROISURFACE); cairo_destroy(CAIRO); // render the texture to our fb PFB->bind(); CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; - renderTextureInternalWithDamage(tex, &box, 1.0, &fakeDamage); + + blend(true); + clear(CColor{0, 0, 0, 1}); + + // first render the background + if (m_pBackgroundTexture) { + const double MONRATIO = m_RenderData.pMonitor->vecTransformedSize.x / m_RenderData.pMonitor->vecTransformedSize.y; + const double WPRATIO = m_pBackgroundTexture->m_vSize.x / m_pBackgroundTexture->m_vSize.y; + Vector2D origin; + double scale = 1.0; + + if (MONRATIO > WPRATIO) { + scale = m_RenderData.pMonitor->vecTransformedSize.x / m_pBackgroundTexture->m_vSize.x; + origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - m_pBackgroundTexture->m_vSize.y * scale) / 2.0; + } else { + scale = m_RenderData.pMonitor->vecTransformedSize.y / m_pBackgroundTexture->m_vSize.y; + origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - m_pBackgroundTexture->m_vSize.x * scale) / 2.0; + } + + CBox texbox = CBox{origin, m_pBackgroundTexture->m_vSize * scale}; + renderTextureInternalWithDamage(m_pBackgroundTexture, &texbox, 1.0, &fakeDamage); + } + + CBox monbox = {{}, pMonitor->vecPixelSize}; + renderTextureInternalWithDamage(tex, &monbox, 1.0, &fakeDamage); // bind back if (m_RenderData.currentFB) diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 712b87f3..1e8325c1 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -145,6 +145,7 @@ class CGradientValueData; class CHyprOpenGLImpl { public: CHyprOpenGLImpl(); + ~CHyprOpenGLImpl(); void begin(CMonitor*, const CRegion& damage, CFramebuffer* fb = nullptr, std::optional finalDamage = {}); void beginSimple(CMonitor*, const CRegion& damage, SP rb = nullptr, CFramebuffer* fb = nullptr); @@ -216,6 +217,7 @@ class CHyprOpenGLImpl { gbm_device* m_pGbmDevice = nullptr; EGLContext m_pEglContext = nullptr; EGLDisplay m_pEglDisplay = nullptr; + EGLDeviceEXT m_pEglDevice = nullptr; bool m_bReloadScreenShader = true; // at launch it can be set @@ -273,6 +275,8 @@ class CHyprOpenGLImpl { CShader m_sFinalScreenShader; CTimer m_tGlobalTimer; + SP m_pBackgroundTexture; + void logShaderError(const GLuint&, bool program = false); GLuint createProgram(const std::string&, const std::string&, bool dynamic = false); GLuint compileShader(const GLuint&, std::string, bool dynamic = false); @@ -280,6 +284,8 @@ class CHyprOpenGLImpl { void initShaders(); void initDRMFormats(); void initEGL(bool gbm); + EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); + void createBackgroundTexture(const std::string& path); // std::optional> getModsForFormat(EGLint format); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 82dfc887..127ae187 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -102,10 +102,10 @@ CHyprRenderer::~CHyprRenderer() { } static void renderSurface(SP surface, int x, int y, void* data) { - if (!surface->current.buffer || !surface->current.buffer->texture) + if (!surface->current.texture) return; - const auto& TEXTURE = surface->current.buffer->texture; + const auto& TEXTURE = surface->current.texture; const auto RDATA = (SRenderData*)data; const auto INTERACTIVERESIZEINPROGRESS = RDATA->pWindow && g_pInputManager->currentlyDraggedWindow.lock() == RDATA->pWindow && g_pInputManager->dragMode == MBIND_RESIZE; @@ -192,8 +192,8 @@ static void renderSurface(SP surface, int x, int y, void* da windowBox.round(); const bool MISALIGNEDFSV1 = std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && - windowBox.size() != surface->current.buffer->size /* misaligned */ && DELTALESSTHAN(windowBox.width, surface->current.buffer->size.x, 3) && - DELTALESSTHAN(windowBox.height, surface->current.buffer->size.y, 3) /* off by one-or-two */ && + windowBox.size() != surface->current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, surface->current.bufferSize.x, 3) && + DELTALESSTHAN(windowBox.height, surface->current.bufferSize.y, 3) /* off by one-or-two */ && (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface, windowBox.size(), MISALIGNEDFSV1); @@ -236,10 +236,8 @@ static void renderSurface(SP surface, int x, int y, void* da g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true); } - if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) { - Debug::log(TRACE, "presentFeedback for visible surface"); + if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) surface->presentFeedback(RDATA->when, RDATA->pMonitor); - } g_pHyprOpenGL->blend(true); @@ -271,7 +269,7 @@ bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, CMonitor* pMonitor) { return true; // if hidden behind fullscreen - if (PWINDOWWORKSPACE->m_bHasFullscreenWindow && !pWindow->m_bIsFullscreen && (!pWindow->m_bIsFloating || !pWindow->m_bCreatedOverFullscreen) && + if (PWINDOWWORKSPACE->m_bHasFullscreenWindow && !pWindow->isFullscreen() && (!pWindow->m_bIsFloating || !pWindow->m_bCreatedOverFullscreen) && pWindow->m_fAlpha.value() == 0) return false; @@ -287,7 +285,7 @@ bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, CMonitor* pMonitor) { // if not, check if it maybe is active on a different monitor. if (g_pCompositor->isWorkspaceVisible(pWindow->m_pWorkspace) && pWindow->m_bIsFloating /* tiled windows can't be multi-ws */) - return !pWindow->m_bIsFullscreen; // Do not draw fullscreen windows on other monitors + return !pWindow->isFullscreen(); // Do not draw fullscreen windows on other monitors if (pMonitor->activeSpecialWorkspace == pWindow->m_pWorkspace) return true; @@ -354,7 +352,7 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, PHLWORK if (w->m_fAlpha.value() == 0.f) continue; - if (w->m_bIsFullscreen || w->m_bIsFloating) + if (w->isFullscreen() || w->m_bIsFloating) continue; if (pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) @@ -371,7 +369,7 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, PHLWORK if (w->m_fAlpha.value() == 0.f) continue; - if (w->m_bIsFullscreen || !w->m_bIsFloating) + if (w->isFullscreen() || !w->m_bIsFloating) continue; if (w->m_iMonitorID == pWorkspace->m_iMonitorID && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) @@ -387,7 +385,7 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, PHLWORK for (auto& w : g_pCompositor->m_vWindows) { const auto PWORKSPACE = w->m_pWorkspace; - if (w->m_pWorkspace != pWorkspace || !w->m_bIsFullscreen) { + if (w->m_pWorkspace != pWorkspace || !w->isFullscreen()) { if (!(PWORKSPACE && (PWORKSPACE->m_vRenderOffset.isBeingAnimated() || PWORKSPACE->m_fAlpha.isBeingAnimated() || PWORKSPACE->m_bForceRendering))) continue; @@ -395,14 +393,14 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, PHLWORK continue; } - if (!w->m_bIsFullscreen) + if (!w->isFullscreen()) continue; if (w->m_iMonitorID == pWorkspace->m_iMonitorID && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; if (shouldRenderWindow(w, pMonitor)) - renderWindow(w, pMonitor, time, pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL, RENDER_PASS_ALL); + renderWindow(w, pMonitor, time, pWorkspace->m_efFullscreenMode != FSMODE_FULLSCREEN, RENDER_PASS_ALL); if (w->m_pWorkspace != pWorkspace) continue; @@ -418,7 +416,7 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, PHLWORK // then render windows over fullscreen. for (auto& w : g_pCompositor->m_vWindows) { - if (w->m_pWorkspace != pWorkspaceWindow->m_pWorkspace || (!w->m_bCreatedOverFullscreen && !w->m_bPinned) || (!w->m_bIsMapped && !w->m_bFadingOut) || w->m_bIsFullscreen) + if (w->m_pWorkspace != pWorkspaceWindow->m_pWorkspace || (!w->m_bCreatedOverFullscreen && !w->m_bPinned) || (!w->m_bIsMapped && !w->m_bFadingOut) || w->isFullscreen()) continue; if (w->m_iMonitorID == pWorkspace->m_iMonitorID && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) @@ -540,10 +538,10 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec decorate = false; renderdata.surface = pWindow->m_pWLSurface->resource(); - renderdata.dontRound = (pWindow->m_bIsFullscreen && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) || pWindow->m_sWindowData.noRounding.valueOrDefault(); + renderdata.dontRound = pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN) || pWindow->m_sWindowData.noRounding.valueOrDefault(); renderdata.fadeAlpha = pWindow->m_fAlpha.value() * (pWindow->m_bPinned ? 1.f : PWORKSPACE->m_fAlpha.value()); renderdata.alpha = pWindow->m_fActiveInactiveAlpha.value(); - renderdata.decorate = decorate && !pWindow->m_bX11DoesntWantBorders && (!pWindow->m_bIsFullscreen || PWORKSPACE->m_efFullscreenMode != FULLSCREEN_FULL); + renderdata.decorate = decorate && !pWindow->m_bX11DoesntWantBorders && !pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN); renderdata.rounding = ignoreAllGeometry || renderdata.dontRound ? 0 : pWindow->rounding() * pMonitor->scale; renderdata.blur = !ignoreAllGeometry; // if it shouldn't, it will be ignored later renderdata.pWindow = pWindow; @@ -570,7 +568,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec renderdata.y += pWindow->m_vFloatingOffset.y; // if window is floating and we have a slide animation, clip it to its full bb - if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->m_bIsFullscreen && PWORKSPACE->m_vRenderOffset.isBeingAnimated() && !pWindow->m_bPinned) { + if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->isFullscreen() && PWORKSPACE->m_vRenderOffset.isBeingAnimated() && !pWindow->m_bPinned) { CRegion rg = pWindow->getFullWindowBoundingBox().translate(-pMonitor->vecPosition + PWORKSPACE->m_vRenderOffset.value() + pWindow->m_vFloatingOffset).scale(pMonitor->scale); g_pHyprOpenGL->m_RenderData.clipBox = rg.getExtents(); @@ -826,8 +824,9 @@ void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, PHLWORKSPAC if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSessionLockPresent()) { // locked with no exclusive, draw only red - CBox boxe = {0, 0, INT16_MAX, INT16_MAX}; - g_pHyprOpenGL->renderRect(&boxe, CColor(1.0, 0.2, 0.2, 1.0)); + CBox boxe = {0, 0, INT16_MAX, INT16_MAX}; + const float A = g_pSessionLockManager->getRedScreenAlphaForMonitor(pMonitor->ID); + g_pHyprOpenGL->renderRect(&boxe, CColor(1.0, 0.2, 0.2, A)); return; } @@ -872,7 +871,7 @@ void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, PHLWORKSPAC // if we have a fullscreen, opaque window that convers the screen, we can skip this. // TODO: check better with solitary after MR for tearing. const auto PFULLWINDOW = pWorkspace ? g_pCompositor->getFullscreenWindowOnWorkspace(pWorkspace->m_iID) : nullptr; - if (!pWorkspace->m_bHasFullscreenWindow || pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL || !PFULLWINDOW || PFULLWINDOW->m_vRealSize.isBeingAnimated() || + if (!pWorkspace->m_bHasFullscreenWindow || pWorkspace->m_efFullscreenMode != FSMODE_FULLSCREEN || !PFULLWINDOW || PFULLWINDOW->m_vRealSize.isBeingAnimated() || !PFULLWINDOW->opaque() || pWorkspace->m_vRenderOffset.value() != Vector2D{} || g_pHyprOpenGL->preBlurQueued()) { if (!g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFBShouldRender) @@ -1015,7 +1014,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SPcurrent.viewport.hasSource) { // we stretch it to dest. if no dest, to 1,1 - Vector2D bufferSize = pSurface->current.buffer->size; + Vector2D bufferSize = pSurface->current.bufferSize; auto bufferSource = pSurface->current.viewport.source; // calculate UV for the basic src_box. Assume dest == size. Scale to dest later @@ -1031,8 +1030,8 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SPcurrent.buffer->size; - const Vector2D MISALIGNMENT = pSurface->current.buffer->size - projSize; + const Vector2D PIXELASUV = Vector2D{1, 1} / pSurface->current.bufferSize; + const Vector2D MISALIGNMENT = pSurface->current.bufferSize - projSize; if (MISALIGNMENT != Vector2D{}) uvBR -= MISALIGNMENT * PIXELASUV; } @@ -1194,6 +1193,8 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { shouldTear = true; } + pMonitor->tearingState.activelyTearing = shouldTear; + if (!*PNODIRECTSCANOUT && !shouldTear) { if (pMonitor->attemptDirectScanout()) { return; @@ -1203,11 +1204,6 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { } } - if (pMonitor->tearingState.activelyTearing != shouldTear) { - // change of state - pMonitor->tearingState.activelyTearing = shouldTear; - } - EMIT_HOOK_EVENT("preRender", pMonitor); timespec now; @@ -1372,6 +1368,8 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { frameDamage.add(damage); g_pHyprRenderer->damageMirrorsWith(pMonitor, frameDamage); + + pMonitor->output->state->addDamage(frameDamage); } pMonitor->renderingActive = false; @@ -1508,7 +1506,8 @@ void CHyprRenderer::setSurfaceScanoutMode(SP surface, SP 0) { + if ((exclusiveEdge == edges[i].singular_anchor || anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) && exclusive + edges[i].margin > 0) { if (edges[i].positive_axis) { *edges[i].positive_axis += exclusive + edges[i].margin; } @@ -1640,7 +1639,7 @@ void CHyprRenderer::arrangeLayerArray(CMonitor* pMonitor, const std::vectorgeometry = box; - applyExclusive(*usableArea, PSTATE->anchor, PSTATE->exclusive, PSTATE->margin.top, PSTATE->margin.right, PSTATE->margin.bottom, PSTATE->margin.left); + applyExclusive(*usableArea, PSTATE->anchor, PSTATE->exclusive, PSTATE->exclusiveEdge, PSTATE->margin.top, PSTATE->margin.right, PSTATE->margin.bottom, PSTATE->margin.left); if (Vector2D{box.width, box.height} != OLDSIZE) ls->layerSurface->configure(box.size()); @@ -1695,7 +1694,7 @@ void CHyprRenderer::damageSurface(SP pSurface, double x, dou return; const auto WLSURF = CWLSurface::fromResource(pSurface); - CRegion damageBox = WLSURF ? WLSURF->logicalDamage() : CRegion{}; + CRegion damageBox = WLSURF ? WLSURF->computeDamage() : CRegion{}; if (!WLSURF) { Debug::log(ERR, "BUG THIS: No CWLSurface for surface in damageSurface!!!"); return; diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 72efe8c4..a9397cac 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -139,7 +139,7 @@ class CHyprRenderer { std::vector> m_vRenderbuffers; friend class CHyprOpenGLImpl; - friend class CToplevelExportProtocolManager; + friend class CToplevelExportFrame; friend class CInputManager; friend class CPointerManager; }; diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 0f5b4c4c..94d00184 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -28,6 +28,8 @@ CTexture::CTexture(const SP buffer) { if (!buffer) return; + m_bOpaque = buffer->opaque; + auto attrs = buffer->dmabuf(); if (!attrs.success) { @@ -86,6 +88,7 @@ void CTexture::createFromDma(const Aquamarine::SDMABUFAttrs& attrs, void* image) return; } + m_bOpaque = FormatUtils::isFormatOpaque(attrs.format); m_iTarget = GL_TEXTURE_2D; m_iType = TEXTURE_RGBA; m_vSize = attrs.size; diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index 0da95300..e0ef5503 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -2,9 +2,11 @@ #include "../defines.hpp" #include +#include class IHLBuffer; HYPRUTILS_FORWARD(Math, CRegion); +using namespace Hyprutils::Math; enum TEXTURETYPE { TEXTURE_INVALID, // Invalid @@ -33,11 +35,13 @@ class CTexture { void allocate(); void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); - TEXTURETYPE m_iType = TEXTURE_RGBA; - GLenum m_iTarget = GL_TEXTURE_2D; - GLuint m_iTexID = 0; - Vector2D m_vSize; - void* m_pEglImage = nullptr; + TEXTURETYPE m_iType = TEXTURE_RGBA; + GLenum m_iTarget = GL_TEXTURE_2D; + GLuint m_iTexID = 0; + Vector2D m_vSize = {}; + void* m_pEglImage = nullptr; + eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; + bool m_bOpaque = false; private: void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp index 708215b6..f5e6e945 100644 --- a/src/render/decorations/CHyprBorderDecoration.cpp +++ b/src/render/decorations/CHyprBorderDecoration.cpp @@ -1,6 +1,7 @@ #include "CHyprBorderDecoration.hpp" #include "../../Compositor.hpp" #include "../../config/ConfigValue.hpp" +#include "../../managers/eventLoop/EventLoopManager.hpp" CHyprBorderDecoration::CHyprBorderDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) { m_pWindow = pWindow; @@ -82,8 +83,17 @@ eDecorationType CHyprBorderDecoration::getDecorationType() { } void CHyprBorderDecoration::updateWindow(PHLWINDOW) { - if (m_pWindow->getRealBorderSize() != m_seExtents.topLeft.x) - g_pDecorationPositioner->repositionDeco(this); + auto borderSize = m_pWindow->getRealBorderSize(); + + if (borderSize == m_iLastBorderSize) + return; + + if (borderSize <= 0 && m_iLastBorderSize <= 0) + return; + + m_iLastBorderSize = borderSize; + + g_pDecorationPositioner->repositionDeco(this); } void CHyprBorderDecoration::damageEntire() { diff --git a/src/render/decorations/CHyprBorderDecoration.hpp b/src/render/decorations/CHyprBorderDecoration.hpp index 8ad3263e..0e196565 100644 --- a/src/render/decorations/CHyprBorderDecoration.hpp +++ b/src/render/decorations/CHyprBorderDecoration.hpp @@ -36,6 +36,8 @@ class CHyprBorderDecoration : public IHyprWindowDecoration { CBox m_bAssignedGeometry = {0}; + int m_iLastBorderSize = -1; + CBox assignedBoxGlobal(); bool doesntWantBorders(); }; diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index a13750d0..8163a8c1 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -464,7 +464,7 @@ bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWIND bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPointer::SButtonEvent& e) { static auto PSTACKED = CConfigValue("group:groupbar:stacked"); - if (m_pWindow->m_bIsFullscreen && m_pWindow->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL) + if (m_pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) return true; const float BARRELATIVEX = pos.x - assignedBoxGlobal().x; diff --git a/src/xwayland/Server.cpp b/src/xwayland/Server.cpp index 3f4e7b43..cec582f6 100644 --- a/src/xwayland/Server.cpp +++ b/src/xwayland/Server.cpp @@ -262,13 +262,16 @@ void CXWaylandServer::die() { if (pipeSource) wl_event_source_remove(pipeSource); - if (waylandFDs[0]) + if (pipeFd >= 0) + close(pipeFd); + + if (waylandFDs[0] >= 0) close(waylandFDs[0]); - if (waylandFDs[1]) + if (waylandFDs[1] >= 0) close(waylandFDs[1]); - if (xwmFDs[0]) + if (xwmFDs[0] >= 0) close(xwmFDs[0]); - if (xwmFDs[1]) + if (xwmFDs[1] >= 0) close(xwmFDs[1]); // possible crash. Better to leak a bit. @@ -364,6 +367,7 @@ bool CXWaylandServer::start() { } pipeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, notify[0], WL_EVENT_READABLE, ::xwaylandReady, nullptr); + pipeFd = notify[0]; serverPID = fork(); if (serverPID < 0) { diff --git a/src/xwayland/Server.hpp b/src/xwayland/Server.hpp index 0c06a56c..7a36a965 100644 --- a/src/xwayland/Server.hpp +++ b/src/xwayland/Server.hpp @@ -41,6 +41,7 @@ class CXWaylandServer { std::array xFDReadEvents = {nullptr, nullptr}; wl_event_source* idleSource = nullptr; wl_event_source* pipeSource = nullptr; + int pipeFd = -1; std::array xwmFDs = {-1, -1}; std::array waylandFDs = {-1, -1}; diff --git a/src/xwayland/XSurface.cpp b/src/xwayland/XSurface.cpp index 5d25e0e8..30ffbc68 100644 --- a/src/xwayland/XSurface.cpp +++ b/src/xwayland/XSurface.cpp @@ -62,12 +62,12 @@ void CXWaylandSurface::ensureListeners() { }); listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { - if (surface->pending.buffer && !mapped) { + if (surface->pending.texture && !mapped) { map(); return; } - if (!surface->pending.buffer && mapped) { + if (!surface->pending.texture && mapped) { unmap(); return; } @@ -114,12 +114,11 @@ void CXWaylandSurface::unmap() { std::erase(g_pXWayland->pWM->mappedSurfacesStacking, self); mapped = false; + events.unmap.emit(); surface->unmap(); Debug::log(LOG, "XWayland surface {:x} unmapping", (uintptr_t)this); - events.unmap.emit(); - g_pXWayland->pWM->updateClientList(); } @@ -132,7 +131,7 @@ void CXWaylandSurface::considerMap() { return; } - if (surface->pending.buffer) { + if (surface->pending.texture) { Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer"); map(); return; diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp index 0eaa16be..f6b6864c 100644 --- a/src/xwayland/XWM.cpp +++ b/src/xwayland/XWM.cpp @@ -153,13 +153,16 @@ static bool lookupParentExists(SP XSURF, SP } void CXWM::readProp(SP XSURF, uint32_t atom, xcb_get_property_reply_t* reply) { - std::string propName = std::format("{}?", atom); - for (auto& ha : HYPRATOMS) { - if (ha.second != atom) - continue; + std::string propName; + if (Debug::trace) { + propName = std::format("{}?", atom); + for (auto& ha : HYPRATOMS) { + if (ha.second != atom) + continue; - propName = ha.first; - break; + propName = ha.first; + break; + } } if (atom == XCB_ATOM_WM_CLASS) { @@ -378,6 +381,20 @@ void CXWM::handleFocusIn(xcb_focus_in_event_t* e) { focusWindow(focusedSurface.lock()); } +void CXWM::handleFocusOut(xcb_focus_out_event_t* e) { + Debug::log(TRACE, "[xwm] focusOut mode={}, detail={}, event={}", e->mode, e->detail, e->event); + + const auto XSURF = windowForXID(e->event); + + if (!XSURF) + return; + + Debug::log(TRACE, "[xwm] focusOut for {} {} {} surface {}", XSURF->mapped ? "mapped" : "unmapped", XSURF->fullscreen ? "fullscreen" : "windowed", + XSURF == focusedSurface ? "focused" : "unfocused", XSURF->state.title); + + // do something? +} + void CXWM::sendWMMessage(SP surf, xcb_client_message_data_t* data, uint32_t mask) { xcb_client_message_event_t event = { .response_type = XCB_CLIENT_MESSAGE, @@ -522,7 +539,7 @@ bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) { // Debug::log(ERR, "[xwm] FIXME: CXWM::handleSelectionPropertyNotify stub"); - return false; + return true; } void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { @@ -674,9 +691,10 @@ int CXWM::onEvent(int fd, uint32_t mask) { case XCB_PROPERTY_NOTIFY: handlePropertyNotify((xcb_property_notify_event_t*)event); break; case XCB_CLIENT_MESSAGE: handleClientMessage((xcb_client_message_event_t*)event); break; case XCB_FOCUS_IN: handleFocusIn((xcb_focus_in_event_t*)event); break; + case XCB_FOCUS_OUT: handleFocusOut((xcb_focus_out_event_t*)event); break; case 0: handleError((xcb_value_error_t*)event); break; default: { - ; + Debug::log(TRACE, "[xwm] unhandled event {}", event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK); } } free(event); @@ -879,7 +897,7 @@ void CXWM::activateSurface(SP surf, bool activate) { if ((surf == focusedSurface && activate) || (surf && surf->overrideRedirect)) return; - if (!surf || (!activate || !surf->overrideRedirect /* if we are focusing on an OR child, don't unfocus parent */)) { + if (!surf) { setActiveWindow((uint32_t)XCB_WINDOW_NONE); focusWindow(nullptr); } else { @@ -891,6 +909,11 @@ void CXWM::activateSurface(SP surf, bool activate) { } void CXWM::sendState(SP surf) { + Debug::log(TRACE, "[xwm] sendState for {} {} {} surface {}", surf->mapped ? "mapped" : "unmapped", surf->fullscreen ? "fullscreen" : "windowed", + surf == focusedSurface ? "focused" : "unfocused", surf->state.title); + if (surf->fullscreen && surf->mapped && surf == focusedSurface) + surf->setWithdrawn(false); // resend normal state + if (surf->withdrawn) { xcb_delete_property(connection, surf->xID, HYPRATOMS["_NET_WM_STATE"]); return; @@ -917,7 +940,7 @@ void CXWM::onNewSurface(SP surf) { if (surf->client() != g_pXWayland->pServer->xwaylandClient) return; - Debug::log(LOG, "[xwm] New XWayland surface at {:x}", (uintptr_t)surf); + Debug::log(LOG, "[xwm] New XWayland surface at {:x}", (uintptr_t)surf.get()); const auto WLID = surf->id(); diff --git a/src/xwayland/XWM.hpp b/src/xwayland/XWM.hpp index 88606416..4f6f3f7c 100644 --- a/src/xwayland/XWM.hpp +++ b/src/xwayland/XWM.hpp @@ -104,6 +104,7 @@ class CXWM { void handlePropertyNotify(xcb_property_notify_event_t* e); void handleClientMessage(xcb_client_message_event_t* e); void handleFocusIn(xcb_focus_in_event_t* e); + void handleFocusOut(xcb_focus_out_event_t* e); void handleError(xcb_value_error_t* e); bool handleSelectionEvent(xcb_generic_event_t* e);