Merge branch 'main' into main

This commit is contained in:
Jasson 2024-08-04 23:06:58 -04:00 committed by GitHub
commit b16d2e2c51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
121 changed files with 3175 additions and 2490 deletions

View file

@ -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:

View file

@ -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 }}'

View file

@ -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

View file

@ -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:

View file

@ -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"
protocolnew("subprojects/hyprland-protocols/protocols"
"hyprland-global-shortcuts-v1" true)
protocol(
"subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml"
protocolnew("unstable/text-input" "text-input-unstable-v1" false)
protocolnew("subprojects/hyprland-protocols/protocols"
"hyprland-toplevel-export-v1" true)
protocol("unstable/text-input/text-input-unstable-v1.xml"
"text-input-unstable-v1" false)
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")

View file

@ -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')

View file

@ -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')

View file

@ -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": {

View file

@ -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(), ' ');

View file

@ -464,12 +464,22 @@ bool CPluginManager::updateHeaders(bool force) {
progress.m_szCurrentMessage = "Checking out sources";
progress.print();
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.branch + " 2>&1");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will run: " + "cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
if (ret.contains("fatal: unable to read tree")) {
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET
<< " Could not checkout the running Hyprland commit. If you are on -git, try updating.\nYou can also try re-running hyprpm update with --no-shallow.\n";
return false;
}
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (co): " + ret);
ret = execAndGet("cd " + WORKINGDIR + " && git rm subprojects/tracy && git submodule update --init 2>&1 && git reset --hard --recurse-submodules " + HLVER.hash);
ret = execAndGet("cd " + WORKINGDIR + " ; git rm subprojects/tracy ; git submodule update --init 2>&1 ; git reset --hard --recurse-submodules " + HLVER.hash);
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (rs): " + ret);
@ -529,7 +539,8 @@ bool CPluginManager::updateHeaders(bool force) {
std::cout << "\n";
} else {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID));
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID) + " (" +
headerErrorShort(HEADERSVALID) + ")");
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Failed";
progress.print();
@ -880,6 +891,18 @@ std::string CPluginManager::headerError(const eHeadersErrors err) {
return std::string{Colors::RED} + "" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n";
}
std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
switch (err) {
case HEADERS_CORRUPTED: return "Headers corrupted";
case HEADERS_MISMATCHED: return "Headers version mismatched";
case HEADERS_NOT_HYPRLAND: return "Not running on Hyprland";
case HEADERS_MISSING: return "Headers missing";
case HEADERS_DUPLICATED: return "Headers duplicated";
default: break;
}
return "?";
}
bool CPluginManager::hasDeps() {
std::vector<std::string> deps = {"meson", "cpio", "cmake"};
for (auto& d : deps) {

View file

@ -66,6 +66,7 @@ class CPluginManager {
private:
std::string headerError(const eHeadersErrors err);
std::string headerErrorShort(const eHeadersErrors err);
std::string m_szWorkingPluginDirectory = "";
};

View file

@ -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')

View file

@ -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 [

View file

@ -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)

View file

@ -7,6 +7,7 @@
#include "managers/SeatManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include <aquamarine/output/Output.hpp>
#include <bit>
#include <random>
#include <cstring>
#include <filesystem>
@ -43,7 +44,7 @@ int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal {}", signo);
if (signo == SIGTERM || signo == SIGINT || signo == SIGKILL)
g_pCompositor->cleanup();
g_pCompositor->stopCompositor();
return 0;
}
@ -193,6 +194,7 @@ CCompositor::CCompositor() {
}
CCompositor::~CCompositor() {
if (!m_bIsShuttingDown)
cleanup();
}
@ -207,7 +209,7 @@ void CCompositor::setRandomSplash() {
static std::vector<SP<Aquamarine::IOutput>> pendingOutputs;
//
void CCompositor::initServer() {
void CCompositor::initServer(std::string socketName, int socketFd) {
m_sWLDisplay = wl_display_create();
@ -246,7 +248,7 @@ void CCompositor::initServer() {
if (!m_pAqBackend) {
Debug::log(CRIT,
"m_pAqBackend was null! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a Wayland "
"m_pAqBackend was null! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a Wayland "
"session, NOT an X11 one.");
throwError("CBackend::create() failed!");
}
@ -257,7 +259,7 @@ void CCompositor::initServer() {
if (!m_pAqBackend->start()) {
Debug::log(CRIT,
"m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a "
"m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a "
"Wayland session, NOT an X11 one.");
throwError("CBackend::create() failed!");
}
@ -267,6 +269,15 @@ void CCompositor::initServer() {
m_iDRMFD = m_pAqBackend->drmFD();
Debug::log(LOG, "Running on DRMFD: {}", m_iDRMFD);
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));
@ -275,7 +286,7 @@ void CCompositor::initServer() {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break;
} else {
} else
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
}
}
@ -292,7 +303,6 @@ void CCompositor::initServer() {
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
}
Debug::log(LOG, "Setting WAYLAND_DISPLAY to {}", m_szWLDisplaySocket);
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
setenv("XDG_SESSION_TYPE", "wayland", 1);
@ -424,8 +434,16 @@ void CCompositor::cleanEnvironment() {
}
}
void CCompositor::stopCompositor() {
Debug::log(LOG, "Hyprland is stopping!");
// this stops the wayland loop, wl_display_run
wl_display_terminate(m_sWLDisplay);
m_bIsShuttingDown = true;
}
void CCompositor::cleanup() {
if (!m_sWLDisplay || m_bIsShuttingDown)
if (!m_sWLDisplay)
return;
signal(SIGABRT, SIG_DFL);
@ -499,13 +517,10 @@ void CCompositor::cleanup() {
if (m_critSigSource)
wl_event_source_remove(m_critSigSource);
wl_event_loop_destroy(m_sWLEventLoop);
wl_display_terminate(m_sWLDisplay);
m_sWLDisplay = nullptr;
// this frees all wayland resources, including sockets
wl_display_destroy(m_sWLDisplay);
std::string waylandSocket = std::string{getenv("XDG_RUNTIME_DIR")} + "/" + m_szWLDisplaySocket;
std::filesystem::remove(waylandSocket);
std::filesystem::remove(waylandSocket + ".lock");
Debug::close();
}
void CCompositor::initManagers(eManagersInitStage stage) {
@ -631,44 +646,7 @@ void CCompositor::prepareFallbackOutput() {
headless->createOutput();
}
void CCompositor::startCompositor(std::string socketName, int socketFd) {
if (!socketName.empty() && socketFd != -1) {
fcntl(socketFd, F_SETFD, FD_CLOEXEC);
const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd);
if (RETVAL >= 0) {
m_szWLDisplaySocket = socketName;
Debug::log(LOG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL);
} else
Debug::log(WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL);
} else {
// get socket, avoid using 0
for (int candidate = 1; candidate <= 32; candidate++) {
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break;
} else
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
}
}
if (m_szWLDisplaySocket.empty()) {
Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto");
const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay);
if (SOCKETSTR)
m_szWLDisplaySocket = SOCKETSTR;
}
if (m_szWLDisplaySocket.empty()) {
Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
}
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
setenv("XDG_SESSION_TYPE", "wayland", 1);
void CCompositor::startCompositor() {
signal(SIGPIPE, SIG_IGN);
if (m_pAqBackend->hasSession() /* Session-less Hyprland usually means a nest, don't update the env in that case */) {
@ -1129,7 +1107,7 @@ void CCompositor::focusSurface(SP<CWLSurfaceResource> pSurface, PHLWINDOW pWindo
return;
if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(pSurface)) {
Debug::log(LOG, "surface {:x} won't receive kb focus becuase grab rejected it", (uintptr_t)pSurface);
Debug::log(LOG, "surface {:x} won't receive kb focus becuase grab rejected it", (uintptr_t)pSurface.get());
return;
}
@ -1152,9 +1130,9 @@ void CCompositor::focusSurface(SP<CWLSurfaceResource> pSurface, PHLWINDOW pWindo
g_pSeatManager->setKeyboardFocus(pSurface);
if (pWindowOwner)
Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface, pWindowOwner);
Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface.get(), pWindowOwner);
else
Debug::log(LOG, "Set keyboard focus to surface {:x}", (uintptr_t)pSurface);
Debug::log(LOG, "Set keyboard focus to surface {:x}", (uintptr_t)pSurface.get());
g_pXWaylandManager->activateSurface(pSurface, true);
m_pLastFocus = pSurface;
@ -1213,15 +1191,10 @@ SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, st
}
PHLWINDOW CCompositor::getWindowFromSurface(SP<CWLSurfaceResource> pSurface) {
for (auto& w : m_vWindows) {
if (!w->m_bIsMapped || w->m_bFadingOut)
continue;
if (w->m_pWLSurface->resource() == pSurface)
return w;
}
if (!pSurface || !pSurface->hlSurface)
return nullptr;
return pSurface->hlSurface->getWindow();
}
PHLWINDOW CCompositor::getWindowFromHandle(uint32_t handle) {
@ -1236,7 +1209,7 @@ PHLWINDOW CCompositor::getWindowFromHandle(uint32_t handle) {
PHLWINDOW CCompositor::getFullscreenWindowOnWorkspace(const int& ID) {
for (auto& w : m_vWindows) {
if (w->workspaceID() == ID && w->m_bIsFullscreen)
if (w->workspaceID() == ID && w->isFullscreen())
return w;
}
@ -1524,7 +1497,7 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
if (!PMONITOR)
return nullptr; // ??
const auto WINDOWIDEALBB = pWindow->m_bIsFullscreen ? CBox{PMONITOR->vecPosition, PMONITOR->vecSize} : pWindow->getWindowIdealBoundingBoxIgnoreReserved();
const auto WINDOWIDEALBB = pWindow->isFullscreen() ? CBox{PMONITOR->vecPosition, PMONITOR->vecSize} : pWindow->getWindowIdealBoundingBoxIgnoreReserved();
const auto POSA = Vector2D(WINDOWIDEALBB.x, WINDOWIDEALBB.y);
const auto SIZEA = Vector2D(WINDOWIDEALBB.width, WINDOWIDEALBB.height);
@ -1537,13 +1510,13 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
// for tiled windows, we calc edges
for (auto& w : m_vWindows) {
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->m_bIsFullscreen && w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->isFullscreen() && w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
continue;
if (pWindow->m_iMonitorID == w->m_iMonitorID && pWindow->m_pWorkspace != w->m_pWorkspace)
continue;
if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen)
if (PWORKSPACE->m_bHasFullscreenWindow && !w->isFullscreen() && !w->m_bCreatedOverFullscreen)
continue;
if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID)
@ -1629,13 +1602,13 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
constexpr float THRESHOLD = 0.3 * M_PI;
for (auto& w : m_vWindows) {
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->m_bIsFullscreen && !w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
if (w == pWindow || !w->m_bIsMapped || w->isHidden() || (!w->isFullscreen() && !w->m_bIsFloating) || !isWorkspaceVisible(w->m_pWorkspace))
continue;
if (pWindow->m_iMonitorID == w->m_iMonitorID && pWindow->m_pWorkspace != w->m_pWorkspace)
continue;
if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen)
if (PWORKSPACE->m_bHasFullscreenWindow && !w->isFullscreen() && !w->m_bCreatedOverFullscreen)
continue;
if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID)
@ -1917,7 +1890,7 @@ void CCompositor::updateWindowAnimatedDecorationValues(PHLWINDOW pWindow) {
// opacity
const auto PWORKSPACE = pWindow->m_pWorkspace;
if (pWindow->m_bIsFullscreen && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
if (pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) {
pWindow->m_fActiveInactiveAlpha = pWindow->m_sWindowData.alphaFullscreen.valueOrDefault().applyAlpha(*PFULLSCREENALPHA);
} else {
if (pWindow == m_pLastWindow)
@ -1987,7 +1960,7 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.goal() - pMonitorA->vecPosition + pMonitorB->vecPosition;
if (w->m_bIsFullscreen) {
if (w->isFullscreen()) {
w->m_vRealPosition = pMonitorB->vecPosition;
w->m_vRealSize = pMonitorB->vecSize;
}
@ -2012,7 +1985,7 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.goal() - pMonitorB->vecPosition + pMonitorA->vecPosition;
if (w->m_bIsFullscreen) {
if (w->isFullscreen()) {
w->m_vRealPosition = pMonitorA->vecPosition;
w->m_vRealSize = pMonitorA->vecSize;
}
@ -2189,7 +2162,7 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, CMonitor* pMon
if (w->m_bIsFloating)
w->m_vRealPosition = w->m_vRealPosition.goal() - POLDMON->vecPosition + pMonitor->vecPosition;
if (w->m_bIsFullscreen) {
if (w->isFullscreen()) {
w->m_vRealPosition = pMonitor->vecPosition;
w->m_vRealSize = pMonitor->vecSize;
}
@ -2264,12 +2237,12 @@ void CCompositor::updateFullscreenFadeOnWorkspace(PHLWORKSPACE pWorkspace) {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == pWorkspace) {
if (w->m_bFadingOut || w->m_bPinned || w->m_bIsFullscreen)
if (w->m_bFadingOut || w->m_bPinned || w->isFullscreen())
continue;
if (!FULLSCREEN)
w->m_fAlpha = 1.f;
else if (!w->m_bIsFullscreen)
else if (!w->isFullscreen())
w->m_fAlpha = !w->m_bCreatedOverFullscreen ? 0.f : 1.f;
}
}
@ -2279,52 +2252,87 @@ void CCompositor::updateFullscreenFadeOnWorkspace(PHLWORKSPACE pWorkspace) {
if (pWorkspace->m_iID == PMONITOR->activeWorkspaceID() || pWorkspace->m_iID == PMONITOR->activeSpecialWorkspaceID()) {
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->fadingOut)
ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL ? 0.f : 1.f;
ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f;
}
}
}
void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMode mode) {
if (!validMapped(pWindow) || g_pCompositor->m_bUnsafeState)
void CCompositor::changeWindowFullscreenModeInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON) {
setWindowFullscreenInternal(
PWINDOW, (eFullscreenMode)(ON ? (uint8_t)PWINDOW->m_sFullscreenState.internal | (uint8_t)MODE : ((uint8_t)PWINDOW->m_sFullscreenState.internal & (uint8_t)~MODE)));
}
void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON) {
setWindowFullscreenClient(PWINDOW,
(eFullscreenMode)(ON ? (uint8_t)PWINDOW->m_sFullscreenState.client | (uint8_t)MODE : ((uint8_t)PWINDOW->m_sFullscreenState.client & (uint8_t)~MODE)));
}
void CCompositor::setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
if (PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = MODE, .client = MODE});
else
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = MODE, .client = PWINDOW->m_sFullscreenState.client});
}
void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
if (PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = MODE, .client = MODE});
else
setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = PWINDOW->m_sFullscreenState.internal, .client = MODE});
}
void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, sFullscreenState state) {
static auto PNODIRECTSCANOUT = CConfigValue<Hyprlang::INT>("misc:no_direct_scanout");
if (!validMapped(PWINDOW) || g_pCompositor->m_bUnsafeState)
return;
if (pWindow->m_bPinned) {
Debug::log(LOG, "Pinned windows cannot be fullscreen'd");
state.internal = std::clamp(state.internal, (eFullscreenMode)0, FSMODE_MAX);
state.client = std::clamp(state.client, (eFullscreenMode)0, FSMODE_MAX);
const auto PMONITOR = getMonitorFromID(PWINDOW->m_iMonitorID);
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
const eFullscreenMode CURRENT_EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)PWINDOW->m_sFullscreenState.internal);
const eFullscreenMode EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)state.internal);
const bool CHANGEINTERNAL = !(PWINDOW->m_bPinned || CURRENT_EFFECTIVE_MODE == EFFECTIVE_MODE || (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen()));
// TODO: update the state on syncFullscreen changes
if (!CHANGEINTERNAL && PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
return;
}
if (pWindow->m_bIsFullscreen == on) {
Debug::log(LOG, "Window is already in the required fullscreen state");
PWINDOW->m_sFullscreenState.client = state.client;
g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN);
if (!CHANGEINTERNAL)
return;
}
const auto PMONITOR = getMonitorFromID(pWindow->m_iMonitorID);
g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(PWINDOW, CURRENT_EFFECTIVE_MODE, EFFECTIVE_MODE);
const auto PWORKSPACE = pWindow->m_pWorkspace;
PWINDOW->m_sFullscreenState.internal = state.internal;
PWORKSPACE->m_efFullscreenMode = EFFECTIVE_MODE;
PWORKSPACE->m_bHasFullscreenWindow = EFFECTIVE_MODE != FSMODE_NONE;
const auto MODE = mode == FULLSCREEN_INVALID ? PWORKSPACE->m_efFullscreenMode : mode;
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)EFFECTIVE_MODE != FSMODE_NONE)});
EMIT_HOOK_EVENT("fullscreen", PWINDOW);
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
Debug::log(LOG, "Rejecting fullscreen ON on a fullscreen workspace");
return;
}
g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(pWindow, MODE, on);
g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->shouldSendFullscreenState());
updateWindowAnimatedDecorationValues(pWindow);
PWINDOW->updateDynamicRules();
PWINDOW->updateWindowDecos();
updateWindowAnimatedDecorationValues(PWINDOW);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->m_iMonitorID);
// make all windows on the same workspace under the fullscreen window
for (auto& w : m_vWindows) {
if (w->m_pWorkspace == PWORKSPACE && !w->m_bIsFullscreen && !w->m_bFadingOut && !w->m_bPinned)
if (w->m_pWorkspace == PWORKSPACE && !w->isFullscreen() && !w->m_bFadingOut && !w->m_bPinned)
w->m_bCreatedOverFullscreen = false;
}
updateFullscreenFadeOnWorkspace(PWORKSPACE);
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goal(), true);
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal(), true);
forceReportSizesToWindowsOnWorkspace(pWindow->workspaceID());
forceReportSizesToWindowsOnWorkspace(PWINDOW->workspaceID());
g_pInputManager->recheckIdleInhibitorStatus();
@ -2333,7 +2341,9 @@ void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMod
return;
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
g_pHyprRenderer->setSurfaceScanoutMode(pWindow->m_pWLSurface->resource(), on ? PMONITOR->self.lock() : nullptr);
// ignore if DS is disabled.
if (!*PNODIRECTSCANOUT)
g_pHyprRenderer->setSurfaceScanoutMode(PWINDOW->m_pWLSurface->resource(), EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->self.lock() : nullptr);
g_pConfigManager->ensureVRR(PMONITOR);
}
@ -2665,11 +2675,11 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
if (pWindow->m_bPinned && pWorkspace->m_bIsSpecialWorkspace)
return;
const bool FULLSCREEN = pWindow->m_bIsFullscreen;
const auto FULLSCREENMODE = pWindow->m_pWorkspace->m_efFullscreenMode;
const bool FULLSCREEN = pWindow->isFullscreen();
const auto FULLSCREENMODE = pWindow->m_sFullscreenState.internal;
if (FULLSCREEN)
setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
setWindowFullscreenInternal(pWindow, FSMODE_NONE);
if (!pWindow->m_bIsFloating) {
g_pLayoutManager->getCurrentLayout()->onWindowRemovedTiling(pWindow);
@ -2702,7 +2712,7 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
}
if (FULLSCREEN)
setWindowFullscreen(pWindow, true, FULLSCREENMODE);
setWindowFullscreenInternal(pWindow, FULLSCREENMODE);
g_pCompositor->updateWorkspaceWindows(pWorkspace->m_iID);
g_pCompositor->updateWorkspaceWindows(pWindow->workspaceID());
@ -2861,7 +2871,7 @@ void CCompositor::setPreferredScaleForSurface(SP<CWLSurfaceResource> pSurface, d
const auto PSURFACE = CWLSurface::fromResource(pSurface);
if (!PSURFACE) {
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface);
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface.get());
return;
}
@ -2874,7 +2884,7 @@ void CCompositor::setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurfac
const auto PSURFACE = CWLSurface::fromResource(pSurface);
if (!PSURFACE) {
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface);
Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface.get());
return;
}

View file

@ -69,8 +69,9 @@ class CCompositor {
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap;
void initServer();
void startCompositor(std::string socketName, int socketFd);
void initServer(std::string socketName, int socketFd);
void startCompositor();
void stopCompositor();
void cleanup();
void createLockFile();
void removeLockFile();
@ -145,7 +146,11 @@ class CCompositor {
void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int64_t&);
void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID);
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const sFullscreenState state);
void changeWindowFullscreenModeInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
PHLWINDOW getX11Parent(PHLWINDOW);
void scheduleFrameForMonitor(CMonitor*, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);

View file

@ -669,7 +669,7 @@ std::string CConfigManager::getConfigDir() {
Debug::log(WARN, "Creating config home directory");
try {
std::filesystem::create_directories(parentPath);
} catch (std::exception e) { throw e; }
} catch (std::exception& e) { throw e; }
}
Debug::log(WARN, "No config file found; attempting to generate.");
@ -1141,7 +1141,7 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
// Since some rules will be applied later, we need to store some flags
bool hasFloating = pWindow->m_bIsFloating;
bool hasFullscreen = pWindow->m_bIsFullscreen;
bool hasFullscreen = pWindow->isFullscreen();
// Local tags for dynamic tag rule match
auto tags = pWindow->m_tags;
@ -1340,12 +1340,14 @@ void CConfigManager::dispatchExecOnce() {
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS");
firstExecDispatched = true;
isLaunchingExecOnce = true;
for (auto& c : firstExecRequests) {
handleRawExec("", c);
}
firstExecRequests.clear(); // free some kb of memory :P
isLaunchingExecOnce = false;
// set input, fixes some certain issues
g_pInputManager->setKeyboardLayout();
@ -1487,7 +1489,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
if (!PWORKSPACE)
return; // ???
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
if (WORKSPACEFULL) {
m->output->state->setAdaptiveSync(true);
@ -2024,6 +2026,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
bool ignoreMods = false;
bool multiKey = false;
bool hasDescription = false;
bool dontInhibit = false;
const auto BINDARGS = command.substr(4);
for (auto& arg : BINDARGS) {
@ -2045,6 +2048,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
multiKey = true;
} else if (arg == 'd') {
hasDescription = true;
} else if (arg == 'p') {
dontInhibit = true;
} else {
return "bind: invalid flag";
}
@ -2113,8 +2118,9 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
return "Invalid catchall, catchall keybinds are only allowed in submaps.";
}
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, DESCRIPTION,
release, repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription});
g_pKeybindManager->addKeybind(SKeybind{
parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, DESCRIPTION, release,
repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit});
}
return {};
@ -2134,11 +2140,11 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma
bool windowRuleValid(const std::string& RULE) {
static const auto rules = std::unordered_set<std::string>{
"fakefullscreen", "float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile",
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile",
};
static const auto rulesPrefix = std::vector<std::string>{
"animation", "bordercolor", "bordersize", "center", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move",
"opacity", "plugin:", "pseudo", "rounding", "size", "suppressevent", "tag", "workspace", "xray",
"animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor",
"move", "opacity", "plugin:", "pseudo", "rounding", "size", "suppressevent", "tag", "workspace", "xray",
};
const auto VALS = CVarList(RULE, 2, ' ');

View file

@ -158,9 +158,6 @@ class CConfigManager {
void performMonitorReload();
void appendMonitorRule(const SMonitorRule&);
bool replaceMonitorRule(const SMonitorRule&);
bool m_bWantsMonitorReload = false;
bool m_bForceReload = false;
bool m_bNoMonitorReload = false;
void ensureMonitorStatus();
void ensureVRR(CMonitor* pMonitor = nullptr);
@ -212,8 +209,10 @@ class CConfigManager {
{"nomaxsize", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noMaxSize; }},
{"norounding", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noRounding; }},
{"noshadow", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noShadow; }},
{"noshortcutsinhibit", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }},
{"opaque", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.opaque; }},
{"forcergbx", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.RGBX; }},
{"syncfullscreen", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }},
{"immediate", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.tearing; }},
{"xray", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.xray; }},
};
@ -222,6 +221,12 @@ class CConfigManager {
{"rounding", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.rounding; }}, {"bordersize", [](PHLWINDOW pWindow) { return &pWindow->m_sWindowData.borderSize; }}};
bool m_bWantsMonitorReload = false;
bool m_bForceReload = false;
bool m_bNoMonitorReload = false;
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
private:
std::unique_ptr<Hyprlang::CConfig> m_pConfig;

View file

@ -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";

View file

@ -32,6 +32,7 @@ using namespace Hyprutils::String;
#include "../devices/IKeyboard.hpp"
#include "../devices/ITouch.hpp"
#include "../devices/Tablet.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "debug/RollingLogFollow.hpp"
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
@ -239,8 +240,7 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
"xwayland": {},
"pinned": {},
"fullscreen": {},
"fullscreenMode": {},
"fakeFullscreen": {},
"fullscreenClient": {},
"grouped": [{}],
"tags": [{}],
"swallowing": "0x{:x}",
@ -251,19 +251,19 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (w->m_bIsPseudotiled ? "true" : "false"),
(int64_t)w->m_iMonitorID, escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass),
escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"),
(w->m_bIsFullscreen ? "true" : "false"), (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0),
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
(uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
} else {
return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
"{}\n\txwayland: {}\n\tpinned: "
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
(uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y,
(int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName),
(int)w->m_bIsFloating, (int)w->m_bIsPseudotiled, (int64_t)w->m_iMonitorID, w->m_szClass, w->m_szTitle, w->m_szInitialClass, w->m_szInitialTitle, w->getPID(),
(int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, (w->m_bIsFullscreen ? (w->m_pWorkspace ? w->m_pWorkspace->m_efFullscreenMode : 0) : 0),
(int)w->m_bFakeFullscreenState, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
(int)w->m_bIsX11, (int)w->m_bPinned, (uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format),
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
}
}
@ -776,7 +776,7 @@ std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request)
std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS)
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
@ -1604,6 +1604,10 @@ CHyprCtl::CHyprCtl() {
CHyprCtl::~CHyprCtl() {
if (m_eventSource)
wl_event_source_remove(m_eventSource);
if (m_iSocketFD >= 0)
close(m_iSocketFD);
if (!m_socketPath.empty())
unlink(m_socketPath.c_str());
}
SP<SHyprCtlCommand> CHyprCtl::registerCommand(SHyprCtlCommand cmd) {
@ -1821,9 +1825,9 @@ void CHyprCtl::startHyprCtlSocket() {
sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX};
std::string socketPath = g_pCompositor->m_szInstancePath + "/.socket.sock";
m_socketPath = g_pCompositor->m_szInstancePath + "/.socket.sock";
strcpy(SERVERADDRESS.sun_path, socketPath.c_str());
strcpy(SERVERADDRESS.sun_path, m_socketPath.c_str());
if (bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) {
Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
@ -1833,7 +1837,7 @@ void CHyprCtl::startHyprCtlSocket() {
// 10 max queued.
listen(m_iSocketFD, 10);
Debug::log(LOG, "Hypr socket started at {}", socketPath);
Debug::log(LOG, "Hypr socket started at {}", m_socketPath);
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
}

View file

@ -31,6 +31,7 @@ class CHyprCtl {
std::vector<SP<SHyprCtlCommand>> m_vCommands;
wl_event_source* m_eventSource = nullptr;
std::string m_socketPath;
};
inline std::unique_ptr<CHyprCtl> g_pHyprCtl;

View file

@ -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.

View file

@ -4,6 +4,7 @@
#include <iostream>
#include <fstream>
#include <chrono>
#include <mutex>
#include "../includes.hpp"
#include "../helpers/MiscFunctions.hpp"
@ -22,6 +23,7 @@ enum LogLevel {
namespace Debug {
inline std::string logFile;
inline std::ofstream logOfs;
inline int64_t* const* disableLogs = nullptr;
inline int64_t* const* disableTime = nullptr;
inline bool disableStdout = false;
@ -30,14 +32,18 @@ namespace Debug {
inline int64_t* const* coloredLogs = nullptr;
inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
inline std::mutex logMutex;
void init(const std::string& IS);
void close();
//
void log(LogLevel level, std::string str);
template <typename... Args>
void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
std::lock_guard<std::mutex> guard(logMutex);
if (level == TRACE && !trace)
return;
@ -66,5 +72,6 @@ namespace Debug {
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
log(level, logMsg);
logMutex.unlock();
}
};

View file

@ -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);

View file

@ -22,7 +22,7 @@ CPopup::CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner) : m_pParent(pOwner),
m_pWindowOwner = pOwner->m_pWindowOwner;
m_vLastSize = popup->surface->current.geometry.size();
unconstrain();
reposition();
initAllSignals();
}
@ -188,18 +188,18 @@ void CPopup::onReposition() {
m_vLastPos = coordsRelativeToParent();
unconstrain();
reposition();
}
void CPopup::unconstrain() {
void CPopup::reposition() {
const auto COORDS = t1ParentCoords();
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);
if (!PMONITOR)
return;
CBox box = {PMONITOR->vecPosition.x - COORDS.x, PMONITOR->vecPosition.y - COORDS.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
m_pResource->applyPositioning(box, COORDS - PMONITOR->vecPosition);
CBox box = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
m_pResource->applyPositioning(box, COORDS);
}
Vector2D CPopup::coordsRelativeToParent() {

View file

@ -74,7 +74,7 @@ class CPopup {
} listeners;
void initAllSignals();
void unconstrain();
void reposition();
void recheckChildrenRecursive();
void sendScale();

View file

@ -1,6 +1,7 @@
#include "WLSurface.hpp"
#include "../Compositor.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/LayerShell.hpp"
void CWLSurface::assign(SP<CWLSurfaceResource> pSurface) {
m_pResource = pSurface;
@ -56,12 +57,12 @@ bool CWLSurface::small() const {
if (!validMapped(m_pWindowOwner) || !exists())
return false;
if (!m_pResource->current.buffer)
if (!m_pResource->current.texture)
return false;
const auto O = m_pWindowOwner.lock();
return O->m_vReportedSize.x > m_pResource->current.buffer->size.x + 1 || O->m_vReportedSize.y > m_pResource->current.buffer->size.y + 1;
return O->m_vReportedSize.x > m_pResource->current.bufferSize.x + 1 || O->m_vReportedSize.y > m_pResource->current.bufferSize.y + 1;
}
Vector2D CWLSurface::correctSmallVec() const {
@ -74,33 +75,52 @@ Vector2D CWLSurface::correctSmallVec() const {
return Vector2D{(O->m_vReportedSize.x - SIZE.x) / 2, (O->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_vRealSize.value() / O->m_vReportedSize);
}
Vector2D CWLSurface::getViewporterCorrectedSize() const {
if (!exists() || !m_pResource->current.buffer)
Vector2D CWLSurface::correctSmallVecBuf() const {
if (!exists() || !small() || m_bFillIgnoreSmall || !m_pResource->current.texture)
return {};
return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.buffer->size;
const auto SIZE = getViewporterCorrectedSize();
const auto BS = m_pResource->current.bufferSize;
return Vector2D{(BS.x - SIZE.x) / 2, (BS.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY});
}
CRegion CWLSurface::logicalDamage() const {
if (!m_pResource->current.buffer)
Vector2D CWLSurface::getViewporterCorrectedSize() const {
if (!exists() || !m_pResource->current.texture)
return {};
return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.bufferSize;
}
CRegion CWLSurface::computeDamage() const {
if (!m_pResource->current.texture)
return {};
CRegion damage = m_pResource->accumulateCurrentBufferDamage();
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.buffer->size.x, m_pResource->current.buffer->size.y);
damage.scale(1.0 / m_pResource->current.scale);
damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y);
const auto VPSIZE = getViewporterCorrectedSize();
const auto CORRECTVEC = correctSmallVec();
const auto BUFSIZE = m_pResource->current.bufferSize;
const auto CORRECTVEC = correctSmallVecBuf();
if (m_pResource->current.viewport.hasSource)
damage.intersect(m_pResource->current.viewport.source);
const auto SCALEDSRCSIZE =
m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.buffer->size;
const auto SCALEDSRCSIZE = m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.bufferSize;
damage.scale({VPSIZE.x / SCALEDSRCSIZE.x, VPSIZE.y / SCALEDSRCSIZE.y});
damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y});
damage.translate(CORRECTVEC);
// go from buffer coords in the damage to hl logical
const auto BOX = getSurfaceBoxGlobal();
const Vector2D SCALE = BOX.has_value() ? BOX->size() / m_pResource->current.bufferSize :
Vector2D{1.0 / m_pResource->current.scale, 1.0 / m_pResource->current.scale /* Wrong... but we can't really do better */};
damage.scale(SCALE);
if (m_pWindowOwner)
damage.scale(m_pWindowOwner->m_fX11SurfaceScaledBy); // fix xwayland:force_zero_scaling stuff that will be fucked by the above a bit
return damage;
}
@ -141,27 +161,27 @@ void CWLSurface::init() {
Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this);
}
PHLWINDOW CWLSurface::getWindow() {
PHLWINDOW CWLSurface::getWindow() const {
return m_pWindowOwner.lock();
}
PHLLS CWLSurface::getLayer() {
PHLLS CWLSurface::getLayer() const {
return m_pLayerOwner.lock();
}
CPopup* CWLSurface::getPopup() {
CPopup* CWLSurface::getPopup() const {
return m_pPopupOwner;
}
CSubsurface* CWLSurface::getSubsurface() {
CSubsurface* CWLSurface::getSubsurface() const {
return m_pSubsurfaceOwner;
}
bool CWLSurface::desktopComponent() {
bool CWLSurface::desktopComponent() const {
return !m_pLayerOwner.expired() || !m_pWindowOwner.expired() || m_pSubsurfaceOwner || m_pPopupOwner;
}
std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() {
std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() const {
if (!desktopComponent())
return {};
@ -181,7 +201,7 @@ void CWLSurface::appendConstraint(WP<CPointerConstraint> constraint) {
m_pConstraint = constraint;
}
SP<CPointerConstraint> CWLSurface::constraint() {
SP<CPointerConstraint> CWLSurface::constraint() const {
return m_pConstraint.lock();
}
@ -202,3 +222,11 @@ SP<CWLSurface> CWLSurface::fromResource(SP<CWLSurfaceResource> pSurface) {
return nullptr;
return pSurface->hlSurface.lock();
}
bool CWLSurface::keyboardFocusable() const {
if (m_pWindowOwner || m_pPopupOwner || m_pSubsurfaceOwner)
return true;
if (m_pLayerOwner && m_pLayerOwner->layerSurface)
return m_pLayerOwner->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
return false;
}

View file

@ -35,20 +35,22 @@ class CWLSurface {
bool exists() const;
bool small() const; // means surface is smaller than the requested size
Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces
Vector2D correctSmallVecBuf() const; // returns a corrective vector for small() surfaces, in BL coords
Vector2D getViewporterCorrectedSize() const;
CRegion logicalDamage() const;
CRegion computeDamage() const; // logical coordinates. May be wrong if the surface is unassigned
bool visible();
bool keyboardFocusable() const;
// getters for owners.
PHLWINDOW getWindow();
PHLLS getLayer();
CPopup* getPopup();
CSubsurface* getSubsurface();
PHLWINDOW getWindow() const;
PHLLS getLayer() const;
CPopup* getPopup() const;
CSubsurface* getSubsurface() const;
// desktop components misc utils
std::optional<CBox> getSurfaceBoxGlobal();
std::optional<CBox> getSurfaceBoxGlobal() const;
void appendConstraint(WP<CPointerConstraint> constraint);
SP<CPointerConstraint> constraint();
SP<CPointerConstraint> constraint() const;
// allow stretching. Useful for plugins.
bool m_bFillIgnoreSmall = false;
@ -107,7 +109,7 @@ class CWLSurface {
void destroy();
void init();
bool desktopComponent();
bool desktopComponent() const;
struct {
CHyprSignalListener destroy;

View file

@ -1,4 +1,5 @@
#include <any>
#include <bit>
#include <string_view>
#include <algorithm>
#include "Window.hpp"
@ -192,7 +193,7 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
auto POS = m_vPosition;
auto SIZE = m_vSize;
if (m_bIsFullscreen) {
if (isFullscreen()) {
POS = PMONITOR->vecPosition;
SIZE = PMONITOR->vecSize;
@ -477,12 +478,6 @@ void unregisterVar(void* ptr) {
void CWindow::onUnmap() {
static auto PCLOSEONLASTSPECIAL = CConfigValue<Hyprlang::INT>("misc:close_special_on_empty");
if (g_pCompositor->m_pLastWindow.lock().get() == this)
g_pCompositor->m_pLastWindow.reset();
if (g_pInputManager->currentlyDraggedWindow.lock().get() == this)
g_pInputManager->currentlyDraggedWindow.reset();
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
if (!m_szInitialWorkspaceToken.empty()) {
@ -976,8 +971,9 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
return;
const auto PCURRENT = getGroupCurrent();
const bool FULLSCREEN = PCURRENT->m_bIsFullscreen;
const bool FULLSCREEN = PCURRENT->isFullscreen();
const auto WORKSPACE = PCURRENT->m_pWorkspace;
const auto MODE = PCURRENT->m_sFullscreenState.client;
const auto PWINDOWSIZE = PCURRENT->m_vRealSize.goal();
const auto PWINDOWPOS = PCURRENT->m_vRealPosition.goal();
@ -985,7 +981,7 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
const auto CURRENTISFOCUS = PCURRENT == g_pCompositor->m_pLastWindow.lock();
if (FULLSCREEN)
g_pCompositor->setWindowFullscreen(PCURRENT, false, WORKSPACE->m_efFullscreenMode);
g_pCompositor->setWindowFullscreenInternal(PCURRENT, FSMODE_NONE);
PCURRENT->setHidden(true);
pWindow->setHidden(false); // can remove m_pLastWindow
@ -1003,7 +999,7 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
g_pCompositor->focusWindow(pWindow);
if (FULLSCREEN)
g_pCompositor->setWindowFullscreen(pWindow, true, WORKSPACE->m_efFullscreenMode);
g_pCompositor->setWindowFullscreenInternal(pWindow, MODE);
g_pHyprRenderer->damageWindow(pWindow);
@ -1102,18 +1098,18 @@ bool CWindow::opaque() {
if (PWORKSPACE->m_fAlpha.value() != 1.f)
return false;
if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.buffer)
return m_pXWaylandSurface->surface->current.buffer->opaque;
if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.texture)
return m_pXWaylandSurface->surface->current.texture->m_bOpaque;
if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.buffer)
if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.texture)
return false;
// TODO: this is wrong
const auto EXTENTS = m_pXDGSurface->surface->current.opaque.getExtents();
if (EXTENTS.w >= m_pXDGSurface->surface->current.buffer->size.x && EXTENTS.h >= m_pXDGSurface->surface->current.buffer->size.y)
if (EXTENTS.w >= m_pXDGSurface->surface->current.bufferSize.x && EXTENTS.h >= m_pXDGSurface->surface->current.bufferSize.y)
return true;
return m_pWLSurface->resource()->current.buffer->opaque;
return m_pWLSurface->resource()->current.texture->m_bOpaque;
}
float CWindow::rounding() {
@ -1146,7 +1142,7 @@ void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) {
}
int CWindow::getRealBorderSize() {
if (m_sWindowData.noBorder.valueOrDefault() || (m_pWorkspace && m_bIsFullscreen && (m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL)))
if (m_sWindowData.noBorder.valueOrDefault() || (m_pWorkspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN)))
return 0;
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
@ -1159,11 +1155,6 @@ bool CWindow::canBeTorn() {
return m_sWindowData.tearing.valueOr(m_bTearingHint) && *PTEARING;
}
bool CWindow::shouldSendFullscreenState() {
const auto MODE = m_pWorkspace->m_efFullscreenMode;
return m_bDontSendFullscreen ? false : (m_bFakeFullscreenState || (m_bIsFullscreen && (MODE == FULLSCREEN_FULL)));
}
void CWindow::setSuspended(bool suspend) {
if (suspend == m_bSuspended)
return;
@ -1190,7 +1181,7 @@ void CWindow::setAnimationsToMove() {
void CWindow::onWorkspaceAnimUpdate() {
// clip box for animated offsets
if (!m_bIsFloating || m_bPinned || m_bIsFullscreen) {
if (!m_bIsFloating || m_bPinned || isFullscreen()) {
m_vFloatingOffset = Vector2D(0, 0);
return;
}
@ -1244,6 +1235,14 @@ int CWindow::surfacesCount() {
return no;
}
bool CWindow::isFullscreen() {
return m_sFullscreenState.internal != FSMODE_NONE;
}
bool CWindow::isEffectiveInternalFSMode(const eFullscreenMode MODE) {
return (eFullscreenMode)std::bit_floor((uint8_t)m_sFullscreenState.internal) == MODE;
}
int CWindow::workspaceID() {
return m_pWorkspace ? m_pWorkspace->m_iID : m_iLastWorkspace;
}
@ -1321,19 +1320,17 @@ void CWindow::onUpdateState() {
if (requestsFS.has_value() && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) {
bool fs = requestsFS.value();
if (fs != m_bIsFullscreen && m_bIsMapped)
g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_FULL);
if (m_bIsMapped) {
g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_FULLSCREEN, requestsFS.value());
}
if (!m_bIsMapped)
m_bWantsInitialFullscreen = fs;
}
if (requestsMX.has_value() && !(m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) {
bool fs = requestsMX.value();
if (fs != m_bIsFullscreen && m_bIsMapped)
g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_MAXIMIZED);
if (m_bIsMapped)
g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_MAXIMIZED, requestsMX.value());
}
}
@ -1438,7 +1435,7 @@ void CWindow::onX11Configure(CBox box) {
g_pHyprRenderer->damageWindow(m_pSelf.lock());
if (!m_bIsFloating || m_bIsFullscreen || g_pInputManager->currentlyDraggedWindow == m_pSelf) {
if (!m_bIsFloating || isFullscreen() || g_pInputManager->currentlyDraggedWindow == m_pSelf) {
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.goal(), true);
g_pInputManager->refocus();
g_pHyprRenderer->damageWindow(m_pSelf.lock());

View file

@ -16,6 +16,7 @@
#include "Popup.hpp"
#include "Subsurface.hpp"
#include "WLSurface.hpp"
#include "Workspace.hpp"
class CXDGSurfaceResource;
class CXWaylandSurface;
@ -167,8 +168,10 @@ struct SWindowData {
CWindowOverridableVar<bool> noMaxSize = false;
CWindowOverridableVar<bool> noRounding = false;
CWindowOverridableVar<bool> noShadow = false;
CWindowOverridableVar<bool> noShortcutsInhibit = false;
CWindowOverridableVar<bool> opaque = false;
CWindowOverridableVar<bool> RGBX = false;
CWindowOverridableVar<bool> syncFullscreen = true;
CWindowOverridableVar<bool> tearing = false;
CWindowOverridableVar<bool> xray = false;
@ -207,6 +210,11 @@ struct SInitialWorkspaceToken {
std::string workspace;
};
struct sFullscreenState {
eFullscreenMode internal = FSMODE_NONE;
eFullscreenMode client = FSMODE_NONE;
};
class CWindow {
public:
static PHLWINDOW create(SP<CXDGSurfaceResource>);
@ -260,9 +268,8 @@ class CWindow {
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;
sFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
uint64_t m_iMonitorID = -1;
std::string m_szTitle = "";
std::string m_szClass = "";
@ -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&);

View file

@ -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;

View file

@ -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;

View file

@ -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,20 +166,23 @@ 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;
xkbStaticState = nullptr;
if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
xkbStaticState = xkb_state_new(keymap);
xkbState = xkb_state_new(keymap);
return;
}
@ -210,6 +228,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
}
xkbState = xkb_state_new(KEYMAP);
xkbStaticState = xkb_state_new(KEYMAP);
xkb_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT);
@ -231,6 +250,7 @@ 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);
xkbStaticState = xkb_state_new(NEWKEYMAP);
xkb_keymap_unref(NEWKEYMAP);
xkb_context_unref(PCONTEXT);
@ -254,9 +274,9 @@ std::string IKeyboard::getActiveLayout() {
return "none";
}
void IKeyboard::updateLEDs() {
std::optional<uint32_t> IKeyboard::getLEDs() {
if (xkbState == nullptr)
return;
return {};
uint32_t leds = 0;
for (uint32_t i = 0; i < LED_COUNT; ++i) {
@ -264,7 +284,16 @@ void IKeyboard::updateLEDs() {
leds |= (1 << i);
}
updateLEDs(leds);
return leds;
}
void IKeyboard::updateLEDs() {
std::optional<uint32_t> leds = getLEDs();
if (!leds.has_value())
return;
updateLEDs(leds.value());
}
void IKeyboard::updateLEDs(uint32_t leds) {
@ -305,6 +334,13 @@ void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t l
if (!updateModifiersState())
return;
keyboardEvents.modifiers.emit(SModifiersEvent{
.depressed = modifiersState.depressed,
.latched = modifiersState.latched,
.locked = modifiersState.locked,
.group = modifiersState.group,
});
updateLEDs();
}

View file

@ -5,6 +5,7 @@
#include "../macros.hpp"
#include "../helpers/math/Math.hpp"
#include <optional>
#include <xkbcommon/xkbcommon.h>
AQUAMARINE_FORWARD(IKeyboard);
@ -64,16 +65,23 @@ class IKeyboard : public IHID {
void setKeymap(const SStringRuleNames& rules);
void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
std::string getActiveLayout();
std::optional<uint32_t> getLEDs();
void updateLEDs();
void updateLEDs(uint32_t leds);
uint32_t getModifiers();
void updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
bool updateModifiersState(); // rets whether changed
void updateXkbStateWithKey(uint32_t xkbKey, bool pressed);
void updateKeymapFD();
bool active = false;
bool enabled = true;
// 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;
xkb_keymap* xkbKeymap = nullptr;

View file

@ -107,6 +107,7 @@ class IPointer : public IHID {
std::string hlName;
bool connected = false; // means connected to the cursor
std::string boundOutput = "";
WP<IPointer> self;
};

View file

@ -32,13 +32,13 @@ CKeyboard::CKeyboard(SP<Aquamarine::IKeyboard> keeb) : keyboard(keeb) {
listeners.key = keeb->events.key.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IKeyboard::SKeyEvent>(d);
updateXkbStateWithKey(E.key + 8, E.pressed);
keyboardEvents.key.emit(SKeyEvent{
.timeMs = E.timeMs,
.keycode = E.key,
.state = E.pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
});
updateXkbStateWithKey(E.key + 8, E.pressed);
});
listeners.modifiers = keeb->events.modifiers.registerListener([this](std::any d) {

View file

@ -32,7 +32,12 @@ CVirtualKeyboard::CVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keeb_) : keybo
});
listeners.keymap = keeb_->events.keymap.registerListener([this](std::any d) {
auto E = std::any_cast<SKeymapEvent>(d);
if (xkbKeymap)
xkb_keymap_unref(xkbKeymap);
xkbKeymap = xkb_keymap_ref(E.keymap);
keymapOverridden = true;
updateXKBTranslationState(xkbKeymap);
updateKeymapFD();
keyboardEvents.keymap.emit(d);
});

View file

@ -20,7 +20,12 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
});
listeners.motion = pointer->events.move.registerListener([this](std::any d) { pointerEvents.motion.emit(d); });
listeners.motionAbsolute = pointer->events.warp.registerListener([this](std::any d) { pointerEvents.motionAbsolute.emit(d); });
listeners.motionAbsolute = pointer->events.warp.registerListener([this](std::any d) {
// we need to unpack the event and add our device here because it's required to calculate the position correctly
auto E = std::any_cast<SMotionAbsoluteEvent>(d);
E.device = self.lock();
pointerEvents.motionAbsolute.emit(E);
});
listeners.button = pointer->events.button.registerListener([this](std::any d) { pointerEvents.button.emit(d); });
listeners.axis = pointer->events.axis.registerListener([this](std::any d) { pointerEvents.axis.emit(d); });
listeners.frame = pointer->events.frame.registerListener([this](std::any d) { pointerEvents.frame.emit(); });
@ -33,6 +38,8 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
listeners.holdBegin = pointer->events.holdBegin.registerListener([this](std::any d) { pointerEvents.holdBegin.emit(d); });
listeners.holdEnd = pointer->events.holdEnd.registerListener([this](std::any d) { pointerEvents.holdEnd.emit(d); });
boundOutput = resource->boundOutput ? resource->boundOutput->szName : "entire";
deviceName = pointer->name;
}

View file

@ -5,6 +5,8 @@
#include "Events.hpp"
#include "../debug/HyprCtl.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/Screencopy.hpp"
#include "../protocols/ToplevelExport.hpp"
#include <aquamarine/output/Output.hpp>
// --------------------------------------------------------- //
@ -118,8 +120,8 @@ void Events::listener_monitorCommit(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR);
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR);
PROTO::screencopy->onOutputCommit(PMONITOR);
PROTO::toplevelExport->onOutputCommit(PMONITOR);
}
}

View file

@ -10,6 +10,7 @@
#include "../protocols/LayerShell.hpp"
#include "../protocols/XDGShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../xwayland/XSurface.hpp"
#include <hyprutils/string/String.hpp>
@ -133,11 +134,10 @@ 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;
std::optional<eFullscreenMode> requestedInternalFSMode, requestedClientFSMode;
std::optional<sFullscreenState> requestedFSState;
if (PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen))
requestedClientFSMode = FSMODE_FULLSCREEN;
for (auto& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("monitor")) {
@ -198,6 +198,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bIsPseudotiled = true;
} else if (r.szRule.starts_with("noinitialfocus")) {
PWINDOW->m_bNoInitialFocus = true;
} else if (r.szRule.starts_with("fullscreenstate")) {
const auto ARGS = CVarList(r.szRule.substr(r.szRule.find_first_of(' ') + 1), 2, ' ');
int internalMode, clientMode;
try {
internalMode = std::stoi(ARGS[0]);
} catch (std::exception& e) { internalMode = 0; }
try {
clientMode = std::stoi(ARGS[1]);
} catch (std::exception& e) { clientMode = 0; }
requestedFSState = sFullscreenState{.internal = (eFullscreenMode)internalMode, .client = (eFullscreenMode)clientMode};
} else if (r.szRule.starts_with("suppressevent")) {
CVarList vars(r.szRule, 0, 's', true);
for (size_t i = 1; i < vars.size(); ++i) {
@ -212,16 +222,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
else
Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]);
}
} else if (r.szRule == "fullscreen") {
requestsFullscreen = true;
overridingNoFullscreen = true;
} else if (r.szRule == "fakefullscreen") {
requestsFakeFullscreen = true;
} else if (r.szRule == "pin") {
PWINDOW->m_bPinned = true;
} else if (r.szRule == "fullscreen") {
requestedInternalFSMode = FSMODE_FULLSCREEN;
} else if (r.szRule == "maximize") {
requestsMaximize = true;
overridingNoMaximize = true;
requestedInternalFSMode = FSMODE_MAXIMIZED;
} else if (r.szRule == "stayfocused") {
PWINDOW->m_bStayFocused = true;
} else if (r.szRule.starts_with("group")) {
@ -460,17 +466,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock());
if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE)
PWINDOW->m_bNoInitialFocus = true;
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow && !requestsFullscreen && !PWINDOW->m_bIsFloating) {
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow && !requestedInternalFSMode.has_value() && !requestedClientFSMode.has_value() && !PWINDOW->m_bIsFloating) {
if (*PNEWTAKESOVERFS == 0)
PWINDOW->m_bNoInitialFocus = true;
else if (*PNEWTAKESOVERFS == 1)
requestedInternalFSMode = PWINDOW->m_pWorkspace->m_efFullscreenMode;
else if (*PNEWTAKESOVERFS == 2)
g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), false, FULLSCREEN_INVALID);
else if (PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
requestsMaximize = true;
if (*PNEWTAKESOVERFS == 1)
overridingNoMaximize = true;
} else
requestsFullscreen = true;
g_pCompositor->setWindowFullscreenInternal(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), FSMODE_NONE);
}
if (!PWINDOW->m_sWindowData.noFocus.valueOrDefault() && !PWINDOW->m_bNoInitialFocus &&
@ -484,24 +487,27 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_fDimPercent.setValueAndWarp(0);
}
if ((requestsFullscreen && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN) || overridingNoFullscreen)) ||
(requestsMaximize && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) || overridingNoMaximize)) || requestsFakeFullscreen) {
// fix fullscreen on requested (basically do a switcheroo)
if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID);
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, FULLSCREEN_FULL);
}
if (requestedClientFSMode.has_value() && (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN))
requestedClientFSMode = (eFullscreenMode)((uint8_t)requestedClientFSMode.value_or(FSMODE_NONE) & ~(uint8_t)FSMODE_FULLSCREEN);
if (requestedClientFSMode.has_value() && (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE))
requestedClientFSMode = (eFullscreenMode)((uint8_t)requestedClientFSMode.value_or(FSMODE_NONE) & ~(uint8_t)FSMODE_MAXIMIZED);
if (!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);
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 (requestedFSState.has_value()) {
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_WINDOW_RULE);
g_pCompositor->setWindowFullscreenState(PWINDOW, requestedFSState.value());
} else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault())
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()});
else if (requestedInternalFSMode.has_value())
g_pCompositor->setWindowFullscreenInternal(PWINDOW, requestedInternalFSMode.value());
else if (requestedClientFSMode.has_value())
g_pCompositor->setWindowFullscreenClient(PWINDOW, requestedClientFSMode.value());
}
// recheck idle inhibitors
@ -557,7 +563,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
// recalc the values for this window
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
// avoid this window being visible
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFullscreen && !PWINDOW->m_bIsFloating)
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen() && !PWINDOW->m_bIsFloating)
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->scale);
@ -582,8 +588,8 @@ void Events::listener_unmapWindow(void* owner, void* data) {
static auto PEXITRETAINSFS = CConfigValue<Hyprlang::INT>("misc:exit_window_retains_fullscreen");
const auto CURRENTWINDOWFSSTATE = PWINDOW->m_bIsFullscreen;
const auto CURRENTWINDOWFSMODE = PWINDOW->m_pWorkspace->m_efFullscreenMode;
const auto CURRENTWINDOWFSSTATE = PWINDOW->isFullscreen();
const auto CURRENTFSMODE = PWINDOW->m_sFullscreenState.internal;
if (!PWINDOW->m_pWLSurface->exists() || !PWINDOW->m_bIsMapped) {
Debug::log(WARN, "{} unmapped without being mapped??", PWINDOW);
@ -601,10 +607,10 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW);
g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW);
PROTO::toplevelExport->onWindowUnmap(PWINDOW);
if (PWINDOW->m_bIsFullscreen)
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
if (PWINDOW->isFullscreen())
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
// Allow the renderer to catch the last frame.
g_pHyprOpenGL->makeWindowSnapshot(PWINDOW);
@ -626,10 +632,13 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pInputManager->releaseAllMouseButtons();
}
if (PWINDOW == g_pInputManager->currentlyDraggedWindow.lock())
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
// remove the fullscreen window status from workspace if we closed it
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
if (PWORKSPACE->m_bHasFullscreenWindow && PWINDOW->m_bIsFullscreen)
if (PWORKSPACE->m_bHasFullscreenWindow && PWINDOW->isFullscreen())
PWORKSPACE->m_bHasFullscreenWindow = false;
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);
@ -646,7 +655,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow.lock() && PWINDOWCANDIDATE) {
g_pCompositor->focusWindow(PWINDOWCANDIDATE);
if (*PEXITRETAINSFS && CURRENTWINDOWFSSTATE)
g_pCompositor->setWindowFullscreen(PWINDOWCANDIDATE, true, CURRENTWINDOWFSMODE);
g_pCompositor->setWindowFullscreenInternal(PWINDOWCANDIDATE, CURRENTFSMODE);
}
if (!PWINDOWCANDIDATE && g_pCompositor->getWindowsOnWorkspace(PWINDOW->workspaceID()) == 0)
@ -702,14 +711,9 @@ void Events::listener_commitWindow(void* owner, void* data) {
if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden())
return;
if (PWINDOW->m_bIsX11)
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
else if (PWINDOW->m_pPendingSizeAck.has_value()) {
PWINDOW->m_vReportedSize = PWINDOW->m_pPendingSizeAck->second;
PWINDOW->m_pPendingSizeAck.reset();
}
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;

View file

@ -30,8 +30,10 @@ void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
const auto POINTSSIZE = m_aPointsBaked.size() * sizeof(m_aPointsBaked[0]) / 1000.f;
const auto BEGINCALC = std::chrono::high_resolution_clock::now();
for (float i = 0.1f; i < 1.f; i += 0.1f)
for (int j = 1; j < 10; ++j) {
float i = j / 10.0f;
getYForPoint(i);
}
const auto ELAPSEDCALCAVG = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - BEGINCALC).count() / 1000.f / 10.f;
Debug::log(LOG, "Created a bezier curve, baked {} points, mem usage: {:.2f}kB, time to bake: {:.2f}µs. Estimated average calc time: {:.2f}µs.", BAKEDPOINTS, POINTSSIZE,

View file

@ -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();

View file

@ -88,14 +88,14 @@ void CHyprError::createQueued() {
cairo_arc(CAIRO, X + RADIUS, Y + RADIUS, RADIUS, 180 * DEGREES, 270 * DEGREES);
cairo_close_path(CAIRO);
cairo_set_source_rgba(CAIRO, m_cQueued.r, m_cQueued.g, m_cQueued.b, m_cQueued.a);
cairo_set_source_rgba(CAIRO, 0.06, 0.06, 0.06, 1.0);
cairo_fill_preserve(CAIRO);
cairo_set_source_rgba(CAIRO, 0, 0, 0, 1);
cairo_set_source_rgba(CAIRO, m_cQueued.r, m_cQueued.g, m_cQueued.b, m_cQueued.a);
cairo_set_line_width(CAIRO, 2);
cairo_stroke(CAIRO);
// draw the text with a common font
const CColor textColor = m_cQueued.r + m_cQueued.g + m_cQueued.b < 0.2f ? CColor(1.0, 1.0, 1.0, 1.0) : CColor(0, 0, 0, 1.0);
const CColor textColor = CColor(0.9, 0.9, 0.9, 1.0);
cairo_set_source_rgba(CAIRO, textColor.r, textColor.g, textColor.b, textColor.a);
static auto fontFamily = CConfigValue<std::string>("misc:font_family");

View file

@ -136,7 +136,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
return;
}
if (PWINDOW->m_bIsFullscreen && !pNode->ignoreFullscreenChecks)
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
return;
PWINDOW->unsetWindowData(PRIORITY_LAYOUT);
@ -158,8 +158,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
const auto NODESONWORKSPACE = getNodesOnWorkspace(PWINDOW->workspaceID());
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() &&
(NODESONWORKSPACE == 1 || (PWINDOW->m_bIsFullscreen && PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) {
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() && (NODESONWORKSPACE == 1 || PWINDOW->isEffectiveInternalFSMode(FSMODE_MAXIMIZED))) {
PWINDOW->m_sWindowData.decorate = CWindowOverridableVar(true, PRIORITY_LAYOUT);
PWINDOW->m_sWindowData.noBorder = CWindowOverridableVar(*PNOGAPSWHENONLY != 2, PRIORITY_LAYOUT);
@ -178,6 +177,8 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
return;
}
PWINDOW->updateWindowDecos();
auto calcPos = PWINDOW->m_vPosition;
auto calcSize = PWINDOW->m_vSize;
@ -216,7 +217,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
calcPos = calcPos + RESERVED.topLeft;
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->m_bIsFullscreen) {
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) {
// if special, we adjust the coords a bit
static auto PSCALEFACTOR = CConfigValue<Hyprlang::FLOAT>("dwindle:special_scale_factor");
@ -245,8 +246,6 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
g_pHyprRenderer->damageWindow(PWINDOW);
}
PWINDOW->updateWindowDecos();
}
void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection direction) {
@ -500,8 +499,8 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
pWindow->unsetWindowData(PRIORITY_LAYOUT);
pWindow->updateWindowData();
if (pWindow->m_bIsFullscreen)
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
if (pWindow->isFullscreen())
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
const auto PPARENT = PNODE->pParent;
@ -560,10 +559,10 @@ void CHyprDwindleLayout::calculateWorkspace(const PHLWORKSPACE& pWorkspace) {
// massive hack from the fullscreen func
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(pWorkspace->m_iID);
if (pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL) {
if (pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition;
PFULLWINDOW->m_vRealSize = PMONITOR->vecSize;
} else if (pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
} else if (pWorkspace->m_efFullscreenMode == FSMODE_MAXIMIZED) {
SDwindleNodeData fakeNode;
fakeNode.pWindow = PFULLWINDOW;
fakeNode.box = {PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft, PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight};
@ -788,41 +787,19 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
}
}
void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscreenMode fullscreenMode, bool on) {
if (!validMapped(pWindow))
return;
if (on == pWindow->m_bIsFullscreen)
return; // ignore
void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto PWORKSPACE = pWindow->m_pWorkspace;
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
// if the window wants to be fullscreen but there already is one,
// ignore the request.
return;
}
// save position and size if floating
if (pWindow->m_bIsFloating && on) {
if (pWindow->m_bIsFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE) {
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goal();
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goal();
pWindow->m_vPosition = pWindow->m_vRealPosition.goal();
pWindow->m_vSize = pWindow->m_vRealSize.goal();
}
// otherwise, accept it.
pWindow->m_bIsFullscreen = on;
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
pWindow->updateDynamicRules();
pWindow->updateWindowDecos();
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)});
EMIT_HOOK_EVENT("fullscreen", pWindow);
if (!pWindow->m_bIsFullscreen) {
if (EFFECTIVE_MODE == FSMODE_NONE) {
// if it got its fullscreen disabled, set back its node if it had one
const auto PNODE = getNodeFromWindow(pWindow);
if (PNODE)
@ -836,12 +813,8 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscre
pWindow->updateWindowData();
}
} else {
// if it now got fullscreen, make it fullscreen
PWORKSPACE->m_efFullscreenMode = fullscreenMode;
// apply new pos and size being monitors' box
if (fullscreenMode == FULLSCREEN_FULL) {
if (EFFECTIVE_MODE == FSMODE_FULLSCREEN) {
pWindow->m_vRealPosition = PMONITOR->vecPosition;
pWindow->m_vRealSize = PMONITOR->vecSize;
} else {
@ -861,13 +834,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscre
}
}
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goal());
g_pCompositor->changeWindowZOrder(pWindow, true);
recalculateMonitor(PMONITOR->ID);
}
void CHyprDwindleLayout::recalculateWindow(PHLWINDOW pWindow) {
@ -957,13 +924,11 @@ void CHyprDwindleLayout::switchWindows(PHLWINDOW pWindow, PHLWINDOW pWindow2) {
if (!PNODE2 || !PNODE)
return;
const bool FS1 = pWindow->m_bIsFullscreen;
const bool FS2 = pWindow2->m_bIsFullscreen;
const eFullscreenMode MODE1 = pWindow->m_sFullscreenState.internal;
const eFullscreenMode MODE2 = pWindow2->m_sFullscreenState.internal;
if (FS1)
g_pCompositor->setWindowFullscreen(pWindow, false);
if (FS2)
g_pCompositor->setWindowFullscreen(pWindow2, false);
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
g_pCompositor->setWindowFullscreenInternal(pWindow2, FSMODE_NONE);
SDwindleNodeData* ACTIVE1 = nullptr;
SDwindleNodeData* ACTIVE2 = nullptr;
@ -1001,10 +966,8 @@ void CHyprDwindleLayout::switchWindows(PHLWINDOW pWindow, PHLWINDOW pWindow2) {
g_pHyprRenderer->damageWindow(pWindow);
g_pHyprRenderer->damageWindow(pWindow2);
if (FS1)
g_pCompositor->setWindowFullscreen(pWindow2, true);
if (FS2)
g_pCompositor->setWindowFullscreen(pWindow, true);
g_pCompositor->setWindowFullscreenInternal(pWindow2, MODE1);
g_pCompositor->setWindowFullscreenInternal(pWindow, MODE2);
}
void CHyprDwindleLayout::alterSplitRatio(PHLWINDOW pWindow, float ratio, bool exact) {
@ -1072,7 +1035,7 @@ void CHyprDwindleLayout::toggleSplit(PHLWINDOW pWindow) {
if (!PNODE || !PNODE->pParent)
return;
if (pWindow->m_bIsFullscreen)
if (pWindow->isFullscreen())
return;
PNODE->pParent->splitTop = !PNODE->pParent->splitTop;
@ -1086,7 +1049,7 @@ void CHyprDwindleLayout::swapSplit(PHLWINDOW pWindow) {
if (!PNODE || !PNODE->pParent)
return;
if (pWindow->m_bIsFullscreen)
if (pWindow->isFullscreen())
return;
std::swap(PNODE->pParent->children[0], PNODE->pParent->children[1]);

View file

@ -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);

View file

@ -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();

View file

@ -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

View file

@ -267,7 +267,8 @@ void CHyprMasterLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
pWindow->unsetWindowData(PRIORITY_LAYOUT);
pWindow->updateWindowData();
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
if (pWindow->isFullscreen())
g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE);
if (PNODE->isMaster && (MASTERSLEFT <= 1 || *SMALLSPLIT == 1)) {
// find a new master from top of the list
@ -327,10 +328,10 @@ void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) {
// massive hack from the fullscreen func
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(pWorkspace->m_iID);
if (pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL) {
if (pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition;
PFULLWINDOW->m_vRealSize = PMONITOR->vecSize;
} else if (pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
} else if (pWorkspace->m_efFullscreenMode == FSMODE_MAXIMIZED) {
SMasterNodeData fakeNode;
fakeNode.pWindow = PFULLWINDOW;
fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
@ -644,7 +645,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
// if user specified them in config
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(PWINDOW->m_pWorkspace);
if (PWINDOW->m_bIsFullscreen && !pNode->ignoreFullscreenChecks)
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
return;
PWINDOW->unsetWindowData(PRIORITY_LAYOUT);
@ -668,8 +669,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
PWINDOW->m_vSize = pNode->size;
PWINDOW->m_vPosition = pNode->position;
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() &&
(getNodesOnWorkspace(PWINDOW->workspaceID()) == 1 || (PWINDOW->m_bIsFullscreen && PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) {
if (*PNOGAPSWHENONLY && !PWINDOW->onSpecialWorkspace() && (getNodesOnWorkspace(PWINDOW->workspaceID()) == 1 || PWINDOW->isEffectiveInternalFSMode(FSMODE_MAXIMIZED))) {
PWINDOW->m_sWindowData.decorate = CWindowOverridableVar(true, PRIORITY_LAYOUT);
PWINDOW->m_sWindowData.noBorder = CWindowOverridableVar(*PNOGAPSWHENONLY != 2, PRIORITY_LAYOUT);
@ -688,6 +688,8 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
return;
}
PWINDOW->updateWindowDecos();
auto calcPos = PWINDOW->m_vPosition;
auto calcSize = PWINDOW->m_vSize;
@ -702,7 +704,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
calcPos = calcPos + RESERVED.topLeft;
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->m_bIsFullscreen) {
if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) {
static auto PSCALEFACTOR = CConfigValue<Hyprlang::FLOAT>("master:special_scale_factor");
CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR};
@ -730,8 +732,6 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
g_pHyprRenderer->damageWindow(PWINDOW);
}
PWINDOW->updateWindowDecos();
}
bool CHyprMasterLayout::isWindowTiled(PHLWINDOW pWindow) {
@ -880,41 +880,19 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne
m_bForceWarps = false;
}
void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscreenMode fullscreenMode, bool on) {
if (!validMapped(pWindow))
return;
if (on == pWindow->m_bIsFullscreen)
return; // ignore
void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto PWORKSPACE = pWindow->m_pWorkspace;
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
// if the window wants to be fullscreen but there already is one,
// ignore the request.
return;
}
// save position and size if floating
if (pWindow->m_bIsFloating && on) {
if (pWindow->m_bIsFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE) {
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goal();
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goal();
pWindow->m_vPosition = pWindow->m_vRealPosition.goal();
pWindow->m_vSize = pWindow->m_vRealSize.goal();
}
// otherwise, accept it.
pWindow->m_bIsFullscreen = on;
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
pWindow->updateDynamicRules();
pWindow->updateWindowDecos();
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)});
EMIT_HOOK_EVENT("fullscreen", pWindow);
if (!pWindow->m_bIsFullscreen) {
if (EFFECTIVE_MODE == FSMODE_NONE) {
// if it got its fullscreen disabled, set back its node if it had one
const auto PNODE = getNodeFromWindow(pWindow);
if (PNODE)
@ -928,12 +906,8 @@ void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscree
pWindow->updateWindowData();
}
} else {
// if it now got fullscreen, make it fullscreen
PWORKSPACE->m_efFullscreenMode = fullscreenMode;
// apply new pos and size being monitors' box
if (fullscreenMode == FULLSCREEN_FULL) {
if (EFFECTIVE_MODE == FSMODE_FULLSCREEN) {
pWindow->m_vRealPosition = PMONITOR->vecPosition;
pWindow->m_vRealSize = PMONITOR->vecSize;
} else {
@ -954,13 +928,7 @@ void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, eFullscree
}
}
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goal());
g_pCompositor->changeWindowZOrder(pWindow, true);
recalculateMonitor(PMONITOR->ID);
}
void CHyprMasterLayout::recalculateWindow(PHLWINDOW pWindow) {
@ -1081,14 +1049,14 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
if (!validMapped(PWINDOWTOCHANGETO))
return;
if (header.pWindow->m_bIsFullscreen) {
if (header.pWindow->isFullscreen()) {
const auto PWORKSPACE = header.pWindow->m_pWorkspace;
const auto FSMODE = PWORKSPACE->m_efFullscreenMode;
const auto FSMODE = header.pWindow->m_sFullscreenState.internal;
static auto INHERITFULLSCREEN = CConfigValue<Hyprlang::INT>("master:inherit_fullscreen");
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
if (*INHERITFULLSCREEN)
g_pCompositor->setWindowFullscreen(PWINDOWTOCHANGETO, true, FSMODE);
g_pCompositor->setWindowFullscreenInternal(PWINDOWTOCHANGETO, FSMODE);
} else {
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
g_pCompositor->warpCursorTo(PWINDOWTOCHANGETO->middle());
@ -1208,7 +1176,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, true);
if (PWINDOWTOSWAPWITH) {
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
switchWindows(header.pWindow, PWINDOWTOSWAPWITH);
switchToWindow(header.pWindow);
}
@ -1224,7 +1192,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, false);
if (PWINDOWTOSWAPWITH) {
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenClient(header.pWindow, FSMODE_NONE);
switchWindows(header.pWindow, PWINDOWTOSWAPWITH);
switchToWindow(header.pWindow);
}
@ -1243,7 +1211,8 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
if (MASTERS + 2 > WINDOWS && *SMALLSPLIT == 0)
return 0;
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
if (!PNODE || PNODE->isMaster) {
// first non-master node
@ -1275,7 +1244,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
if (WINDOWS < 2 || MASTERS < 2)
return 0;
g_pCompositor->setWindowFullscreen(header.pWindow, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE);
if (!PNODE || !PNODE->isMaster) {
// first non-master node
@ -1296,7 +1265,7 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
if (!PWINDOW)
return 0;
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->workspaceID());
@ -1392,7 +1361,7 @@ void CHyprMasterLayout::runOrientationCycle(SLayoutMessageHeader& header, CVarLi
if (!PWINDOW)
return;
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->workspaceID());

View file

@ -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);

View file

@ -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;
}

View file

@ -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");
if (!xcursor.defaultCursor) {
g_pPointerManager->resetCursorImage();
return;
}
}
auto& icon = xcursor.defaultCursor;
// try to get an icon we know if we have one
if (xcursor.cursors.contains(name))
icon = xcursor.cursors.at(name);
for (auto const& c : xcursor.cursors) {
if (c.first != name)
continue;
icon = c.second;
}
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>((uint8_t*)icon->pixels.data(), icon->size, icon->hotspot));
@ -305,87 +311,86 @@ bool CCursorManager::changeTheme(const std::string& name, const int size) {
}
// Taken from https://gitlab.freedesktop.org/xorg/lib/libxcursor/-/blob/master/src/library.c
// however modified to fit wayland cursor shape names better.
// _ -> -
// clang-format off
static std::array<const char*, 77> XCURSOR_STANDARD_NAMES = {
"X_cursor",
"default", // arrow
"ns-resize", // based-arrow-down
"ns-resize", // based-arrow-up
"arrow",
"based_arrow_down",
"based_arrow_up",
"boat",
"bogosity",
"sw-resize", // bottom-left-corner
"se-resize", // bottom-right-corner
"s-resize", // bottom-side
"bottom-tee",
"box-spiral",
"center-ptr",
"bottom_left_corner",
"bottom_right_corner",
"bottom_side",
"bottom_tee",
"box_spiral",
"center_ptr",
"circle",
"clock",
"coffee-mug",
"coffee_mug",
"cross",
"cross-reverse",
"cross_reverse",
"crosshair",
"diamond-cross",
"diamond_cross",
"dot",
"dotbox",
"double-arrow",
"draft-large",
"draft-small",
"draped-box",
"double_arrow",
"draft_large",
"draft_small",
"draped_box",
"exchange",
"move", // fleur
"fleur",
"gobbler",
"gumby",
"pointer", // hand1
"grabbing", // hand2
"hand1",
"hand2",
"heart",
"icon",
"iron-cross",
"default", // left-ptr
"w-resize", // left-side
"left-tee",
"iron_cross",
"left_ptr",
"left_side",
"left_tee",
"leftbutton",
"ll-angle",
"lr-angle",
"ll_angle",
"lr_angle",
"man",
"middlebutton",
"mouse",
"pencil",
"pirate",
"plus",
"help", // question-arrow
"right-ptr",
"e-resize", // right-side
"right-tee",
"question_arrow",
"right_ptr",
"right_side",
"right_tee",
"rightbutton",
"rtl-logo",
"rtl_logo",
"sailboat",
"ns-resize", // sb-down-arrow
"ew-resize", // sb-h-double-arrow
"ew-resize", // sb-left-arrow
"ew-resize", // sb-right-arrow
"n-resize", // sb-up-arrow
"s-resize", // sb-v-double-arrow
"sb_down_arrow",
"sb_h_double_arrow",
"sb_left_arrow",
"sb_right_arrow",
"sb_up_arrow",
"sb_v_double_arrow",
"shuttle",
"sizing",
"spider",
"spraycan",
"star",
"target",
"cell", // tcross
"nw-resize", // top-left-arrow
"nw-resize", // top-left-corner
"ne-resize", // top-right-corner
"n-resize", // top-side
"top-tee",
"tcross",
"top_left_arrow",
"top_left_corner",
"top_right_corner",
"top_side",
"top_tee",
"trek",
"ul-angle",
"ul_angle",
"umbrella",
"ur-angle",
"wait", // watch
"text", // xterm
"ur_angle",
"watch",
"xterm",
};
// clang-format on
@ -397,52 +402,22 @@ void CCursorManager::SXCursorManager::loadTheme(const std::string& name, int siz
themeLoaded = false;
themeName = name.empty() ? "default" : name;
auto img = XcursorShapeLoadImage(2, themeName.c_str(), size);
std::vector<std::pair<std::string, SP<SXCursor>>> newCursors;
if (!img) {
Debug::log(ERR, "XCursor failed finding theme \"{}\". Trying size 24.", themeName);
size = 24;
img = XcursorShapeLoadImage(2, themeName.c_str(), size);
if (!img) {
Debug::log(ERR, "XCursor failed finding theme \"{}\".", themeName);
return;
}
}
defaultCursor = makeShared<SXCursor>();
defaultCursor->size = {(int)img->width, (int)img->height};
defaultCursor->hotspot = {(int)img->xhot, (int)img->yhot};
defaultCursor->pixels.resize(img->width * img->height);
std::memcpy(defaultCursor->pixels.data(), img->pixels, img->width * img->height * sizeof(uint32_t));
themeLoaded = true;
XcursorImageDestroy(img);
// gather as many shapes as we can find.
cursors.clear();
for (auto& shape : CURSOR_SHAPE_NAMES) {
int id = -1;
// load the default xcursor shapes that exist in the theme
for (size_t i = 0; i < XCURSOR_STANDARD_NAMES.size(); ++i) {
if (XCURSOR_STANDARD_NAMES.at(i) == std::string{shape}) {
id = i;
break;
}
}
auto shape = XCURSOR_STANDARD_NAMES.at(i);
auto xImage = XcursorShapeLoadImage(i << 1 /* wtf xcursor? */, themeName.c_str(), size);
if (id < 0) {
Debug::log(LOG, "XCursor has no shape {}, skipping", shape);
continue;
}
auto xImage = XcursorShapeLoadImage(id << 1 /* wtf xcursor? */, themeName.c_str(), size);
if (!xImage) {
Debug::log(LOG, "XCursor failed to find a shape with name {}, trying size 24.", shape);
xImage = XcursorShapeLoadImage(i << 1 /* wtf xcursor? */, themeName.c_str(), 24);
if (!xImage) {
Debug::log(LOG, "XCursor failed to find a shape with name {}, skipping", shape);
continue;
}
}
auto xcursor = makeShared<SXCursor>();
xcursor->size = {(int)xImage->width, (int)xImage->height};
@ -451,8 +426,214 @@ void CCursorManager::SXCursorManager::loadTheme(const std::string& name, int siz
xcursor->pixels.resize(xImage->width * xImage->height);
std::memcpy(xcursor->pixels.data(), xImage->pixels, xImage->width * xImage->height * sizeof(uint32_t));
cursors.emplace(std::string{shape}, xcursor);
newCursors.emplace_back(std::string{shape}, xcursor);
XcursorImageDestroy(xImage);
}
if (newCursors.empty()) {
Debug::log(ERR, "XCursor failed finding any shapes in theme \"{}\".", themeName);
if (!defaultCursor) {
defaultCursor = createDefaultCursor();
}
return;
}
cursors.clear();
cursors = std::move(newCursors);
defaultCursor.reset();
themeLoaded = true;
for (auto const& shape : CURSOR_SHAPE_NAMES) {
auto legacyName = getLegacyShapeName(shape);
if (legacyName.empty())
continue;
auto it = std::find_if(cursors.begin(), cursors.end(), [&legacyName](auto const& c) { return c.first == legacyName; });
if (it == cursors.end()) {
Debug::log(LOG, "XCursor failed to find a legacy shape with name {}, skipping", legacyName);
continue;
}
cursors.emplace_back(shape, it->second);
}
// set default cursor
for (auto const& c : cursors) {
if (c.first == "left_ptr" || c.first == "arrow") {
defaultCursor = c.second;
break;
}
}
// just assign the first one then.
if (!defaultCursor)
defaultCursor = cursors.at(0).second;
}
std::string CCursorManager::SXCursorManager::getLegacyShapeName(std::string const& shape) {
if (shape == "invalid")
return std::string();
else if (shape == "default")
return "left_ptr";
else if (shape == "context-menu")
return "left_ptr";
else if (shape == "help")
return "left_ptr";
else if (shape == "pointer")
return "hand2";
else if (shape == "progress")
return "watch";
else if (shape == "wait")
return "watch";
else if (shape == "cell")
return "plus";
else if (shape == "crosshair")
return "cross";
else if (shape == "text")
return "xterm";
else if (shape == "vertical-text")
return "xterm";
else if (shape == "alias")
return "dnd-link";
else if (shape == "copy")
return "dnd-copy";
else if (shape == "move")
return "dnd-move";
else if (shape == "no-drop")
return "dnd-none";
else if (shape == "not-allowed")
return "crossed_circle";
else if (shape == "grab")
return "hand1";
else if (shape == "grabbing")
return "hand1";
else if (shape == "e-resize")
return "right_side";
else if (shape == "n-resize")
return "top_side";
else if (shape == "ne-resize")
return "top_right_corner";
else if (shape == "nw-resize")
return "top_left_corner";
else if (shape == "s-resize")
return "bottom_side";
else if (shape == "se-resize")
return "bottom_right_corner";
else if (shape == "sw-resize")
return "bottom_left_corner";
else if (shape == "w-resize")
return "left_side";
else if (shape == "ew-resize")
return "sb_h_double_arrow";
else if (shape == "ns-resize")
return "sb_v_double_arrow";
else if (shape == "nesw-resize")
return "fd_double_arrow";
else if (shape == "nwse-resize")
return "bd_double_arrow";
else if (shape == "col-resize")
return "sb_h_double_arrow";
else if (shape == "row-resize")
return "sb_v_double_arrow";
else if (shape == "all-scroll")
return "fleur";
else if (shape == "zoom-in")
return "left_ptr";
else if (shape == "zoom-out")
return "left_ptr";
return std::string();
}
SP<CCursorManager::SXCursor> CCursorManager::SXCursorManager::createDefaultCursor() {
int cursorWidth = 32;
int cursorHeight = 32;
int cursorHotspotX = 3;
int cursorHotspotY = 2;
static const uint32_t pixels[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1b001816, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8e008173, 0x5f00564d, 0x16001412, 0x09000807, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2b002624, 0x05000404, 0x00000000, 0x35002f2b, 0xd400bead,
0xc300b09e, 0x90008275, 0x44003e37, 0x04000403, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x67005a56,
0x6f00615c, 0x00000000, 0x00000000, 0x8b007c72, 0xf200d7c6, 0xfa00e0cc, 0xe800d0bd, 0xa0009181, 0x44003e37, 0x1a001815, 0x06000505, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x8d007976, 0xd600b8b3, 0x2500201f, 0x00000000, 0x17001413, 0xbd00a79c, 0xf600dacb, 0xff00e3d1, 0xfc00e1ce, 0xe800d0bc, 0xbf00ac9b,
0x95008778, 0x51004a41, 0x0f000e0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x92007b7b, 0xf500d0cf, 0x9e008685, 0x00000000, 0x00000000, 0x23001f1d, 0x64005853,
0x9b008980, 0xd900bfb3, 0xfb00dfce, 0xff00e4d0, 0xfb00e1cd, 0xec00d5c0, 0xa7009788, 0x47004139, 0x1e001b18, 0x05000504, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xa200878a, 0xff00d6d9, 0xd600b4b5,
0x0e000c0c, 0x00000000, 0x00000000, 0x02000202, 0x0c000b0a, 0x30002a28, 0x8e007d75, 0xd600bdb0, 0xef00d4c4, 0xfb00e0ce, 0xff00e4d0, 0xe600cfbb, 0xb800a695, 0x5f00564d,
0x06000505, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x02000202, 0xc600a3aa, 0xff00d3da, 0xea00c3c8, 0x08000707, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000101, 0x2a002523, 0x61005550, 0x9500837b,
0xd800bfb1, 0xfd00e1cf, 0xff00e5d0, 0xf500dcc7, 0x7c007065, 0x2a002622, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x06000505, 0xd600aeb9, 0xff00d0dc, 0xcc00a7af, 0x04000303, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x01000101, 0x01000101, 0x2c002724, 0xa1008e85, 0xe600ccbd, 0xf800ddcb, 0xef00d6c3, 0xc300af9f, 0x2c002824, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x09000708, 0xd800adbc, 0xff00cdde, 0xb90095a0, 0x02000202, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000e0d, 0x4b00423e, 0xa4009088, 0xfd00dfd0, 0xff00e3d1,
0xae009c8f, 0x42003b36, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x14001012, 0xf400c0d6,
0xff00cadf, 0xb2008e9c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x02000202, 0x1200100f, 0xa2008e86, 0xec00cfc3, 0xfc00ded0, 0xc300ada0, 0x15001311, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x2e002429, 0xfd00c4e0, 0xff00c7e2, 0x8f00707e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1e001a19, 0x75006662, 0xfb00dbd1, 0xf700d9cc, 0x9600847c, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3e002f37, 0xfc00c1e1, 0xff00c5e3, 0x60004b55, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x15001212, 0xa6008f8b, 0xff00ddd5,
0xf800d8ce, 0x36002f2d, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x51003d49, 0xfe00bfe5, 0xfe00c1e4, 0x4c003a44,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x01000101, 0x1d001918, 0xf400d1cd, 0xfe00dad5, 0xb3009a96, 0x03000303, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x66004b5d, 0xff00bee7, 0xfd00bee5, 0x4500343f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000202, 0x82006e6e, 0xff00d8d7, 0xd800b9b6, 0x33002c2b, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x70005267, 0xff00bbe9, 0xfa00b8e3, 0x29001e25, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3f003536, 0xea00c3c7, 0xf800d1d3,
0x4a003e3f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5f004458, 0xff00b8eb, 0xf400b1e0, 0x29001e26, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x1b001617, 0xe100bac0, 0xff00d4da, 0x82006c6f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5a004054, 0xfe00b4eb,
0xfb00b3e8, 0x3b002a36, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0a000809, 0xc900a4ad, 0xff00d1dc, 0x88007075, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x44002f3f, 0xf300aae3, 0xfc00b1ea, 0x48003343, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05000404, 0xdf00b3c2, 0xff00cedd, 0x8f00747c, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x25001a23, 0xf200a6e4, 0xff00b1ef, 0x84005c7c, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x09000708,
0xe400b5c8, 0xff00cbdf, 0x78006068, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x12000c11, 0xc00082b6, 0xff00aef1, 0xaa0075a0,
0x11000c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x1e00171b, 0xf700c1db, 0xff00c8e1, 0x4b003b42, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x05000305, 0x8d005f86, 0xfc00aaef, 0xed00a0e1, 0x26001a24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6f005463, 0xff00c4e3, 0xf500beda, 0x0c00090b, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3e00293b, 0xed009de2, 0xff00aaf3, 0x8b005d84, 0x04000304, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000506, 0xeb00b1d4, 0xff00c1e6, 0xba008ea7,
0x02000202, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xb20075ab, 0xff00a7f4, 0xf300a1e9, 0x35002333,
0x06000406, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x8900647d, 0xff00bde8, 0xfb00bce2, 0x26001d22, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x2b001c29, 0xf1009ce8, 0xfe00a5f4, 0xc60082be, 0x3b002738, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x01000101, 0x54003c4d, 0xfd00b8e8, 0xff00baea, 0x96006f89, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x78004d74, 0xe20091da, 0xff00a5f6, 0xb60077af, 0x42002b3f, 0x0c00080c, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x9400688a, 0xff00b5ed, 0xff00b6ec, 0xcc0093bc, 0x02000102, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000304, 0x8100527d, 0xeb0096e4, 0xf900a0f1, 0xdc008dd4,
0x9b006595, 0x32002130, 0x0a00070a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2100161f, 0x5300394e, 0xe2009cd4, 0xff00b1ef, 0xff00b2ee, 0xc8008cba, 0x17001015,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x19001018, 0x5e003b5b, 0xcb0081c5, 0xff00a2f8, 0xfc00a1f4, 0xde0090d6, 0xa9006da3, 0x8300567e, 0x6f00496a, 0x76004e71, 0xbb007db2, 0xeb009edf, 0xfb00a9ee, 0xff00adf1,
0xfd00adee, 0x9e006d95, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000304, 0x34002133, 0xa60069a1, 0xd70089d1, 0xf2009bea, 0xff00a3f7, 0xfb00a1f2, 0xfb00a2f2, 0xff00a6f5,
0xff00a8f4, 0xfd00a7f2, 0xed009ee2, 0xcf008bc5, 0x14000e13, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000b11, 0x35002134, 0x5b003959,
0x8b005887, 0xb30072ae, 0xc90080c3, 0xd10086ca, 0xa6006ba0, 0x65004261, 0x3c002839, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x01000101, 0x06000406, 0x0d00080d, 0x0f000a0f, 0x0a00060a, 0x01000101, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000};
auto hackcursor = makeShared<SXCursor>();
hackcursor->size = {cursorWidth, cursorHeight};
hackcursor->hotspot = {cursorHotspotX, cursorHotspotY};
hackcursor->pixels.resize(cursorWidth * cursorHeight);
std::memcpy(hackcursor->pixels.data(), pixels, cursorWidth * cursorHeight * sizeof(uint32_t));
return hackcursor;
}

View file

@ -84,13 +84,14 @@ class CCursorManager {
struct SXCursorManager {
void loadTheme(const std::string& name, int size);
std::string getLegacyShapeName(std::string const& shape);
SP<SXCursor> createDefaultCursor();
int lastLoadSize = 0;
bool themeLoaded = false;
std::string themeName = "";
SP<SXCursor> defaultCursor;
std::unordered_map<std::string, SP<SXCursor>> cursors;
std::vector<std::pair<std::string, SP<SXCursor>>> cursors;
} xcursor;
};

View file

@ -3,6 +3,7 @@
#include "../managers/SeatManager.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/ShortcutsInhibit.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "../devices/IKeyboard.hpp"
#include "KeybindManager.hpp"
@ -36,7 +37,7 @@ using namespace Hyprutils::String;
static std::vector<std::pair<std::string, std::string>> getHyprlandLaunchEnv() {
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
if (!*PINITIALWSTRACKING)
if (!*PINITIALWSTRACKING || g_pConfigManager->isLaunchingExecOnce)
return {};
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
@ -67,7 +68,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["workspace"] = changeworkspace;
m_mDispatchers["renameworkspace"] = renameWorkspace;
m_mDispatchers["fullscreen"] = fullscreenActive;
m_mDispatchers["fakefullscreen"] = fakeFullscreenActive;
m_mDispatchers["fullscreenstate"] = fullscreenStateActive;
m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace;
m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent;
m_mDispatchers["pseudo"] = toggleActivePseudo;
@ -101,7 +102,6 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["pass"] = pass;
m_mDispatchers["sendshortcut"] = sendshortcut;
m_mDispatchers["layoutmsg"] = layoutmsg;
m_mDispatchers["toggleopaque"] = toggleOpaque;
m_mDispatchers["dpms"] = dpms;
m_mDispatchers["movewindowpixel"] = moveWindow;
m_mDispatchers["resizewindowpixel"] = resizeWindow;
@ -243,27 +243,14 @@ void CKeybindManager::updateXKBTranslationState() {
}
bool CKeybindManager::ensureMouseBindState() {
if (!m_bIsMouseBindActive)
if (!g_pInputManager->currentlyDraggedWindow)
return false;
if (!g_pInputManager->currentlyDraggedWindow.expired()) {
PHLWINDOW lastDraggedWindow = g_pInputManager->currentlyDraggedWindow.lock();
m_bIsMouseBindActive = false;
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
g_pInputManager->currentlyDraggedWindow.reset();
g_pInputManager->dragMode = MBIND_INVALID;
g_pCompositor->updateWorkspaceWindows(lastDraggedWindow->workspaceID());
g_pCompositor->updateWorkspaceWindowData(lastDraggedWindow->workspaceID());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(lastDraggedWindow->m_iMonitorID);
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
changeMouseBindMode(MBIND_INVALID);
return true;
}
m_bIsMouseBindActive = false;
return false;
}
@ -282,11 +269,16 @@ 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;
}
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PNOWARPS = CConfigValue<Hyprlang::INT>("cursor:no_warps");
const auto PWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace;
const auto PNEWMAINWORKSPACE = monitor->activeWorkspace;
@ -301,9 +293,11 @@ bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) {
g_pCompositor->focusWindow(PNEWWINDOW);
PNEWWINDOW->warpCursor();
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,6 +308,9 @@ bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) {
}
void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO) {
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PNOWARPS = CConfigValue<Hyprlang::INT>("cursor:no_warps");
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (PWINDOWTOCHANGETO == PLASTWINDOW || !PWINDOWTOCHANGETO)
@ -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();
// Move mouse focus to the new window if required by current follow_mouse and warp modes
if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) {
g_pInputManager->m_pForcedFocus = PWINDOWTOCHANGETO;
g_pInputManager->simulateMouseMovement();
g_pInputManager->m_pForcedFocus.reset();
}
if (PLASTWINDOW && PLASTWINDOW->m_iMonitorID != PWINDOWTOCHANGETO->m_iMonitorID) {
// event
@ -528,10 +528,7 @@ bool CKeybindManager::onMouseEvent(const IPointer::SButtonEvent& e) {
}
void CKeybindManager::resizeWithBorder(const IPointer::SButtonEvent& e) {
if (e.state == WL_POINTER_BUTTON_STATE_PRESSED)
mouse("1resizewindow");
else
mouse("0resizewindow");
changeMouseBindMode(e.state == WL_POINTER_BUTTON_STATE_PRESSED ? MBIND_RESIZE : MBIND_INVALID);
}
void CKeybindManager::onSwitchEvent(const std::string& switchName) {
@ -607,10 +604,8 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
static auto PDISABLEINHIBIT = CConfigValue<Hyprlang::INT>("binds:disable_keybind_grabbing");
if (!*PDISABLEINHIBIT && PROTO::shortcutsInhibit->isInhibited()) {
if (!*PDISABLEINHIBIT && PROTO::shortcutsInhibit->isInhibited())
Debug::log(LOG, "Keybind handling is disabled due to an inhibitor");
return false;
}
for (auto& k : m_lKeybinds) {
const bool SPECIALDISPATCHER = k.handler == "global" || k.handler == "pass" || k.handler == "sendshortcut" || k.handler == "mouse";
@ -619,6 +614,9 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
const bool IGNORECONDITIONS =
SPECIALDISPATCHER && !pressed && SPECIALTRIGGERED; // ignore mods. Pass, global dispatchers should be released immediately once the key is released.
if (!k.dontInhibit && !*PDISABLEINHIBIT && PROTO::shortcutsInhibit->isInhibited())
continue;
if (!k.locked && g_pSessionLockManager->isSessionLocked())
continue;
@ -958,7 +956,8 @@ static void toggleActiveFloatingCore(std::string args, std::optional<bool> float
return;
// remove drag status
g_pInputManager->currentlyDraggedWindow.reset();
if (!g_pInputManager->currentlyDraggedWindow.expired())
g_pKeybindManager->changeMouseBindMode(MBIND_INVALID);
if (PWINDOW->m_sGroupData.pNextWindow.lock() && PWINDOW->m_sGroupData.pNextWindow.lock() != PWINDOW) {
const auto PCURRENT = PWINDOW->getGroupCurrent();
@ -997,7 +996,7 @@ void CKeybindManager::setActiveTiled(std::string args) {
void CKeybindManager::centerWindow(std::string args) {
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PWINDOW || !PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
if (!PWINDOW || !PWINDOW->m_bIsFloating || PWINDOW->isFullscreen())
return;
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
@ -1023,7 +1022,7 @@ void CKeybindManager::toggleActivePseudo(std::string args) {
PWINDOW->m_bIsPseudotiled = !PWINDOW->m_bIsPseudotiled;
if (!PWINDOW->m_bIsFullscreen)
if (!PWINDOW->isFullscreen())
g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW);
}
@ -1150,10 +1149,52 @@ void CKeybindManager::fullscreenActive(std::string args) {
if (!PWINDOW)
return;
PWINDOW->m_bDontSendFullscreen = false;
if (args == "2")
PWINDOW->m_bDontSendFullscreen = true;
g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, args == "1" ? FULLSCREEN_MAXIMIZED : FULLSCREEN_FULL);
const eFullscreenMode MODE = args == "1" ? FSMODE_MAXIMIZED : FSMODE_FULLSCREEN;
if (PWINDOW->isEffectiveInternalFSMode(MODE))
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
else
g_pCompositor->setWindowFullscreenInternal(PWINDOW, MODE);
}
void CKeybindManager::fullscreenStateActive(std::string args) {
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
const auto ARGS = CVarList(args, 2, ' ');
if (!PWINDOW)
return;
int internalMode, clientMode;
try {
internalMode = std::stoi(ARGS[0]);
} catch (std::exception& e) { internalMode = -1; }
try {
clientMode = std::stoi(ARGS[1]);
} catch (std::exception& e) { clientMode = -1; }
const sFullscreenState STATE = sFullscreenState{.internal = (internalMode != -1 ? (eFullscreenMode)internalMode : PWINDOW->m_sFullscreenState.internal),
.client = (clientMode != -1 ? (eFullscreenMode)clientMode : PWINDOW->m_sFullscreenState.client)};
if (internalMode != -1 && clientMode != -1 && PWINDOW->m_sFullscreenState.internal == STATE.internal && PWINDOW->m_sFullscreenState.client == STATE.client) {
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = FSMODE_NONE, .client = FSMODE_NONE});
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(true, PRIORITY_SET_PROP);
return;
}
if (internalMode != -1 && clientMode == -1 && PWINDOW->m_sFullscreenState.internal == STATE.internal) {
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = PWINDOW->m_sFullscreenState.client, .client = PWINDOW->m_sFullscreenState.client});
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(true, PRIORITY_SET_PROP);
return;
}
if (internalMode == -1 && clientMode != -1 && PWINDOW->m_sFullscreenState.client == STATE.client) {
g_pCompositor->setWindowFullscreenState(PWINDOW, sFullscreenState{.internal = PWINDOW->m_sFullscreenState.internal, .client = PWINDOW->m_sFullscreenState.internal});
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(true, PRIORITY_SET_PROP);
return;
}
PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_SET_PROP);
g_pCompositor->setWindowFullscreenState(PWINDOW, STATE);
}
void CKeybindManager::moveActiveToWorkspace(std::string args) {
@ -1278,7 +1319,7 @@ void CKeybindManager::moveFocusTo(std::string args) {
return;
}
const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->m_bIsFullscreen ?
const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->isFullscreen() ?
(arg == 'd' || arg == 'b' || arg == 'r' ? g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, true) : g_pCompositor->getPrevWindowOnWorkspace(PLASTWINDOW, true)) :
g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
@ -1335,7 +1376,7 @@ void CKeybindManager::swapActive(std::string args) {
Debug::log(LOG, "Swapping active window in direction {}", arg);
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
return;
const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
@ -1373,7 +1414,7 @@ void CKeybindManager::moveActiveTo(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
return;
if (PLASTWINDOW->m_bIsFloating) {
@ -1428,7 +1469,8 @@ void CKeybindManager::toggleGroup(std::string args) {
if (!PWINDOW)
return;
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
if (PWINDOW->isFullscreen())
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
if (PWINDOW->m_sGroupData.pNextWindow.expired())
PWINDOW->createGroup();
@ -1662,7 +1704,7 @@ void CKeybindManager::renameWorkspace(std::string args) {
}
void CKeybindManager::exitHyprland(std::string argz) {
g_pEventLoopManager->doLater([]() { g_pCompositor->cleanup(); });
g_pCompositor->stopCompositor();
}
void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) {
@ -1820,7 +1862,7 @@ void CKeybindManager::forceRendererReload(std::string args) {
void CKeybindManager::resizeActive(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
return;
const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealSize.goal());
@ -1837,7 +1879,7 @@ void CKeybindManager::resizeActive(std::string args) {
void CKeybindManager::moveActive(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
if (!PLASTWINDOW || PLASTWINDOW->isFullscreen())
return;
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealPosition.goal());
@ -1857,7 +1899,7 @@ void CKeybindManager::moveWindow(std::string args) {
return;
}
if (PWINDOW->m_bIsFullscreen)
if (PWINDOW->isFullscreen())
return;
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealPosition.goal());
@ -1877,7 +1919,7 @@ void CKeybindManager::resizeWindow(std::string args) {
return;
}
if (PWINDOW->m_bIsFullscreen)
if (PWINDOW->isFullscreen())
return;
const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealSize.goal());
@ -1954,12 +1996,12 @@ void CKeybindManager::focusWindow(std::string regexp) {
g_pCompositor->focusWindow(PWINDOW);
} else {
if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned)
g_pCompositor->setWindowFullscreen(FSWINDOW, false, FULLSCREEN_FULL);
g_pCompositor->setWindowFullscreenClient(FSWINDOW, FSMODE_NONE);
g_pCompositor->focusWindow(PWINDOW);
if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned)
g_pCompositor->setWindowFullscreen(PWINDOW, true, FSMODE);
g_pCompositor->setWindowFullscreenClient(PWINDOW, FSMODE);
}
} else
g_pCompositor->focusWindow(PWINDOW);
@ -2236,17 +2278,6 @@ void CKeybindManager::layoutmsg(std::string msg) {
g_pLayoutManager->getCurrentLayout()->layoutMessage(hd, msg);
}
void CKeybindManager::toggleOpaque(std::string unused) {
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PWINDOW)
return;
PWINDOW->m_sWindowData.opaque = CWindowOverridableVar(!PWINDOW->m_sWindowData.opaque.valueOrDefault(), PRIORITY_SET_PROP);
g_pHyprRenderer->damageWindow(PWINDOW);
}
void CKeybindManager::dpms(std::string arg) {
bool enable = arg.starts_with("on");
std::string port = "";
@ -2342,11 +2373,19 @@ void CKeybindManager::pinActive(std::string args) {
return;
}
if (!PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
if (!PWINDOW->m_bIsFloating || PWINDOW->isFullscreen())
return;
PWINDOW->m_bPinned = !PWINDOW->m_bPinned;
PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->activeWorkspace;
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();
changeMouseBindMode(MBIND_MOVE);
} else {
g_pKeybindManager->m_bIsMouseBindActive = false;
if (!g_pInputManager->currentlyDraggedWindow.expired()) {
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
g_pInputManager->currentlyDraggedWindow.reset();
g_pInputManager->dragMode = MBIND_INVALID;
}
}
} else if (ARGS[0] == "resizewindow") {
if (PRESSED) {
g_pKeybindManager->m_bIsMouseBindActive = true;
g_pInputManager->currentlyDraggedWindow =
g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
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;
case 1: changeMouseBindMode(MBIND_RESIZE_FORCE_RATIO); break;
case 2: changeMouseBindMode(MBIND_RESIZE_BLOCK_RATIO); break;
default: changeMouseBindMode(MBIND_RESIZE);
}
} catch (std::exception& e) { g_pInputManager->dragMode = MBIND_RESIZE; }
} catch (std::exception& e) { changeMouseBindMode(MBIND_RESIZE); }
}
}
void CKeybindManager::changeMouseBindMode(const eMouseBindMode MODE) {
if (MODE != MBIND_INVALID) {
if (!g_pInputManager->currentlyDraggedWindow.expired() || g_pInputManager->dragMode != MBIND_INVALID)
return;
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
const PHLWINDOW PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
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 {
g_pKeybindManager->m_bIsMouseBindActive = false;
if (g_pInputManager->currentlyDraggedWindow.expired() || g_pInputManager->dragMode == MBIND_INVALID)
return;
if (!g_pInputManager->currentlyDraggedWindow.expired()) {
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
g_pInputManager->currentlyDraggedWindow.reset();
g_pInputManager->dragMode = MBIND_INVALID;
}
}
g_pInputManager->dragMode = MODE;
}
}
@ -2444,14 +2479,6 @@ void CKeybindManager::alterZOrder(std::string args) {
g_pInputManager->simulateMouseMovement();
}
void CKeybindManager::fakeFullscreenActive(std::string args) {
if (!g_pCompositor->m_pLastWindow.expired()) {
// will also set the flag
g_pCompositor->m_pLastWindow->m_bFakeFullscreenState = !g_pCompositor->m_pLastWindow->m_bFakeFullscreenState;
g_pXWaylandManager->setWindowFullscreen(g_pCompositor->m_pLastWindow.lock(), g_pCompositor->m_pLastWindow->shouldSendFullscreenState());
}
}
void CKeybindManager::lockGroups(std::string args) {
if (args == "lock" || args.empty() || args == "lockgroups")
g_pKeybindManager->m_bGroupsLocked = true;
@ -2489,6 +2516,11 @@ void CKeybindManager::moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowIn
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); // This removes groupped property!
if (pWindow->m_iMonitorID != pWindowInDirection->m_iMonitorID) {
pWindow->moveToWorkspace(pWindowInDirection->m_pWorkspace);
pWindow->m_iMonitorID = pWindowInDirection->m_iMonitorID;
}
static auto USECURRPOS = CConfigValue<Hyprlang::INT>("group:insert_after_current");
pWindowInDirection = *USECURRPOS ? pWindowInDirection : pWindowInDirection->getGroupTail();
@ -2606,7 +2638,7 @@ void CKeybindManager::moveWindowOrGroup(std::string args) {
}
const auto PWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PWINDOW || PWINDOW->m_bIsFullscreen)
if (!PWINDOW || PWINDOW->isFullscreen())
return;
if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) {
@ -2676,10 +2708,10 @@ void CKeybindManager::global(std::string args) {
if (NAME.empty())
return;
if (!g_pProtocolManager->m_pGlobalShortcutsProtocolManager->globalShortcutExists(APPID, NAME))
if (!PROTO::globalShortcuts->isTaken(APPID, NAME))
return;
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->sendGlobalShortcutEvent(APPID, NAME, g_pKeybindManager->m_iPassPressed);
PROTO::globalShortcuts->sendGlobalShortcutEvent(APPID, NAME, g_pKeybindManager->m_iPassPressed);
}
void CKeybindManager::moveGroupWindow(std::string args) {

View file

@ -34,6 +34,7 @@ struct SKeybind {
bool ignoreMods = false;
bool multiKey = false;
bool hasDescription = false;
bool dontInhibit = false;
// DO NOT INITIALIZE
bool shadowed = false;
@ -104,6 +105,8 @@ class CKeybindManager {
//we also store the keyboard pointer (in the string) to differentiate between different keyboard (layouts)
std::unordered_map<std::string, xkb_keycode_t> m_mKeyToCodeCache;
static void changeMouseBindMode(const eMouseBindMode mode);
private:
std::deque<SPressedKeyWithMods> m_dPressedKeys;
@ -115,7 +118,6 @@ class CKeybindManager {
uint32_t m_uLastCode = 0;
uint32_t m_uLastMouseCode = 0;
bool m_bIsMouseBindActive = false;
std::vector<SKeybind*> m_vPressedSpecialBinds;
int m_iPassPressed = -1; // used for pass
@ -153,7 +155,7 @@ class CKeybindManager {
static void setActiveTiled(std::string);
static void changeworkspace(std::string);
static void fullscreenActive(std::string);
static void fakeFullscreenActive(std::string);
static void fullscreenStateActive(std::string args);
static void moveActiveToWorkspace(std::string);
static void moveActiveToWorkspaceSilent(std::string);
static void moveFocusTo(std::string);
@ -189,7 +191,6 @@ class CKeybindManager {
static void pass(std::string);
static void sendshortcut(std::string);
static void layoutmsg(std::string);
static void toggleOpaque(std::string);
static void dpms(std::string);
static void swapnext(std::string);
static void swapActiveWorkspaces(std::string);

View file

@ -4,6 +4,7 @@
#include "../protocols/PointerGestures.hpp"
#include "../protocols/FractionalScale.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/core/Seat.hpp"
#include "eventLoop/EventLoopManager.hpp"
#include "SeatManager.hpp"
#include <cstring>
@ -24,6 +25,14 @@ CPointerManager::CPointerManager() {
},
nullptr);
});
hooks.monitorPreRender = g_pHookSystem->hookDynamic("preMonitorCommit", [this](void* self, SCallbackInfo& info, std::any data) {
auto state = stateFor(std::any_cast<CMonitor*>(data)->self.lock());
if (!state)
return;
state->cursorRendered = false;
});
}
void CPointerManager::lockSoftwareAll() {
@ -143,18 +152,20 @@ void CPointerManager::setCursorSurface(SP<CWLSurface> surf, const Vector2D& hots
currentCursorImage.surface = surf;
currentCursorImage.scale = surf->resource()->current.scale;
surf->resource()->map();
currentCursorImage.destroySurface = surf->events.destroy.registerListener([this](std::any data) { resetCursorImage(); });
currentCursorImage.commitSurface = surf->resource()->events.commit.registerListener([this](std::any data) {
damageIfSoftware();
currentCursorImage.size = currentCursorImage.surface->resource()->current.buffer ? currentCursorImage.surface->resource()->current.buffer->size : Vector2D{};
currentCursorImage.size = currentCursorImage.surface->resource()->current.texture ? currentCursorImage.surface->resource()->current.bufferSize : Vector2D{};
currentCursorImage.scale = currentCursorImage.surface ? currentCursorImage.surface->resource()->current.scale : 1.F;
recheckEnteredOutputs();
updateCursorBackend();
damageIfSoftware();
});
if (surf->resource()->current.buffer) {
currentCursorImage.size = surf->resource()->current.buffer->size;
if (surf->resource()->current.texture) {
currentCursorImage.size = surf->resource()->current.bufferSize;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
surf->resource()->frame(&now);
@ -214,6 +225,8 @@ void CPointerManager::resetCursorImage(bool apply) {
currentCursorImage.surface->resource()->leave(m);
}
currentCursorImage.surface->resource()->unmap();
currentCursorImage.destroySurface.reset();
currentCursorImage.commitSurface.reset();
currentCursorImage.surface.reset();
@ -386,6 +399,14 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
}
}
// if we already rendered the cursor, revert the swapchain to avoid rendering the cursor over
// the current front buffer
// this flag will be reset in the preRender hook, so when we commit this buffer to KMS
if (state->cursorRendered)
state->monitor->cursorSwapchain->rollback();
state->cursorRendered = true;
auto buf = state->monitor->cursorSwapchain->next(nullptr);
if (!buf) {
Debug::log(TRACE, "Failed to acquire a buffer from the cursor swapchain");
@ -410,16 +431,39 @@ SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager
// clear buffer
memset(bufPtr, 0, std::get<2>(bufData));
auto texBuffer = currentCursorImage.pBuffer ? currentCursorImage.pBuffer : currentCursorImage.surface->resource()->current.buffer;
if (currentCursorImage.pBuffer) {
auto texAttrs = currentCursorImage.pBuffer->shm();
if (texBuffer) {
auto textAttrs = texBuffer->shm();
auto texData = texBuffer->beginDataPtr(GBM_BO_TRANSFER_WRITE);
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{} {} {} {}", textAttrs.size.x, textAttrs.size.y, (void*)texPtr, textAttrs.format, textAttrs.stride);
Debug::log(TRACE, "cursor texture {}x{} {} {} {}", texAttrs.size.x, texAttrs.size.y, (void*)texPtr, texAttrs.format, texAttrs.stride);
// copy cursor texture
for (int i = 0; i < texBuffer->shm().size.y; i++)
memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * textAttrs.stride, textAttrs.stride);
for (int i = 0; i < texAttrs.size.y; i++)
memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * texAttrs.stride, texAttrs.stride);
} else if (currentCursorImage.surface && currentCursorImage.surface->resource()->role->role() == SURFACE_ROLE_CURSOR) {
const auto SURFACE = currentCursorImage.surface->resource();
auto& shmBuffer = CCursorSurfaceRole::cursorPixelData(SURFACE);
Debug::log(TRACE, "cursor texture pixel data length: {}B", shmBuffer.size());
if (shmBuffer.data()) {
// copy cursor texture
// assume format is 32bpp
size_t STRIDE = 4 * SURFACE->current.bufferSize.x;
for (int i = 0; i < SURFACE->current.bufferSize.y; i++)
memcpy(bufPtr + i * buf->dmabuf().strides[0], shmBuffer.data() + i * STRIDE, STRIDE);
} else {
// if there is no data, hide the cursor
memset(bufPtr, '\0', buf->size.x * buf->size.y * 4 /* assume 32bpp */);
}
} else {
Debug::log(TRACE, "Unsupported cursor buffer/surface, falling back to sw (can't dumb copy)");
return nullptr;
}
buf->endDataPtr();
@ -620,7 +664,7 @@ void CPointerManager::move(const Vector2D& deltaLogical) {
void CPointerManager::warpAbsolute(Vector2D abs, SP<IHID> dev) {
SP<CMonitor> currentMonitor = g_pCompositor->m_pLastMonitor.lock();
if (!currentMonitor)
if (!currentMonitor || !dev)
return;
if (!std::isnan(abs.x))
@ -658,6 +702,32 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP<IHID> dev) {
}
break;
}
case HID_TYPE_POINTER: {
IPointer* POINTER = reinterpret_cast<IPointer*>(dev.get());
if (!POINTER->boundOutput.empty()) {
if (POINTER->boundOutput == "entire") {
// find x and y size of the entire space
Vector2D bottomRight = {-9999999, -9999999}, topLeft = {9999999, 9999999};
for (auto& m : g_pCompositor->m_vMonitors) {
const auto EXTENT = m->logicalBox().extent();
const auto POS = m->logicalBox().pos();
if (EXTENT.x > bottomRight.x)
bottomRight.x = EXTENT.x;
if (EXTENT.y > bottomRight.y)
bottomRight.y = EXTENT.y;
if (POS.x < topLeft.x)
topLeft.x = POS.x;
if (POS.y < topLeft.y)
topLeft.y = POS.y;
}
mappedArea = {topLeft, bottomRight - topLeft};
} else if (const auto PMONITOR = g_pCompositor->getMonitorFromString(POINTER->boundOutput); PMONITOR) {
currentMonitor = PMONITOR->self.lock();
mappedArea = currentMonitor->logicalBox();
}
}
break;
}
default: break;
}
@ -694,7 +764,7 @@ void CPointerManager::onMonitorLayoutChange() {
}
SP<CTexture> CPointerManager::getCurrentCursorTexture() {
if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.buffer))
if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.texture))
return nullptr;
if (currentCursorImage.pBuffer) {
@ -703,7 +773,7 @@ SP<CTexture> CPointerManager::getCurrentCursorTexture() {
return currentCursorImage.bufferTex;
}
return currentCursorImage.surface->resource()->current.buffer->texture;
return currentCursorImage.surface->resource()->current.texture;
}
void CPointerManager::attachPointer(SP<IPointer> pointer) {

View file

@ -165,6 +165,7 @@ class CPointerManager {
CBox box; // logical
bool entered = false;
bool hwApplied = false;
bool cursorRendered = false;
SP<Aquamarine::IBuffer> cursorFrontBuffer;
};
@ -177,6 +178,7 @@ class CPointerManager {
struct {
SP<HOOK_CALLBACK_FN> monitorAdded;
SP<HOOK_CALLBACK_FN> monitorPreRender;
} hooks;
};

View file

@ -39,6 +39,10 @@
#include "../protocols/LinuxDMABUF.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/Screencopy.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../protocols/TextInputV1.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "../protocols/core/Seat.hpp"
#include "../protocols/core/DataDevice.hpp"
@ -66,8 +70,7 @@ void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) {
else if (!ISMIRROR && (!PROTO::outputs.contains(pMonitor->szName) || PROTO::outputs.at(pMonitor->szName)->isDefunct())) {
if (PROTO::outputs.contains(pMonitor->szName))
PROTO::outputs.erase(pMonitor->szName);
PROTO::outputs.emplace(pMonitor->szName,
std::make_unique<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock()));
PROTO::outputs.emplace(pMonitor->szName, makeShared<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock()));
}
}
@ -86,7 +89,10 @@ CProtocolManager::CProtocolManager() {
if (PROTO::outputs.contains(M->szName))
PROTO::outputs.erase(M->szName);
PROTO::outputs.emplace(M->szName, std::make_unique<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", M->szName), M->self.lock()));
auto ref = makeShared<CWLOutputProtocol>(&wl_output_interface, 4, std::format("WLOutput ({})", M->szName), M->self.lock());
PROTO::outputs.emplace(M->szName, ref);
ref->self = ref;
m_mModeChangeListeners[M->szName] = M->events.modeChanged.registerListener([M, this](std::any d) { onMonitorModeChange(M); });
});
@ -121,6 +127,7 @@ CProtocolManager::CProtocolManager() {
PROTO::pointerGestures = std::make_unique<CPointerGesturesProtocol>(&zwp_pointer_gestures_v1_interface, 3, "PointerGestures");
PROTO::foreignToplevelWlr = std::make_unique<CForeignToplevelWlrProtocol>(&zwlr_foreign_toplevel_manager_v1_interface, 3, "ForeignToplevelWlr");
PROTO::shortcutsInhibit = std::make_unique<CKeyboardShortcutsInhibitProtocol>(&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1, "ShortcutsInhibit");
PROTO::textInputV1 = std::make_unique<CTextInputV1Protocol>(&zwp_text_input_manager_v1_interface, 1, "TextInputV1");
PROTO::textInputV3 = std::make_unique<CTextInputV3Protocol>(&zwp_text_input_manager_v3_interface, 1, "TextInputV3");
PROTO::constraints = std::make_unique<CPointerConstraintsProtocol>(&zwp_pointer_constraints_v1_interface, 1, "PointerConstraints");
PROTO::outputPower = std::make_unique<COutputPowerProtocol>(&zwlr_output_power_manager_v1_interface, 1, "OutputPower");
@ -140,6 +147,9 @@ CProtocolManager::CProtocolManager() {
PROTO::dataWlr = std::make_unique<CDataDeviceWLRProtocol>(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr");
PROTO::primarySelection = std::make_unique<CPrimarySelectionProtocol>(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection");
PROTO::xwaylandShell = std::make_unique<CXWaylandShellProtocol>(&xwayland_shell_v1_interface, 1, "XWaylandShell");
PROTO::screencopy = std::make_unique<CScreencopyProtocol>(&zwlr_screencopy_manager_v1_interface, 3, "Screencopy");
PROTO::toplevelExport = std::make_unique<CToplevelExportProtocol>(&hyprland_toplevel_export_manager_v1_interface, 2, "ToplevelExport");
PROTO::globalShortcuts = std::make_unique<CGlobalShortcutsProtocol>(&hyprland_global_shortcuts_manager_v1_interface, 1, "GlobalShortcuts");
for (auto& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
@ -156,11 +166,62 @@ CProtocolManager::CProtocolManager() {
PROTO::linuxDma = std::make_unique<CLinuxDMABufV1Protocol>(&zwp_linux_dmabuf_v1_interface, 5, "LinuxDMABUF");
} else
Debug::log(WARN, "ProtocolManager: Not binding linux-dmabuf and MesaDRM: DMABUF not available");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.
m_pToplevelExportProtocolManager = std::make_unique<CToplevelExportProtocolManager>();
m_pTextInputV1ProtocolManager = std::make_unique<CTextInputV1ProtocolManager>();
m_pGlobalShortcutsProtocolManager = std::make_unique<CGlobalShortcutsProtocolManager>();
m_pScreencopyProtocolManager = std::make_unique<CScreencopyProtocolManager>();
}
CProtocolManager::~CProtocolManager() {
// this is dumb but i don't want to replace all 600 PROTO with the right thing
// Output
PROTO::outputs.clear();
// Core
PROTO::seat.reset();
PROTO::data.reset();
PROTO::compositor.reset();
PROTO::subcompositor.reset();
PROTO::shm.reset();
// Extensions
PROTO::viewport.reset();
PROTO::tearing.reset();
PROTO::fractional.reset();
PROTO::xdgOutput.reset();
PROTO::cursorShape.reset();
PROTO::idleInhibit.reset();
PROTO::relativePointer.reset();
PROTO::xdgDecoration.reset();
PROTO::alphaModifier.reset();
PROTO::gamma.reset();
PROTO::foreignToplevel.reset();
PROTO::pointerGestures.reset();
PROTO::foreignToplevelWlr.reset();
PROTO::shortcutsInhibit.reset();
PROTO::textInputV1.reset();
PROTO::textInputV3.reset();
PROTO::constraints.reset();
PROTO::outputPower.reset();
PROTO::activation.reset();
PROTO::idle.reset();
PROTO::sessionLock.reset();
PROTO::ime.reset();
PROTO::virtualKeyboard.reset();
PROTO::virtualPointer.reset();
PROTO::outputManagement.reset();
PROTO::serverDecorationKDE.reset();
PROTO::focusGrab.reset();
PROTO::tablet.reset();
PROTO::layerShell.reset();
PROTO::presentation.reset();
PROTO::xdgShell.reset();
PROTO::dataWlr.reset();
PROTO::primarySelection.reset();
PROTO::xwaylandShell.reset();
PROTO::screencopy.reset();
PROTO::toplevelExport.reset();
PROTO::globalShortcuts.reset();
PROTO::lease.reset();
PROTO::sync.reset();
PROTO::mesaDRM.reset();
PROTO::linuxDma.reset();
}

View file

@ -1,10 +1,7 @@
#pragma once
#include "../defines.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../protocols/TextInputV1.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "../protocols/Screencopy.hpp"
#include "../helpers/Monitor.hpp"
#include "../helpers/memory/Memory.hpp"
#include "../helpers/signal/Signal.hpp"
#include <unordered_map>
@ -12,12 +9,7 @@
class CProtocolManager {
public:
CProtocolManager();
// TODO: rewrite to use the new protocol framework
std::unique_ptr<CToplevelExportProtocolManager> m_pToplevelExportProtocolManager;
std::unique_ptr<CTextInputV1ProtocolManager> m_pTextInputV1ProtocolManager;
std::unique_ptr<CGlobalShortcutsProtocolManager> m_pGlobalShortcutsProtocolManager;
std::unique_ptr<CScreencopyProtocolManager> m_pScreencopyProtocolManager;
~CProtocolManager();
private:
std::unordered_map<std::string, CHyprSignalListener> m_mModeChangeListeners;

View file

@ -71,7 +71,8 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
g_pHyprRenderer->damageMonitor(m.get());
});
m_pSessionLock->listeners.destroy = pLock->events.destroyed.registerListener([](std::any data) {
m_pSessionLock->listeners.destroy = pLock->events.destroyed.registerListener([this](std::any data) {
m_pSessionLock.reset();
g_pCompositor->focusSurface(nullptr);
for (auto& m : g_pCompositor->m_vMonitors)
@ -104,7 +105,7 @@ SSessionLockSurface* CSessionLockManager::getSessionLockSurfaceForMonitor(uint64
// We don't want the red screen to flash.
float CSessionLockManager::getRedScreenAlphaForMonitor(uint64_t id) {
if (!m_pSessionLock)
return 0.F;
return 1.F;
const auto& NOMAPPEDSURFACETIMER = m_pSessionLock->mMonitorsWithoutMappedSurfaceTimers.find(id);
@ -123,7 +124,7 @@ void CSessionLockManager::onLockscreenRenderedOnMonitor(uint64_t id) {
m_pSessionLock->m_lockedMonitors.emplace(id);
const auto MONITORS = g_pCompositor->m_vMonitors;
const bool LOCKED = std::all_of(MONITORS.begin(), MONITORS.end(), [this](auto m) { return m_pSessionLock->m_lockedMonitors.contains(m->ID); });
if (LOCKED) {
if (LOCKED && m_pSessionLock->lock->good()) {
m_pSessionLock->lock->sendLocked();
m_pSessionLock->m_hasSentLocked = true;
}

View file

@ -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) {

View file

@ -7,7 +7,7 @@ void CInputManager::newIdleInhibitor(std::any inhibitor) {
const auto PINHIBIT = m_vIdleInhibitors.emplace_back(std::make_unique<SIdleInhibitor>()).get();
PINHIBIT->inhibitor = std::any_cast<SP<CIdleInhibitor>>(inhibitor);
Debug::log(LOG, "New idle inhibitor registered for surface {:x}", (uintptr_t)PINHIBIT->inhibitor->surface);
Debug::log(LOG, "New idle inhibitor registered for surface {:x}", (uintptr_t)PINHIBIT->inhibitor->surface.get());
PINHIBIT->inhibitor->listeners.destroy = PINHIBIT->inhibitor->resource->events.destroy.registerListener([this, PINHIBIT](std::any data) {
std::erase_if(m_vIdleInhibitors, [PINHIBIT](const auto& other) { return other.get() == PINHIBIT; });
@ -63,7 +63,7 @@ void CInputManager::recheckIdleInhibitorStatus() {
return;
}
if (w->m_eIdleInhibitMode == IDLEINHIBIT_FULLSCREEN && w->m_bIsFullscreen && g_pCompositor->isWorkspaceVisible(w->m_pWorkspace)) {
if (w->m_eIdleInhibitMode == IDLEINHIBIT_FULLSCREEN && w->isFullscreen() && g_pCompositor->isWorkspaceVisible(w->m_pWorkspace)) {
PROTO::idle->setInhibit(true);
return;
}

View file

@ -17,6 +17,7 @@
#include "../../protocols/LayerShell.hpp"
#include "../../protocols/core/Seat.hpp"
#include "../../protocols/core/DataDevice.hpp"
#include "../../protocols/core/Compositor.hpp"
#include "../../protocols/XDGShell.hpp"
#include "../../devices/Mouse.hpp"
@ -228,7 +229,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
return;
} else
Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF, (uintptr_t)CONSTRAINT.get());
Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF.get(), (uintptr_t)CONSTRAINT.get());
}
// if we are holding a pointer button,
@ -297,7 +298,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
// then, we check if the workspace doesnt have a fullscreen window
const auto PWORKSPACE = PMONITOR->activeWorkspace;
if (PWORKSPACE->m_bHasFullscreenWindow && !foundSurface && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
if (PWORKSPACE->m_bHasFullscreenWindow && !foundSurface && PWORKSPACE->m_efFullscreenMode == FSMODE_FULLSCREEN) {
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
if (!pFoundWindow) {
@ -324,7 +325,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
// then windows
if (!foundSurface) {
if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FSMODE_MAXIMIZED) {
if (!foundSurface) {
if (PMONITOR->activeSpecialWorkspace) {
pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
@ -469,7 +470,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
else if (pFoundWindow) {
// change cursor icon if hovering over border
if (*PRESIZEONBORDER && *PRESIZECURSORICON) {
if (!pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) {
if (!pFoundWindow->isFullscreen() && !pFoundWindow->hasPopupAt(mouseCoords)) {
setCursorIconOnBorder(pFoundWindow);
} else if (m_eBorderIconDirection != BORDERICON_NONE) {
unsetCursorImage();
@ -570,7 +571,7 @@ void CInputManager::processMouseRequest(std::any E) {
auto e = std::any_cast<CSeatManager::SSetCursorEvent>(E);
Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf);
Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf.get());
if (e.surf != m_sCursorSurfaceInfo.wlSurface->resource()) {
m_sCursorSurfaceInfo.wlSurface->unassign();
@ -680,7 +681,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) {
// clicking on border triggers resize
// TODO detect click on LS properly
if (*PRESIZEONBORDER && !m_bLastFocusOnLS && e.state == WL_POINTER_BUTTON_STATE_PRESSED && (!w || w->m_iX11Type != 2)) {
if (w && !w->m_bIsFullscreen) {
if (w && !w->isFullscreen()) {
const CBox real = {w->m_vRealPosition.value().x, w->m_vRealPosition.value().y, w->m_vRealSize.value().x, w->m_vRealSize.value().y};
const CBox grab = {real.x - BORDER_GRAB_AREA, real.y - BORDER_GRAB_AREA, real.width + 2 * BORDER_GRAB_AREA, real.height + 2 * BORDER_GRAB_AREA};
@ -850,7 +851,7 @@ void CInputManager::newKeyboard(SP<Aquamarine::IKeyboard> keyboard) {
setupKeyboard(PNEWKEYBOARD);
Debug::log(LOG, "New keyboard created, pointers Hypr: {:x} and WLR: {:x}", (uintptr_t)PNEWKEYBOARD.get(), (uintptr_t)keyboard);
Debug::log(LOG, "New keyboard created, pointers Hypr: {:x} and AQ: {:x}", (uintptr_t)PNEWKEYBOARD.get(), (uintptr_t)keyboard.get());
}
void CInputManager::newVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keyboard) {
@ -992,7 +993,7 @@ void CInputManager::newMouse(SP<Aquamarine::IPointer> mouse) {
setupMouse(PMOUSE);
Debug::log(LOG, "New mouse created, pointer WLR: {:x}", (uintptr_t)mouse);
Debug::log(LOG, "New mouse created, pointer AQ: {:x}", (uintptr_t)mouse.get());
}
void CInputManager::setupMouse(SP<IPointer> mauz) {
@ -1264,7 +1265,14 @@ void CInputManager::updateKeyboardsLeds(SP<IKeyboard> pKeyboard) {
if (!pKeyboard)
return;
pKeyboard->updateLEDs();
std::optional<uint32_t> leds = pKeyboard->getLEDs();
if (!leds.has_value())
return;
for (auto& k : m_vKeyboards) {
k->updateLEDs(leds.value());
}
}
void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
@ -1382,6 +1390,12 @@ void CInputManager::refocusLastWindow(CMonitor* pMonitor) {
g_pCompositor->focusWindow(PLASTWINDOW);
} else {
// otherwise fall back to a normal refocus.
if (foundSurface && !foundSurface->hlSurface->keyboardFocusable()) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
g_pCompositor->focusWindow(PLASTWINDOW);
}
refocus();
}
}
@ -1670,7 +1684,7 @@ void CInputManager::releaseAllMouseButtons() {
void CInputManager::setCursorIconOnBorder(PHLWINDOW w) {
// do not override cursor icons set by mouse binds
if (g_pKeybindManager->m_bIsMouseBindActive) {
if (g_pInputManager->currentlyDraggedWindow.expired()) {
m_eBorderIconDirection = BORDERICON_NONE;
return;
}

View file

@ -2,6 +2,7 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/TextInputV3.hpp"
#include "../../protocols/TextInputV1.hpp"
#include "../../protocols/InputMethodV2.hpp"
#include "../../protocols/core/Compositor.hpp"
@ -9,7 +10,8 @@ CInputMethodRelay::CInputMethodRelay() {
static auto P =
g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast<SP<CWLSurfaceResource>>(param)); });
listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(ti); });
listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(std::any_cast<WP<CTextInputV3>>(ti)); });
listeners.newTIV1 = PROTO::textInputV1->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(std::any_cast<WP<CTextInputV1>>(ti)); });
listeners.newIME = PROTO::ime->events.newIME.registerListener([this](std::any ime) { onNewIME(std::any_cast<SP<CInputMethodV2>>(ime)); });
}
@ -86,11 +88,11 @@ CTextInput* CInputMethodRelay::getFocusedTextInput() {
return nullptr;
}
void CInputMethodRelay::onNewTextInput(std::any tiv3) {
m_vTextInputs.emplace_back(std::make_unique<CTextInput>(std::any_cast<WP<CTextInputV3>>(tiv3)));
void CInputMethodRelay::onNewTextInput(WP<CTextInputV3> tiv3) {
m_vTextInputs.emplace_back(std::make_unique<CTextInput>(tiv3));
}
void CInputMethodRelay::onNewTextInput(STextInputV1* pTIV1) {
void CInputMethodRelay::onNewTextInput(WP<CTextInputV1> pTIV1) {
m_vTextInputs.emplace_back(std::make_unique<CTextInput>(pTIV1));
}

View file

@ -10,7 +10,7 @@
class CInputManager;
class CHyprRenderer;
struct STextInputV1;
class CTextInputV1;
class CInputMethodV2;
class CInputMethodRelay {
@ -18,8 +18,8 @@ class CInputMethodRelay {
CInputMethodRelay();
void onNewIME(SP<CInputMethodV2>);
void onNewTextInput(std::any tiv3);
void onNewTextInput(STextInputV1* pTIV1);
void onNewTextInput(WP<CTextInputV3> tiv3);
void onNewTextInput(WP<CTextInputV1> pTIV1);
void activateIME(CTextInput* pInput);
void deactivateIME(CTextInput* pInput);
@ -48,6 +48,7 @@ class CInputMethodRelay {
struct {
CHyprSignalListener newTIV3;
CHyprSignalListener newTIV1;
CHyprSignalListener newIME;
CHyprSignalListener commitIME;
CHyprSignalListener destroyIME;

View file

@ -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;
}
}

View file

@ -7,8 +7,7 @@
#include "../../protocols/InputMethodV2.hpp"
#include "../../protocols/core/Compositor.hpp"
CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) {
ti->pTextInput = this;
CTextInput::CTextInput(WP<CTextInputV1> ti) : pV1Input(ti) {
initCallbacks();
}
@ -16,17 +15,6 @@ CTextInput::CTextInput(WP<CTextInputV3> ti) : pV3Input(ti) {
initCallbacks();
}
CTextInput::~CTextInput() {
if (pV1Input)
pV1Input->pTextInput = nullptr;
}
void CTextInput::tiV1Destroyed() {
pV1Input = nullptr;
g_pInputManager->m_sIMERelay.removeTextInput(this);
}
void CTextInput::initCallbacks() {
if (isV3()) {
const auto INPUT = pV3Input.lock();
@ -41,25 +29,19 @@ void CTextInput::initCallbacks() {
g_pInputManager->m_sIMERelay.removeTextInput(this);
});
} else {
hyprListener_textInputEnable.initCallback(&pV1Input->sEnable, [this](void* owner, void* data) { onEnabled(); }, this, "textInput");
const auto INPUT = pV1Input.lock();
hyprListener_textInputCommit.initCallback(&pV1Input->sCommit, [this](void* owner, void* data) { onCommit(); }, this, "textInput");
hyprListener_textInputDisable.initCallback(&pV1Input->sDisable, [this](void* owner, void* data) { onDisabled(); }, this, "textInput");
hyprListener_textInputDestroy.initCallback(
&pV1Input->sDestroy,
[this](void* owner, void* data) {
hyprListener_textInputCommit.removeCallback();
hyprListener_textInputDestroy.removeCallback();
hyprListener_textInputDisable.removeCallback();
hyprListener_textInputEnable.removeCallback();
listeners.enable = INPUT->events.enable.registerListener([this](std::any p) {
const auto SURFACE = std::any_cast<SP<CWLSurfaceResource>>(p);
onEnabled(SURFACE);
});
listeners.disable = INPUT->events.disable.registerListener([this](std::any p) { onDisabled(); });
listeners.commit = INPUT->events.onCommit.registerListener([this](std::any p) { onCommit(); });
listeners.destroy = INPUT->events.destroy.registerListener([this](std::any p) {
listeners.surfaceUnmap.reset();
listeners.surfaceDestroy.reset();
g_pInputManager->m_sIMERelay.removeTextInput(this);
},
this, "textInput");
});
}
}
@ -149,7 +131,7 @@ void CTextInput::setFocusedSurface(SP<CWLSurfaceResource> pSurface) {
}
bool CTextInput::isV3() {
return !pV1Input;
return pV3Input && !pV1Input;
}
void CTextInput::enter(SP<CWLSurfaceResource> pSurface) {
@ -174,8 +156,7 @@ void CTextInput::enter(SP<CWLSurfaceResource> pSurface) {
if (isV3())
pV3Input->enter(pSurface);
else {
zwp_text_input_v1_send_enter(pV1Input->resourceImpl, pSurface->getResource()->resource());
pV1Input->active = true;
pV1Input->enter(pSurface);
}
setFocusedSurface(pSurface);
@ -194,8 +175,7 @@ void CTextInput::leave() {
if (isV3() && focusedSurface())
pV3Input->leave(focusedSurface());
else if (focusedSurface() && pV1Input) {
zwp_text_input_v1_send_leave(pV1Input->resourceImpl);
pV1Input->active = false;
pV1Input->leave();
}
setFocusedSurface(nullptr);
@ -208,7 +188,7 @@ SP<CWLSurfaceResource> CTextInput::focusedSurface() {
}
wl_client* CTextInput::client() {
return isV3() ? pV3Input->client() : pV1Input->client;
return isV3() ? pV3Input->client() : pV1Input->client();
}
void CTextInput::commitStateToIME(SP<CInputMethodV2> ime) {
@ -223,13 +203,15 @@ void CTextInput::commitStateToIME(SP<CInputMethodV2> ime) {
if (INPUT->current.contentType.updated)
ime->textContentType(INPUT->current.contentType.hint, INPUT->current.contentType.purpose);
} else {
if (pV1Input->pendingSurrounding.isPending)
ime->surroundingText(pV1Input->pendingSurrounding.text, pV1Input->pendingSurrounding.cursor, pV1Input->pendingSurrounding.anchor);
const auto INPUT = pV1Input.lock();
if (INPUT->pendingSurrounding.isPending)
ime->surroundingText(INPUT->pendingSurrounding.text, INPUT->pendingSurrounding.cursor, INPUT->pendingSurrounding.anchor);
ime->textChangeCause(ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD);
if (pV1Input->pendingContentType.isPending)
ime->textContentType((zwpTextInputV3ContentHint)pV1Input->pendingContentType.hint, (zwpTextInputV3ContentPurpose)pV1Input->pendingContentType.purpose);
ime->textContentType((zwpTextInputV3ContentHint)INPUT->pendingContentType.hint, (zwpTextInputV3ContentPurpose)INPUT->pendingContentType.purpose);
}
g_pInputManager->m_sIMERelay.updateAllPopups();
@ -252,25 +234,27 @@ void CTextInput::updateIMEState(SP<CInputMethodV2> ime) {
INPUT->sendDone();
} else {
const auto INPUT = pV1Input.lock();
if (ime->current.preeditString.committed) {
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, std::string(ime->current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str(), "");
INPUT->preeditCursor(ime->current.preeditString.begin);
INPUT->preeditStyling(0, std::string(ime->current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
INPUT->preeditString(pV1Input->serial, ime->current.preeditString.string.c_str(), "");
} else {
zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, "", "");
INPUT->preeditCursor(ime->current.preeditString.begin);
INPUT->preeditStyling(0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
INPUT->preeditString(pV1Input->serial, "", "");
}
if (ime->current.committedString.committed)
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.committedString.string.c_str());
INPUT->commitString(pV1Input->serial, ime->current.committedString.string.c_str());
if (ime->current.deleteSurrounding.committed) {
zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preeditString.string).length() - ime->current.deleteSurrounding.before,
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());
}
}
}

View file

@ -8,7 +8,7 @@
struct wl_client;
struct STextInputV1;
class CTextInputV1;
class CTextInputV3;
class CInputMethodV2;
class CWLSurfaceResource;
@ -16,8 +16,7 @@ class CWLSurfaceResource;
class CTextInput {
public:
CTextInput(WP<CTextInputV3> ti);
CTextInput(STextInputV1* ti);
~CTextInput();
CTextInput(WP<CTextInputV1> ti);
bool isV3();
void enter(SP<CWLSurfaceResource> pSurface);
@ -43,12 +42,7 @@ class CTextInput {
WP<CWLSurfaceResource> pFocusedSurface;
int enterLocks = 0;
WP<CTextInputV3> pV3Input;
STextInputV1* pV1Input = nullptr;
DYNLISTENER(textInputEnable);
DYNLISTENER(textInputDisable);
DYNLISTENER(textInputCommit);
DYNLISTENER(textInputDestroy);
WP<CTextInputV1> pV1Input;
struct {
CHyprSignalListener enable;

View file

@ -6,13 +6,15 @@
#include "../SeatManager.hpp"
void CInputManager::onTouchDown(ITouch::SDownEvent e) {
m_bLastInputTouch = true;
static auto PSWIPETOUCH = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_touch");
static auto PGAPSOUTDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
auto* const PGAPSOUT = (CCssGapData*)(PGAPSOUTDATA.ptr())->getData();
// TODO: WORKSPACERULE.gapsOut.value_or()
auto gapsOut = *PGAPSOUT;
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_invert");
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_touch_invert");
EMIT_HOOK_EVENT_CANCELLABLE("touchDown", e);
auto PMONITOR = g_pCompositor->getMonitorFromName(!e.device->boundOutput.empty() ? e.device->boundOutput : "");
@ -54,8 +56,6 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
}
}
m_bLastInputTouch = true;
m_sTouchData.touchFocusWindow = m_pFoundWindowToFocus;
m_sTouchData.touchFocusSurface = m_pFoundSurfaceToFocus;
m_sTouchData.touchFocusLS = m_pFoundLSToFocus;
@ -83,6 +83,8 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
}
void CInputManager::onTouchUp(ITouch::SUpEvent e) {
m_bLastInputTouch = true;
EMIT_HOOK_EVENT_CANCELLABLE("touchUp", e);
if (m_sActiveSwipe.pWorkspaceBegin) {
// If there was a swipe from this finger, end it.
@ -96,6 +98,8 @@ void CInputManager::onTouchUp(ITouch::SUpEvent e) {
}
void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
m_bLastInputTouch = true;
EMIT_HOOK_EVENT_CANCELLABLE("touchMove", e);
if (m_sActiveSwipe.pWorkspaceBegin) {
// Do nothing if this is using a different finger.

View file

@ -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'),

View file

@ -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);

View file

@ -87,7 +87,7 @@ void CAlphaModifierProtocol::destroyModifier(CAlphaModifier* modifier) {
void CAlphaModifierProtocol::onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, SP<CWLSurfaceResource> surface) {
if (std::find_if(m_mAlphaModifiers.begin(), m_mAlphaModifiers.end(), [surface](const auto& e) { return e.first == surface; }) != m_mAlphaModifiers.end()) {
LOGM(ERR, "AlphaModifier already present for surface {:x}", (uintptr_t)surface);
LOGM(ERR, "AlphaModifier already present for surface {:x}", (uintptr_t)surface.get());
pMgr->error(WP_ALPHA_MODIFIER_V1_ERROR_ALREADY_CONSTRUCTED, "AlphaModifier already present");
return;
}

View file

@ -47,7 +47,7 @@ CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurf
return;
}
if ((acquireTimeline || releaseTimeline) && !surface->pending.buffer) {
if ((acquireTimeline || releaseTimeline) && !surface->pending.texture) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
surface->pending.rejected = true;
return;

View file

@ -51,7 +51,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
}
}
g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_FULL);
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_FULLSCREEN, true);
g_pHyprRenderer->damageWindow(PWINDOW);
});
@ -64,7 +64,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
if (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN)
return;
g_pCompositor->setWindowFullscreen(PWINDOW, false);
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_FULLSCREEN, false);
});
resource->setSetMaximized([this](CZwlrForeignToplevelHandleV1* p) {
@ -81,7 +81,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
return;
}
g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_MAXIMIZED);
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, true);
});
resource->setUnsetMaximized([this](CZwlrForeignToplevelHandleV1* p) {
@ -93,7 +93,7 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP<CZwlrForeignToplevelHand
if (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE)
return;
g_pCompositor->setWindowFullscreen(PWINDOW, false);
g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, false);
});
resource->setClose([this](CZwlrForeignToplevelHandleV1* p) {
@ -155,9 +155,9 @@ void CForeignToplevelHandleWlr::sendState() {
*p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED;
}
if (PWINDOW->m_bIsFullscreen) {
if (PWINDOW->isFullscreen()) {
auto p = (uint32_t*)wl_array_add(&state, sizeof(uint32_t));
if (PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL)
if (PWINDOW->isEffectiveInternalFSMode(FSMODE_FULLSCREEN))
*p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN;
else
*p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED;

View file

@ -28,7 +28,7 @@ void CFractionalScaleProtocol::onManagerResourceDestroy(wl_resource* res) {
void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, SP<CWLSurfaceResource> surface) {
for (auto& [k, v] : m_mAddons) {
if (k == surface) {
LOGM(ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface);
LOGM(ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface.get());
pMgr->error(WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS, "Fractional scale already exists");
return;
}

View file

@ -1,110 +1,61 @@
#include "GlobalShortcuts.hpp"
#include "../Compositor.hpp"
#define GLOBAL_SHORTCUTS_VERSION 1
#define LOGM PROTO::globalShortcuts->protoLog
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->bindManager(client, data, version, id);
}
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->displayDestroy();
}
void CGlobalShortcutsProtocolManager::displayDestroy() {
wl_global_destroy(m_pGlobal);
}
CGlobalShortcutsProtocolManager::CGlobalShortcutsProtocolManager() {
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &hyprland_global_shortcuts_manager_v1_interface, GLOBAL_SHORTCUTS_VERSION, this, bindManagerInt);
if (!m_pGlobal) {
Debug::log(ERR, "GlobalShortcutsManager could not start!");
CShortcutClient::CShortcutClient(SP<CHyprlandGlobalShortcutsManagerV1> resource_) : resource(resource_) {
if (!good())
return;
}
m_liDisplayDestroy.notify = handleDisplayDestroy;
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
resource->setOnDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); });
resource->setDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); });
Debug::log(LOG, "GlobalShortcutsManager started successfully!");
}
static void handleRegisterShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
resource->setRegisterShortcut([this](CHyprlandGlobalShortcutsManagerV1* pMgr, uint32_t shortcut, const char* id, const char* app_id, const char* description,
const char* trigger_description) {
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->registerShortcut(client, resource, shortcut, id, app_id, description, trigger_description);
}
static void handleDestroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct hyprland_global_shortcuts_manager_v1_interface globalShortcutsManagerImpl = {
.register_shortcut = handleRegisterShortcut,
.destroy = handleDestroy,
};
static const struct hyprland_global_shortcut_v1_interface shortcutImpl = {
.destroy = handleDestroy,
};
void CGlobalShortcutsProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
const auto RESOURCE = wl_resource_create(client, &hyprland_global_shortcuts_manager_v1_interface, version, id);
wl_resource_set_implementation(RESOURCE, &globalShortcutsManagerImpl, this, nullptr);
Debug::log(LOG, "GlobalShortcutsManager bound successfully!");
m_vClients.emplace_back(std::make_unique<SShortcutClient>(client));
}
SShortcutClient* CGlobalShortcutsProtocolManager::clientFromWlClient(wl_client* client) {
for (auto& c : m_vClients) {
if (c->client == client) {
return c.get();
}
}
return nullptr;
}
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?");
if (PROTO::globalShortcuts->isTaken(id, app_id)) {
resource->error(HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ALREADY_TAKEN, "Combination is taken");
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;
}
}
}
const auto PSHORTCUT = PCLIENT->shortcuts.emplace_back(std::make_unique<SShortcut>()).get();
const auto PSHORTCUT = shortcuts.emplace_back(makeShared<SShortcut>(makeShared<CHyprlandGlobalShortcutV1>(resource->client(), resource->version(), shortcut)));
PSHORTCUT->id = id;
PSHORTCUT->description = description;
PSHORTCUT->appid = app_id;
PSHORTCUT->shortcut = shortcut;
PSHORTCUT->resource = wl_resource_create(client, &hyprland_global_shortcut_v1_interface, 1, shortcut);
if (!PSHORTCUT->resource) {
wl_client_post_no_memory(client);
std::erase_if(PCLIENT->shortcuts, [&](const auto& other) { return other.get() == PSHORTCUT; });
if (!PSHORTCUT->resource->resource()) {
PSHORTCUT->resource->noMemory();
shortcuts.pop_back();
return;
}
wl_resource_set_implementation(PSHORTCUT->resource, &shortcutImpl, this, &onShortcutDestroy);
PSHORTCUT->resource->setDestroy([this](CHyprlandGlobalShortcutV1* pMgr) { std::erase_if(shortcuts, [&](const auto& other) { return other->resource.get() == pMgr; }); });
});
}
bool CGlobalShortcutsProtocolManager::globalShortcutExists(std::string appid, std::string trigger) {
bool CShortcutClient::good() {
return resource->resource();
}
CGlobalShortcutsProtocol::CGlobalShortcutsProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CGlobalShortcutsProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESROUCE = m_vClients.emplace_back(makeShared<CShortcutClient>(makeShared<CHyprlandGlobalShortcutsManagerV1>(client, ver, id)));
if (!RESROUCE->good()) {
wl_client_post_no_memory(client);
m_vClients.pop_back();
return;
}
}
void CGlobalShortcutsProtocol::destroyResource(CShortcutClient* client) {
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
}
bool CGlobalShortcutsProtocol::isTaken(std::string appid, std::string trigger) {
for (auto& c : m_vClients) {
for (auto& sh : c->shortcuts) {
if (sh->appid == appid && sh->id == trigger) {
@ -116,7 +67,7 @@ bool CGlobalShortcutsProtocolManager::globalShortcutExists(std::string appid, st
return false;
}
void CGlobalShortcutsProtocolManager::sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed) {
void CGlobalShortcutsProtocol::sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed) {
for (auto& c : m_vClients) {
for (auto& sh : c->shortcuts) {
if (sh->appid == appid && sh->id == trigger) {
@ -125,15 +76,15 @@ void CGlobalShortcutsProtocolManager::sendGlobalShortcutEvent(std::string appid,
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
if (pressed)
hyprland_global_shortcut_v1_send_pressed(sh->resource, tvSecHi, tvSecLo, now.tv_nsec);
sh->resource->sendPressed(tvSecHi, tvSecLo, now.tv_nsec);
else
hyprland_global_shortcut_v1_send_released(sh->resource, tvSecHi, tvSecLo, now.tv_nsec);
sh->resource->sendReleased(tvSecHi, tvSecLo, now.tv_nsec);
}
}
}
}
std::vector<SShortcut> CGlobalShortcutsProtocolManager::getAllShortcuts() {
std::vector<SShortcut> CGlobalShortcutsProtocol::getAllShortcuts() {
std::vector<SShortcut> copy;
for (auto& c : m_vClients) {
for (auto& sh : c->shortcuts) {
@ -143,9 +94,3 @@ std::vector<SShortcut> CGlobalShortcutsProtocolManager::getAllShortcuts() {
return copy;
}
void CGlobalShortcutsProtocolManager::destroyShortcut(wl_resource* resource) {
for (auto& c : m_vClients) {
std::erase_if(c->shortcuts, [&](const auto& other) { return other->resource == resource; });
}
}

View file

@ -1,39 +1,43 @@
#pragma once
#include "../defines.hpp"
#include "hyprland-global-shortcuts-v1-protocol.h"
#include "hyprland-global-shortcuts-v1.hpp"
#include "../protocols/WaylandProtocol.hpp"
#include <vector>
struct SShortcut {
wl_resource* resource;
SP<CHyprlandGlobalShortcutV1> resource;
std::string id, description, appid;
uint32_t shortcut = 0;
};
struct SShortcutClient {
wl_client* client = nullptr;
std::vector<std::unique_ptr<SShortcut>> shortcuts;
class CShortcutClient {
public:
CShortcutClient(SP<CHyprlandGlobalShortcutsManagerV1> resource);
bool good();
private:
SP<CHyprlandGlobalShortcutsManagerV1> resource;
std::vector<SP<SShortcut>> shortcuts;
friend class CGlobalShortcutsProtocol;
};
class CGlobalShortcutsProtocolManager {
class CGlobalShortcutsProtocol : IWaylandProtocol {
public:
CGlobalShortcutsProtocolManager();
CGlobalShortcutsProtocol(const wl_interface* iface, const int& ver, const std::string& name);
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
void displayDestroy();
void destroyResource(CShortcutClient* client);
void registerShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
const char* trigger_description);
void destroyShortcut(wl_resource* resource);
bool globalShortcutExists(std::string appid, std::string trigger);
void sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed);
bool isTaken(std::string id, std::string app_id);
std::vector<SShortcut> getAllShortcuts();
private:
std::vector<std::unique_ptr<SShortcutClient>> m_vClients;
SShortcutClient* clientFromWlClient(wl_client* client);
wl_global* m_pGlobal = nullptr;
wl_listener m_liDisplayDestroy;
std::vector<SP<CShortcutClient>> m_vClients;
};
namespace PROTO {
inline UP<CGlobalShortcutsProtocol> globalShortcuts;
};

View file

@ -107,14 +107,14 @@ CInputMethodPopupV2::CInputMethodPopupV2(SP<CZwpInputPopupSurfaceV2> resource_,
});
listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) {
if (pSurface->current.buffer && !mapped) {
if (pSurface->current.texture && !mapped) {
mapped = true;
pSurface->map();
events.map.emit();
return;
}
if (!pSurface->current.buffer && mapped) {
if (!pSurface->current.texture && mapped) {
mapped = false;
pSurface->unmap();
events.unmap.emit();

View file

@ -11,7 +11,7 @@ void CLayerShellResource::SState::reset() {
exclusive = 0;
interactivity = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
exclusiveEdge = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
exclusiveEdge = (zwlrLayerSurfaceV1Anchor)0;
desiredSize = {};
margin = {0, 0, 0, 0};
}
@ -38,11 +38,13 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
PROTO::layerShell->destroyResource(this);
});
listeners.unmapSurface = surf_->events.unmap.registerListener([this](std::any d) { events.unmap.emit(); });
listeners.commitSurface = surf_->events.commit.registerListener([this](std::any d) {
current = pending;
pending.committed = 0;
bool attachedBuffer = surface->current.buffer;
bool attachedBuffer = surface->current.texture;
if (attachedBuffer && !configured) {
surface->error(-1, "layerSurface was not configured, but a buffer was attached");
@ -71,8 +73,8 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
if (!attachedBuffer && mapped) {
mapped = false;
surface->unmap();
events.unmap.emit();
surface->unmap();
configured = false;
return;
}
@ -152,6 +154,16 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<C
});
resource->setSetExclusiveEdge([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1Anchor anchor) {
if (anchor > (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, "Invalid exclusive edge");
return;
}
if (anchor && (!pending.anchor || !(pending.anchor & anchor))) {
r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, "Exclusive edge doesn't align with anchor");
return;
}
pending.committed |= STATE_EDGE;
pending.exclusiveEdge = anchor;
});
@ -163,10 +175,6 @@ CLayerShellResource::~CLayerShellResource() {
surface->resetRole();
}
eSurfaceRole CLayerShellResource::role() {
return SURFACE_ROLE_LAYER_SHELL;
}
bool CLayerShellResource::good() {
return resource->resource();
}
@ -233,8 +241,12 @@ void CLayerShellProtocol::onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id
return;
}
SURF->role = RESOURCE;
SURF->role = makeShared<CLayerShellRole>(RESOURCE);
g_pCompositor->m_vLayers.emplace_back(CLayerSurface::create(RESOURCE));
LOGM(LOG, "New wlr_layer_surface {:x}", (uintptr_t)RESOURCE.get());
}
CLayerShellRole::CLayerShellRole(SP<CLayerShellResource> ls) : layerSurface(ls) {
;
}

View file

@ -12,8 +12,19 @@
class CMonitor;
class CWLSurfaceResource;
class CLayerShellResource;
class CLayerShellResource : public ISurfaceRole {
class CLayerShellRole : public ISurfaceRole {
public:
CLayerShellRole(SP<CLayerShellResource> ls);
virtual eSurfaceRole role() {
return SURFACE_ROLE_LAYER_SHELL;
}
WP<CLayerShellResource> layerSurface;
};
class CLayerShellResource {
public:
CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, SP<CWLSurfaceResource> surf_, std::string namespace_, CMonitor* pMonitor, zwlrLayerShellV1Layer layer);
~CLayerShellResource();
@ -21,7 +32,6 @@ class CLayerShellResource : public ISurfaceRole {
bool good();
void configure(const Vector2D& size);
void sendClosed();
virtual eSurfaceRole role();
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;

View file

@ -60,7 +60,7 @@ void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespe
if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION)
flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION;
if (data->wasPresented && when)
if (data->wasPresented)
resource->sendPresented((uint32_t)(when->tv_sec >> 32), (uint32_t)(when->tv_sec & 0xFFFFFFFF), (uint32_t)(when->tv_nsec), untilRefreshNs, (uint32_t)(seq >> 32),
(uint32_t)(seq & 0xFFFFFFFF), (wpPresentationFeedbackKind)flags);
else
@ -104,6 +104,14 @@ void CPresentationProtocol::onGetFeedback(CWpPresentation* pMgr, wl_resource* su
}
void CPresentationProtocol::onPresented(CMonitor* pMonitor, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) {
timespec now;
timespec* presentedAt = when;
if (!presentedAt) {
// just put the current time, we don't have anything better
clock_gettime(CLOCK_MONOTONIC, &now);
when = &now;
}
for (auto& feedback : m_vFeedbacks) {
if (!feedback->surface)
continue;

View file

@ -9,238 +9,39 @@
#include <algorithm>
#define SCREENCOPY_VERSION 3
#define LOGM PROTO::screencopy->protoLog
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
g_pProtocolManager->m_pScreencopyProtocolManager->bindManager(client, data, version, id);
CScreencopyFrame::~CScreencopyFrame() {
if (buffer && buffer->locked())
buffer->unlock();
}
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
g_pProtocolManager->m_pScreencopyProtocolManager->displayDestroy();
}
CScreencopyFrame::CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource_, int32_t overlay_cursor, wl_resource* output, CBox box_) : resource(resource_) {
if (!good())
return;
void CScreencopyProtocolManager::displayDestroy() {
wl_global_destroy(m_pGlobal);
}
overlayCursor = !!overlay_cursor;
pMonitor = CWLOutputResource::fromResource(output)->monitor.get();
static SScreencopyFrame* frameFromResource(wl_resource*);
CScreencopyProtocolManager::CScreencopyProtocolManager() {
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &zwlr_screencopy_manager_v1_interface, SCREENCOPY_VERSION, this, bindManagerInt);
if (!m_pGlobal) {
Debug::log(ERR, "ScreencopyProtocolManager could not start! Screensharing will not work!");
if (!pMonitor) {
LOGM(ERR, "Client requested sharing of a monitor that doesnt exist");
resource->sendFailed();
PROTO::screencopy->destroyResource(this);
return;
}
m_liDisplayDestroy.notify = handleDisplayDestroy;
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
Debug::log(LOG, "ScreencopyProtocolManager started successfully!");
m_pSoftwareCursorTimer = makeShared<CEventLoopTimer>(
std::nullopt,
[this](SP<CEventLoopTimer> self, void* data) {
// TODO: make it per-monitor
for (auto& m : g_pCompositor->m_vMonitors) {
g_pPointerManager->unlockSoftwareForMonitor(m);
}
m_bTimerArmed = false;
Debug::log(LOG, "[screencopy] Releasing software cursor lock");
},
nullptr);
g_pEventLoopManager->addTimer(m_pSoftwareCursorTimer);
}
static void handleCaptureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output) {
g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output);
}
static void handleCaptureRegion(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, int32_t x, int32_t y, int32_t width,
int32_t height) {
g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output, {x, y, width, height});
}
static void handleDestroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) {
const auto PFRAME = frameFromResource(resource);
if (!PFRAME)
return;
g_pProtocolManager->m_pScreencopyProtocolManager->copyFrame(client, resource, buffer);
}
static void handleCopyWithDamage(wl_client* client, wl_resource* resource, wl_resource* buffer) {
const auto PFRAME = frameFromResource(resource);
if (!PFRAME)
return;
PFRAME->withDamage = true;
handleCopyFrame(client, resource, buffer);
}
static void handleDestroyFrame(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct zwlr_screencopy_manager_v1_interface screencopyMgrImpl = {
.capture_output = handleCaptureOutput,
.capture_output_region = handleCaptureRegion,
.destroy = handleDestroy,
};
static const struct zwlr_screencopy_frame_v1_interface screencopyFrameImpl = {
.copy = handleCopyFrame,
.destroy = handleDestroyFrame,
.copy_with_damage = handleCopyWithDamage,
};
static CScreencopyClient* clientFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_manager_v1_interface, &screencopyMgrImpl));
return (CScreencopyClient*)wl_resource_get_user_data(resource);
}
static SScreencopyFrame* frameFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_frame_v1_interface, &screencopyFrameImpl));
return (SScreencopyFrame*)wl_resource_get_user_data(resource);
}
void CScreencopyProtocolManager::removeClient(CScreencopyClient* client, bool force) {
if (!client)
return;
if (!force) {
if (!client || client->ref <= 0)
return;
if (--client->ref != 0)
return;
}
m_lClients.remove(*client); // TODO: this doesn't get cleaned up after sharing app exits???
for (auto& f : m_lFrames) {
// avoid dangling ptrs
if (f.client == client)
f.client = nullptr;
}
}
static void handleManagerResourceDestroy(wl_resource* resource) {
const auto PCLIENT = clientFromResource(resource);
g_pProtocolManager->m_pScreencopyProtocolManager->removeClient(PCLIENT, true);
}
CScreencopyClient::~CScreencopyClient() {
g_pHookSystem->unhook(tickCallback);
}
CScreencopyClient::CScreencopyClient() {
lastMeasure.reset();
lastFrame.reset();
tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); });
}
void CScreencopyClient::onTick() {
if (lastMeasure.getMillis() < 500)
return;
framesInLastHalfSecond = frameCounter;
frameCounter = 0;
lastMeasure.reset();
const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0;
const bool FRAMEAWAITING = std::ranges::any_of(g_pProtocolManager->m_pScreencopyProtocolManager->m_lFrames, [&](const auto& frame) { return frame.client == this; }) ||
std::ranges::any_of(g_pProtocolManager->m_pToplevelExportProtocolManager->m_lFrames, [&](const auto& frame) { return frame.client == this; });
if (framesInLastHalfSecond > 3 && !sentScreencast) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)});
sentScreencast = true;
} else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)});
sentScreencast = false;
}
}
void CScreencopyProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
const auto PCLIENT = &m_lClients.emplace_back();
PCLIENT->resource = wl_resource_create(client, &zwlr_screencopy_manager_v1_interface, version, id);
if (!PCLIENT->resource) {
Debug::log(ERR, "ScreencopyProtocolManager could not bind! (out of memory?)");
m_lClients.remove(*PCLIENT);
wl_client_post_no_memory(client);
return;
}
PCLIENT->ref = 1;
wl_resource_set_implementation(PCLIENT->resource, &screencopyMgrImpl, PCLIENT, handleManagerResourceDestroy);
Debug::log(LOG, "ScreencopyProtocolManager bound successfully!");
}
static void handleFrameResourceDestroy(wl_resource* resource) {
const auto PFRAME = frameFromResource(resource);
g_pProtocolManager->m_pScreencopyProtocolManager->removeFrame(PFRAME);
}
void CScreencopyProtocolManager::removeFrame(SScreencopyFrame* frame, bool force) {
if (!frame)
return;
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; });
wl_resource_set_user_data(frame->resource, nullptr);
if (frame->buffer && frame->buffer->locked())
frame->buffer->unlock();
removeClient(frame->client, force);
m_lFrames.remove(*frame);
}
void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, CBox box) {
const auto PCLIENT = clientFromResource(resource);
const auto PFRAME = &m_lFrames.emplace_back();
PFRAME->overlayCursor = !!overlay_cursor;
PFRAME->resource = wl_resource_create(client, &zwlr_screencopy_frame_v1_interface, wl_resource_get_version(resource), frame);
PFRAME->pMonitor = CWLOutputResource::fromResource(output)->monitor.get();
if (!PFRAME->pMonitor) {
Debug::log(ERR, "client requested sharing of a monitor that doesnt exist");
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
return;
}
if (!PFRAME->resource) {
Debug::log(ERR, "Couldn't alloc frame for sharing! (no memory)");
removeFrame(PFRAME);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(PFRAME->resource, &screencopyFrameImpl, PFRAME, handleFrameResourceDestroy);
PFRAME->client = PCLIENT;
PCLIENT->ref++;
resource->setOnDestroy([this](CZwlrScreencopyFrameV1* pMgr) { PROTO::screencopy->destroyResource(this); });
resource->setDestroy([this](CZwlrScreencopyFrameV1* pFrame) { PROTO::screencopy->destroyResource(this); });
resource->setCopy([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { this->copy(pFrame, res); });
resource->setCopyWithDamage([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) {
withDamage = true;
this->copy(pFrame, res);
});
g_pHyprRenderer->makeEGLCurrent();
if (g_pHyprOpenGL->m_mMonitorRenderResources.contains(PFRAME->pMonitor)) {
const auto& RDATA = g_pHyprOpenGL->m_mMonitorRenderResources.at(PFRAME->pMonitor);
if (g_pHyprOpenGL->m_mMonitorRenderResources.contains(pMonitor)) {
const auto& RDATA = g_pHyprOpenGL->m_mMonitorRenderResources.at(pMonitor);
// bind the fb for its format. Suppress gl errors.
#ifndef GLES2
glBindFramebuffer(GL_READ_FRAMEBUFFER, RDATA.offloadFB.m_iFb);
@ -248,246 +49,212 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r
glBindFramebuffer(GL_FRAMEBUFFER, RDATA.offloadFB.m_iFb);
#endif
} else
Debug::log(ERR, "No RDATA in screencopy???");
LOGM(ERR, "No RDATA in screencopy???");
PFRAME->shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PFRAME->pMonitor);
if (PFRAME->shmFormat == DRM_FORMAT_INVALID) {
Debug::log(ERR, "No format supported by renderer in capture output");
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
shmFormat = g_pHyprOpenGL->getPreferredReadFormat(pMonitor);
if (shmFormat == DRM_FORMAT_INVALID) {
LOGM(ERR, "No format supported by renderer in capture output");
resource->sendFailed();
PROTO::screencopy->destroyResource(this);
return;
}
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(PFRAME->shmFormat);
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(shmFormat);
if (!PSHMINFO) {
Debug::log(ERR, "No pixel format supported by renderer in capture output");
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
LOGM(ERR, "No pixel format supported by renderer in capture output");
resource->sendFailed();
PROTO::screencopy->destroyResource(this);
return;
}
PFRAME->dmabufFormat = PFRAME->pMonitor->output->state->state().drmFormat;
dmabufFormat = pMonitor->output->state->state().drmFormat;
if (box.width == 0 && box.height == 0)
PFRAME->box = {0, 0, (int)(PFRAME->pMonitor->vecSize.x), (int)(PFRAME->pMonitor->vecSize.y)};
if (box_.width == 0 && box_.height == 0)
box = {0, 0, (int)(pMonitor->vecSize.x), (int)(pMonitor->vecSize.y)};
else {
PFRAME->box = box;
box = box_;
}
PFRAME->box.transform(wlTransformToHyprutils(PFRAME->pMonitor->transform), PFRAME->pMonitor->vecTransformedSize.x, PFRAME->pMonitor->vecTransformedSize.y)
.scale(PFRAME->pMonitor->scale)
.round();
box.transform(wlTransformToHyprutils(pMonitor->transform), pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y).scale(pMonitor->scale).round();
PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w);
shmStride = FormatUtils::minStride(PSHMINFO, box.w);
zwlr_screencopy_frame_v1_send_buffer(PFRAME->resource, FormatUtils::drmToShm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride);
resource->sendBuffer(FormatUtils::drmToShm(shmFormat), box.width, box.height, shmStride);
if (wl_resource_get_version(resource) >= 3) {
if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) {
zwlr_screencopy_frame_v1_send_linux_dmabuf(PFRAME->resource, PFRAME->dmabufFormat, PFRAME->box.width, PFRAME->box.height);
if (resource->version() >= 3) {
if (dmabufFormat != DRM_FORMAT_INVALID) {
resource->sendLinuxDmabuf(dmabufFormat, box.width, box.height);
}
zwlr_screencopy_frame_v1_send_buffer_done(PFRAME->resource);
resource->sendBufferDone();
}
}
void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) {
const auto PFRAME = frameFromResource(resource);
if (!PFRAME) {
Debug::log(ERR, "No frame in copyFrame??");
void CScreencopyFrame::copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer_) {
if (!good()) {
LOGM(ERR, "No frame in copyFrame??");
return;
}
if (!g_pCompositor->monitorExists(PFRAME->pMonitor)) {
Debug::log(ERR, "client requested sharing of a monitor that is gone");
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
if (!g_pCompositor->monitorExists(pMonitor)) {
LOGM(ERR, "Client requested sharing of a monitor that is gone");
resource->sendFailed();
PROTO::screencopy->destroyResource(this);
return;
}
const auto PBUFFER = CWLBufferResource::fromResource(buffer);
const auto PBUFFER = CWLBufferResource::fromResource(buffer_);
if (!PBUFFER) {
Debug::log(ERR, "[sc] invalid buffer in {:x}", (uintptr_t)PFRAME);
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
removeFrame(PFRAME);
LOGM(ERR, "Invalid buffer in {:x}", (uintptr_t)this);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
PROTO::screencopy->destroyResource(this);
return;
}
PBUFFER->buffer->lock();
if (PBUFFER->buffer->size != PFRAME->box.size()) {
Debug::log(ERR, "[sc] invalid dimensions in {:x}", (uintptr_t)PFRAME);
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
removeFrame(PFRAME);
if (PBUFFER->buffer->size != box.size()) {
LOGM(ERR, "Invalid dimensions in {:x}", (uintptr_t)this);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
PROTO::screencopy->destroyResource(this);
return;
}
if (PFRAME->buffer) {
Debug::log(ERR, "[sc] buffer used in {:x}", (uintptr_t)PFRAME);
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
removeFrame(PFRAME);
if (buffer) {
LOGM(ERR, "Buffer used in {:x}", (uintptr_t)this);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
PROTO::screencopy->destroyResource(this);
return;
}
if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) {
PFRAME->bufferDMA = true;
bufferDMA = true;
if (attrs.format != PFRAME->dmabufFormat) {
Debug::log(ERR, "[sc] invalid buffer dma format in {:x}", (uintptr_t)PFRAME);
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
removeFrame(PFRAME);
if (attrs.format != dmabufFormat) {
LOGM(ERR, "Invalid buffer dma format in {:x}", (uintptr_t)pFrame);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
PROTO::screencopy->destroyResource(this);
return;
}
} else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) {
if (attrs.format != PFRAME->shmFormat) {
Debug::log(ERR, "[sc] invalid buffer shm format in {:x}", (uintptr_t)PFRAME);
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
removeFrame(PFRAME);
if (attrs.format != shmFormat) {
LOGM(ERR, "Invalid buffer shm format in {:x}", (uintptr_t)pFrame);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
PROTO::screencopy->destroyResource(this);
return;
} else if ((int)attrs.stride != PFRAME->shmStride) {
Debug::log(ERR, "[sc] invalid buffer shm stride in {:x}", (uintptr_t)PFRAME);
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
removeFrame(PFRAME);
} else if ((int)attrs.stride != shmStride) {
LOGM(ERR, "Invalid buffer shm stride in {:x}", (uintptr_t)pFrame);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
PROTO::screencopy->destroyResource(this);
return;
}
} else {
Debug::log(ERR, "[sc] invalid buffer type in {:x}", (uintptr_t)PFRAME);
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
removeFrame(PFRAME);
LOGM(ERR, "Invalid buffer type in {:x}", (uintptr_t)pFrame);
resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
PROTO::screencopy->destroyResource(this);
return;
}
PFRAME->buffer = PBUFFER->buffer;
buffer = PBUFFER->buffer;
m_vFramesAwaitingWrite.emplace_back(PFRAME);
PROTO::screencopy->m_vFramesAwaitingWrite.emplace_back(self);
g_pHyprRenderer->m_bDirectScanoutBlocked = true;
if (PFRAME->overlayCursor && !PFRAME->lockedSWCursors) {
PFRAME->lockedSWCursors = true;
if (overlayCursor && !lockedSWCursors) {
lockedSWCursors = true;
// TODO: make it per-monitor
if (!m_bTimerArmed) {
if (!PROTO::screencopy->m_bTimerArmed) {
for (auto& m : g_pCompositor->m_vMonitors) {
g_pPointerManager->lockSoftwareForMonitor(m);
}
m_bTimerArmed = true;
Debug::log(LOG, "[screencopy] Locking sw cursors due to screensharing");
PROTO::screencopy->m_bTimerArmed = true;
LOGM(LOG, "Locking sw cursors due to screensharing");
}
m_pSoftwareCursorTimer->updateTimeout(std::chrono::seconds(1));
PROTO::screencopy->m_pSoftwareCursorTimer->updateTimeout(std::chrono::seconds(1));
}
if (!PFRAME->withDamage)
g_pHyprRenderer->damageMonitor(PFRAME->pMonitor);
if (!withDamage)
g_pHyprRenderer->damageMonitor(pMonitor);
}
void CScreencopyProtocolManager::onOutputCommit(CMonitor* pMonitor) {
m_pLastMonitorBackBuffer = pMonitor->output->state->state().buffer;
shareAllFrames(pMonitor);
m_pLastMonitorBackBuffer.reset();
}
void CScreencopyProtocolManager::shareAllFrames(CMonitor* pMonitor) {
if (m_vFramesAwaitingWrite.empty())
return; // nothing to share
std::vector<SScreencopyFrame*> framesToRemove;
// share frame if correct output
for (auto& f : m_vFramesAwaitingWrite) {
if (!f->pMonitor || !f->buffer) {
framesToRemove.push_back(f);
continue;
}
if (f->pMonitor != pMonitor)
continue;
shareFrame(f);
f->client->lastFrame.reset();
++f->client->frameCounter;
framesToRemove.push_back(f);
}
for (auto& f : framesToRemove) {
removeFrame(f);
}
if (m_vFramesAwaitingWrite.empty()) {
g_pHyprRenderer->m_bDirectScanoutBlocked = false;
}
}
void CScreencopyProtocolManager::shareFrame(SScreencopyFrame* frame) {
if (!frame->buffer)
void CScreencopyFrame::share() {
if (!buffer || !pMonitor)
return;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t flags = 0;
if (frame->bufferDMA) {
if (!copyFrameDmabuf(frame)) {
Debug::log(ERR, "[sc] dmabuf copy failed in {:x}", (uintptr_t)frame);
zwlr_screencopy_frame_v1_send_failed(frame->resource);
if (bufferDMA) {
if (!copyDmabuf()) {
LOGM(ERR, "Dmabuf copy failed in {:x}", (uintptr_t)this);
resource->sendFailed();
return;
}
} else {
if (!copyFrameShm(frame, &now)) {
Debug::log(ERR, "[sc] shm copy failed in {:x}", (uintptr_t)frame);
zwlr_screencopy_frame_v1_send_failed(frame->resource);
if (!copyShm()) {
LOGM(ERR, "Shm copy failed in {:x}", (uintptr_t)this);
resource->sendFailed();
return;
}
}
zwlr_screencopy_frame_v1_send_flags(frame->resource, flags);
sendFrameDamage(frame);
resource->sendFlags((zwlrScreencopyFrameV1Flags)0);
if (withDamage) {
// TODO: add a damage ring for this.
resource->sendDamage(0, 0, buffer->size.x, buffer->size.y);
}
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
zwlr_screencopy_frame_v1_send_ready(frame->resource, tvSecHi, tvSecLo, now.tv_nsec);
resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec);
}
void CScreencopyProtocolManager::sendFrameDamage(SScreencopyFrame* frame) {
if (!frame->withDamage)
return;
bool CScreencopyFrame::copyDmabuf() {
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
// TODO:
// add a damage ring for this.
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
// for (auto& RECT : frame->pMonitor->lastFrameDamage.getRects()) {
// if (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;
// }
// 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));
// }
zwlr_screencopy_frame_v1_send_damage(frame->resource, 0, 0, frame->buffer->size.x, frame->buffer->size.y);
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;
}
bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) {
auto TEXTURE = makeShared<CTexture>(m_pLastMonitorBackBuffer);
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);
auto shm = frame->buffer->shm();
auto [pixelData, fmt, bufLen] = frame->buffer->beginDataPtr(0); // no need for end, cuz it's shm
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
g_pHyprRenderer->endRender();
LOGM(TRACE, "Copied frame via dma");
return true;
}
bool CScreencopyFrame::copyShm() {
auto TEXTURE = makeShared<CTexture>(pMonitor->output->state->state().buffer);
auto shm = buffer->shm();
auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
g_pHyprRenderer->makeEGLCurrent();
CFramebuffer fb;
fb.alloc(frame->box.w, frame->box.h, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : frame->pMonitor->output->state->state().drmFormat);
fb.alloc(box.w, box.h, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : pMonitor->output->state->state().drmFormat);
if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb, true)) {
Debug::log(ERR, "Screencopy: can't copy: failed to begin rendering");
if (!g_pHyprRenderer->beginRender(pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb, true)) {
LOGM(ERR, "Can't copy: failed to begin rendering");
return false;
}
CBox monbox = CBox{0, 0, frame->pMonitor->vecTransformedSize.x, frame->pMonitor->vecTransformedSize.y}.translate({-frame->box.x, -frame->box.y});
CBox monbox = CBox{0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}.translate({-box.x, -box.y});
g_pHyprOpenGL->setMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false);
g_pHyprOpenGL->renderTexture(TEXTURE, &monbox, 1);
@ -502,7 +269,7 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec*
const auto PFORMAT = FormatUtils::getPixelFormatFromDRM(shm.format);
if (!PFORMAT) {
Debug::log(ERR, "Screencopy: can't copy: failed to find a pixel format");
LOGM(ERR, "Can't copy: failed to find a pixel format");
g_pHyprRenderer->endRender();
return false;
}
@ -513,53 +280,164 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec*
g_pHyprRenderer->endRender();
g_pHyprRenderer->makeEGLCurrent();
g_pHyprOpenGL->m_RenderData.pMonitor = frame->pMonitor;
g_pHyprOpenGL->m_RenderData.pMonitor = pMonitor;
fb.bind();
glPixelStorei(GL_PACK_ALIGNMENT, 1);
const auto drmFmt = FormatUtils::getPixelFormatFromDRM(shm.format);
uint32_t packStride = FormatUtils::minStride(drmFmt, frame->box.w);
uint32_t packStride = FormatUtils::minStride(drmFmt, box.w);
if (packStride == (uint32_t)shm.stride) {
glReadPixels(0, 0, frame->box.w, frame->box.h, glFormat, PFORMAT->glType, pixelData);
glReadPixels(0, 0, box.w, box.h, glFormat, PFORMAT->glType, pixelData);
} else {
for (size_t i = 0; i < frame->box.h; ++i) {
for (size_t i = 0; i < box.h; ++i) {
uint32_t y = i;
glReadPixels(0, y, frame->box.w, 1, glFormat, PFORMAT->glType, ((unsigned char*)pixelData) + i * shm.stride);
glReadPixels(0, y, box.w, 1, glFormat, PFORMAT->glType, ((unsigned char*)pixelData) + i * shm.stride);
}
}
g_pHyprOpenGL->m_RenderData.pMonitor = nullptr;
Debug::log(TRACE, "Screencopy: copied frame via shm");
LOGM(TRACE, "Copied frame via shm");
return true;
}
bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) {
auto TEXTURE = makeShared<CTexture>(m_pLastMonitorBackBuffer);
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
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;
bool CScreencopyFrame::good() {
return resource->resource();
}
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;
CScreencopyClient::~CScreencopyClient() {
g_pHookSystem->unhook(tickCallback);
}
CScreencopyClient::CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_) : resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); });
resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); });
resource->setCaptureOutput(
[this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output) { this->captureOutput(frame, overlayCursor, output, {}); });
resource->setCaptureOutputRegion([this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output, int32_t x, int32_t y, int32_t w,
int32_t h) { this->captureOutput(frame, overlayCursor, output, {x, y, w, h}); });
lastMeasure.reset();
lastFrame.reset();
tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); });
}
void CScreencopyClient::captureOutput(uint32_t frame, int32_t overlayCursor_, wl_resource* output, CBox box) {
const auto FRAME = PROTO::screencopy->m_vFrames.emplace_back(
makeShared<CScreencopyFrame>(makeShared<CZwlrScreencopyFrameV1>(resource->client(), resource->version(), frame), overlayCursor_, output, box));
if (!FRAME->good()) {
LOGM(ERR, "Couldn't alloc frame for sharing! (no memory)");
resource->noMemory();
PROTO::screencopy->destroyResource(FRAME.get());
return;
}
FRAME->self = FRAME;
FRAME->client = self;
}
void CScreencopyClient::onTick() {
if (lastMeasure.getMillis() < 500)
return;
framesInLastHalfSecond = frameCounter;
frameCounter = 0;
lastMeasure.reset();
const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0;
const bool FRAMEAWAITING = std::ranges::any_of(PROTO::screencopy->m_vFrames, [&](const auto& frame) { return frame->client.get() == this; });
if (framesInLastHalfSecond > 3 && !sentScreencast) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)});
sentScreencast = true;
} else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)});
sentScreencast = false;
}
}
bool CScreencopyClient::good() {
return resource->resource();
}
CScreencopyProtocol::CScreencopyProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
m_pSoftwareCursorTimer = makeShared<CEventLoopTimer>(
std::nullopt,
[this](SP<CEventLoopTimer> self, void* data) {
// TODO: make it per-monitor
for (auto& m : g_pCompositor->m_vMonitors) {
g_pPointerManager->unlockSoftwareForMonitor(m);
}
m_bTimerArmed = false;
LOGM(LOG, "Releasing software cursor lock");
},
nullptr);
g_pEventLoopManager->addTimer(m_pSoftwareCursorTimer);
}
void CScreencopyProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto CLIENT = m_vClients.emplace_back(makeShared<CScreencopyClient>(makeShared<CZwlrScreencopyManagerV1>(client, ver, id)));
if (!CLIENT->good()) {
LOGM(LOG, "Failed to bind client! (out of memory)");
CLIENT->resource->noMemory();
m_vClients.pop_back();
return;
}
CLIENT->self = CLIENT;
LOGM(LOG, "Bound client successfully!");
}
void CScreencopyProtocol::destroyResource(CScreencopyClient* client) {
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
std::erase_if(m_vFrames, [&](const auto& other) { return other->client.get() == client; });
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other->client.get() == client; });
}
void CScreencopyProtocol::destroyResource(CScreencopyFrame* frame) {
std::erase_if(m_vFrames, [&](const auto& other) { return other.get() == frame; });
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other.get() == frame; });
}
void CScreencopyProtocol::onOutputCommit(CMonitor* pMonitor) {
if (m_vFramesAwaitingWrite.empty()) {
g_pHyprRenderer->m_bDirectScanoutBlocked = false;
return; // nothing to share
}
std::vector<WP<CScreencopyFrame>> framesToRemove;
// share frame if correct output
for (auto& f : m_vFramesAwaitingWrite) {
if (!f->pMonitor || !f->buffer) {
framesToRemove.push_back(f);
continue;
}
if (f->pMonitor != pMonitor)
continue;
f->share();
f->client->lastFrame.reset();
++f->client->frameCounter;
framesToRemove.push_back(f);
}
for (auto& f : framesToRemove) {
destroyResource(f.get());
}
}

View file

@ -1,7 +1,8 @@
#pragma once
#include "../defines.hpp"
#include "wlr-screencopy-unstable-v1-protocol.h"
#include "wlr-screencopy-unstable-v1.hpp"
#include "WaylandProtocol.hpp"
#include <list>
#include <vector>
@ -20,87 +21,93 @@ enum eClientOwners {
class CScreencopyClient {
public:
CScreencopyClient();
CScreencopyClient(SP<CZwlrScreencopyManagerV1> resource_);
~CScreencopyClient();
int ref = 0;
wl_resource* resource = nullptr;
bool good();
WP<CScreencopyClient> self;
eClientOwners clientOwner = CLIENT_SCREENCOPY;
CTimer lastFrame;
int frameCounter = 0;
private:
SP<CZwlrScreencopyManagerV1> resource;
int framesInLastHalfSecond = 0;
CTimer lastMeasure;
CTimer lastFrame;
bool sentScreencast = false;
void onTick();
SP<HOOK_CALLBACK_FN> tickCallback;
void onTick();
bool operator==(const CScreencopyClient& other) const {
return resource == other.resource;
}
void captureOutput(uint32_t frame, int32_t overlayCursor, wl_resource* output, CBox box);
friend class CScreencopyProtocol;
};
struct SScreencopyFrame {
wl_resource* resource = nullptr;
CScreencopyClient* client = nullptr;
class CScreencopyFrame {
public:
CScreencopyFrame(SP<CZwlrScreencopyFrameV1> resource, int32_t overlay_cursor, wl_resource* output, CBox box);
~CScreencopyFrame();
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;
CBox box = {};
int shmStride = 0;
bool good();
SP<CScreencopyFrame> self;
WP<CScreencopyClient> client;
private:
SP<CZwlrScreencopyFrameV1> resource;
CMonitor* pMonitor = nullptr;
bool overlayCursor = false;
bool withDamage = false;
bool lockedSWCursors = false;
bool bufferDMA = false;
WP<IHLBuffer> buffer;
bool bufferDMA = false;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;
int shmStride = 0;
CBox box = {};
CMonitor* pMonitor = nullptr;
PHLWINDOWREF pWindow;
void copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer);
bool copyDmabuf();
bool copyShm();
void share();
bool operator==(const SScreencopyFrame& other) const {
return resource == other.resource && client == other.client;
}
friend class CScreencopyProtocol;
};
class CScreencopyProtocolManager {
class CScreencopyProtocol : public IWaylandProtocol {
public:
CScreencopyProtocolManager();
CScreencopyProtocol(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 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);
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:
wl_global* m_pGlobal = nullptr;
std::list<SScreencopyFrame> m_lFrames;
std::list<CScreencopyClient> m_lClients;
std::vector<SP<CScreencopyFrame>> m_vFrames;
std::vector<SP<CScreencopyFrame>> m_vFramesAwaitingWrite;
std::vector<SP<CScreencopyClient>> m_vClients;
SP<CEventLoopTimer> m_pSoftwareCursorTimer;
bool m_bTimerArmed = false;
wl_listener m_liDisplayDestroy;
std::vector<SScreencopyFrame*> m_vFramesAwaitingWrite;
SP<Aquamarine::IBuffer> m_pLastMonitorBackBuffer;
void shareAllFrames(CMonitor* pMonitor);
void shareFrame(SScreencopyFrame* frame);
void sendFrameDamage(SScreencopyFrame* frame);
bool copyFrameDmabuf(SScreencopyFrame* frame);
bool copyFrameShm(SScreencopyFrame* frame, timespec* now);
void shareFrame(CScreencopyFrame* frame);
void sendFrameDamage(CScreencopyFrame* frame);
bool copyFrameDmabuf(CScreencopyFrame* frame);
bool copyFrameShm(CScreencopyFrame* frame, timespec* now);
friend class CScreencopyFrame;
friend class CScreencopyClient;
};
namespace PROTO {
inline UP<CScreencopyProtocol> screencopy;
};

View file

@ -24,7 +24,7 @@ CSessionLockSurface::CSessionLockSurface(SP<CExtSessionLockSurfaceV1> resource_,
resource->setAckConfigure([this](CExtSessionLockSurfaceV1* r, uint32_t serial) { ackdConfigure = true; });
listeners.surfaceCommit = pSurface->events.commit.registerListener([this](std::any d) {
if (!pSurface->current.buffer) {
if (!pSurface->current.texture) {
LOGM(ERR, "SessionLock attached a null buffer");
resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "Null buffer attached");
return;

View file

@ -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;

View file

@ -3,221 +3,125 @@
#include "../Compositor.hpp"
#include "core/Compositor.hpp"
#define TEXT_INPUT_VERSION 1
#define LOGM PROTO::textInputV1->protoLog
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->bindManager(client, data, version, id);
CTextInputV1::~CTextInputV1() {
events.destroy.emit();
}
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->displayDestroy();
}
void CTextInputV1ProtocolManager::displayDestroy() {
wl_global_destroy(m_pGlobal);
}
CTextInputV1ProtocolManager::CTextInputV1ProtocolManager() {
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &zwp_text_input_manager_v1_interface, TEXT_INPUT_VERSION, this, bindManagerInt);
if (!m_pGlobal) {
Debug::log(ERR, "TextInputV1Manager could not start!");
return;
}
m_liDisplayDestroy.notify = handleDisplayDestroy;
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
Debug::log(LOG, "TextInputV1Manager started successfully!");
}
static void createTI(wl_client* client, wl_resource* resource, uint32_t id) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->createTI(client, resource, id);
}
static const struct zwp_text_input_manager_v1_interface textInputManagerImpl = {
.create_text_input = createTI,
};
void CTextInputV1ProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
const auto RESOURCE = wl_resource_create(client, &zwp_text_input_manager_v1_interface, version, id);
wl_resource_set_implementation(RESOURCE, &textInputManagerImpl, this, nullptr);
Debug::log(LOG, "TextInputV1Manager bound successfully!");
}
//
static void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleActivate(client, resource, seat, surface);
}
static void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleDeactivate(client, resource, seat);
}
static void handleShowInputPanel(wl_client* client, wl_resource* resource) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleShowInputPanel(client, resource);
}
static void handleHideInputPanel(wl_client* client, wl_resource* resource) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleHideInputPanel(client, resource);
}
static void handleReset(wl_client* client, wl_resource* resource) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleReset(client, resource);
}
static void handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetSurroundingText(client, resource, text, cursor, anchor);
}
static void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetContentType(client, resource, hint, purpose);
}
static void handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetCursorRectangle(client, resource, x, y, width, height);
}
static void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleSetPreferredLanguage(client, resource, language);
}
static void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleCommitState(client, resource, serial);
}
static void handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index) {
g_pProtocolManager->m_pTextInputV1ProtocolManager->handleInvokeAction(client, resource, button, index);
}
static const struct zwp_text_input_v1_interface textInputImpl = {
.activate = handleActivate,
.deactivate = handleDeactivate,
.show_input_panel = handleShowInputPanel,
.hide_input_panel = handleHideInputPanel,
.reset = handleReset,
.set_surrounding_text = handleSetSurroundingText,
.set_content_type = handleSetContentType,
.set_cursor_rectangle = handleSetCursorRectangle,
.set_preferred_language = handleSetPreferredLanguage,
.commit_state = handleCommitState,
.invoke_action = handleInvokeAction,
};
void CTextInputV1ProtocolManager::removeTI(STextInputV1* pTI) {
const auto TI = std::find_if(m_pClients.begin(), m_pClients.end(), [&](const auto& other) { return other->resourceCaller == pTI->resourceCaller; });
if (TI == m_pClients.end())
CTextInputV1::CTextInputV1(SP<CZwpTextInputV1> resource_) : resource(resource_) {
if (!good())
return;
// if ((*TI)->resourceImpl)
// wl_resource_destroy((*TI)->resourceImpl);
resource->setOnDestroy([this](CZwpTextInputV1* pMgr) { PROTO::textInputV1->destroyResource(this); });
std::erase_if(m_pClients, [&](const auto& other) { return other.get() == pTI; });
}
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);
}
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);
}
void CTextInputV1ProtocolManager::createTI(wl_client* client, wl_resource* resource, uint32_t id) {
const auto PTI = m_pClients.emplace_back(std::make_unique<STextInputV1>()).get();
Debug::log(LOG, "New TI V1 at {:x}", (uintptr_t)PTI);
PTI->client = client;
PTI->resourceCaller = resource;
PTI->resourceImpl = wl_resource_create(client, &zwp_text_input_v1_interface, TEXT_INPUT_VERSION, id);
if (!PTI->resourceImpl) {
Debug::log(ERR, "Could not alloc wl_resource for TIV1");
removeTI(PTI);
return;
}
wl_resource_set_implementation(PTI->resourceImpl, &textInputImpl, PTI, &destroyTI);
wl_resource_set_user_data(PTI->resourceImpl, PTI);
wl_signal_init(&PTI->sEnable);
wl_signal_init(&PTI->sDisable);
wl_signal_init(&PTI->sDestroy);
wl_signal_init(&PTI->sCommit);
g_pInputManager->m_sIMERelay.onNewTextInput(PTI);
}
void CTextInputV1ProtocolManager::handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface) {
const auto PTI = tiFromResource(resource);
resource->setActivate([this](CZwpTextInputV1* pMgr, wl_resource* seat, wl_resource* surface) {
if (!surface) {
Debug::log(WARN, "Text-input-v1 PTI{:x}: No surface to activate text input on!", (uintptr_t)PTI);
LOGM(WARN, "Text-input-v1 PTI{:x}: No surface to activate text input on!", (uintptr_t)this);
return;
}
PTI->active = true;
PTI->pTextInput->onEnabled(CWLSurfaceResource::fromResource(surface));
}
void CTextInputV1ProtocolManager::handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) {
const auto PTI = tiFromResource(resource);
PTI->active = false;
PTI->pTextInput->onDisabled();
}
active = true;
events.enable.emit(CWLSurfaceResource::fromResource(surface));
});
void CTextInputV1ProtocolManager::handleShowInputPanel(wl_client* client, wl_resource* resource) {
;
}
resource->setDeactivate([this](CZwpTextInputV1* pMgr, wl_resource* seat) {
active = false;
events.disable.emit();
});
void CTextInputV1ProtocolManager::handleHideInputPanel(wl_client* client, wl_resource* resource) {
;
}
resource->setReset([this](CZwpTextInputV1* pMgr) {
pendingSurrounding.isPending = false;
pendingContentType.isPending = false;
});
void CTextInputV1ProtocolManager::handleReset(wl_client* client, wl_resource* resource) {
const auto PTI = tiFromResource(resource);
PTI->pendingSurrounding.isPending = false;
PTI->pendingContentType.isPending = false;
}
resource->setSetSurroundingText(
[this](CZwpTextInputV1* pMgr, const char* text, uint32_t cursor, uint32_t anchor) { pendingSurrounding = {true, std::string(text), cursor, anchor}; });
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 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,
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) {});
}
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};
bool CTextInputV1::good() {
return resource->resource();
}
void CTextInputV1ProtocolManager::handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language) {
wl_client* CTextInputV1::client() {
return resource->client();
}
void CTextInputV1::enter(SP<CWLSurfaceResource> surface) {
resource->sendEnter(surface->getResource()->resource());
active = true;
}
void CTextInputV1::leave() {
resource->sendLeave();
active = false;
}
void CTextInputV1::preeditCursor(int32_t index) {
resource->sendPreeditCursor(index);
}
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::handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial) {
const auto PTI = tiFromResource(resource);
PTI->serial = serial;
PTI->pTextInput->onCommit();
void CTextInputV1Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CZwpTextInputManagerV1>(client, ver, id));
RESOURCE->setOnDestroy([this](CZwpTextInputManagerV1* pMgr) { PROTO::textInputV1->destroyResource(pMgr); });
RESOURCE->setCreateTextInput([this](CZwpTextInputManagerV1* pMgr, uint32_t id) {
const auto PTI = m_vClients.emplace_back(makeShared<CTextInputV1>(makeShared<CZwpTextInputV1>(pMgr->client(), pMgr->version(), id)));
LOGM(LOG, "New TI V1 at {:x}", (uintptr_t)PTI.get());
if (!PTI->good()) {
LOGM(ERR, "Could not alloc wl_resource for TIV1");
pMgr->noMemory();
PROTO::textInputV1->destroyResource(PTI.get());
return;
}
void CTextInputV1ProtocolManager::handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index) {
;
events.newTextInput.emit(WP<CTextInputV1>(PTI));
});
}
void CTextInputV1Protocol::destroyResource(CTextInputV1* client) {
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
}
void CTextInputV1Protocol::destroyResource(CZwpTextInputManagerV1* client) {
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == client; });
}

View file

@ -1,29 +1,45 @@
#pragma once
#include "../defines.hpp"
#include "text-input-unstable-v1-protocol.h"
#include "../protocols/core/Compositor.hpp"
#include "text-input-unstable-v1.hpp"
#include "WaylandProtocol.hpp"
#include <vector>
class CTextInput;
struct STextInputV1 {
wl_client* client = nullptr;
wl_resource* resourceCaller = nullptr;
class CTextInputV1 {
public:
CTextInputV1(SP<CZwpTextInputV1> resource);
~CTextInputV1();
wl_resource* resourceImpl = nullptr;
void enter(SP<CWLSurfaceResource> surface);
void leave();
CTextInput* pTextInput = nullptr;
void preeditCursor(int32_t index);
void preeditStyling(uint32_t index, uint32_t length, zwpTextInputV1PreeditStyle style);
void preeditString(uint32_t serial, const char* text, const char* commit);
void commitString(uint32_t serial, const char* text);
void deleteSurroundingText(int32_t index, uint32_t length);
wl_signal sEnable;
wl_signal sDisable;
wl_signal sCommit;
wl_signal sDestroy;
bool good();
wl_client* client();
private:
SP<CZwpTextInputV1> resource;
WP<CTextInputV1> self;
uint32_t serial = 0;
bool active = false;
struct {
CSignal onCommit;
CSignal enable;
CSignal disable;
CSignal destroy;
} events;
struct SPendingSurr {
bool isPending = false;
std::string text = "";
@ -39,37 +55,29 @@ struct STextInputV1 {
CBox cursorRectangle = {0, 0, 0, 0};
bool operator==(const STextInputV1& other) {
return other.client == client && other.resourceCaller == resourceCaller && other.resourceImpl == resourceImpl;
}
friend class CTextInput;
friend class CTextInputV1Protocol;
};
class CTextInputV1ProtocolManager {
class CTextInputV1Protocol : IWaylandProtocol {
public:
CTextInputV1ProtocolManager();
CTextInputV1Protocol(const wl_interface* iface, const int& ver, const std::string& name);
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
void createTI(wl_client* client, wl_resource* resource, uint32_t id);
void removeTI(STextInputV1* pTI);
virtual void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
void destroyResource(CTextInputV1* resource);
void destroyResource(CZwpTextInputManagerV1* client);
void displayDestroy();
// handlers for tiv1
void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface);
void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat);
void handleShowInputPanel(wl_client* client, wl_resource* resource);
void handleHideInputPanel(wl_client* client, wl_resource* resource);
void handleReset(wl_client* client, wl_resource* resource);
void handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor);
void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose);
void handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height);
void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language);
void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial);
void handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index);
struct {
CSignal newTextInput; // WP<CTextInputV3>
} events;
private:
wl_global* m_pGlobal = nullptr;
wl_listener m_liDisplayDestroy;
std::vector<SP<CZwpTextInputManagerV1>> m_vManagers;
std::vector<SP<CTextInputV1>> m_vClients;
std::vector<std::unique_ptr<STextInputV1>> m_pClients;
friend class CTextInputV1;
};
namespace PROTO {
inline UP<CTextInputV1Protocol> textInputV1;
};

View file

@ -8,356 +8,240 @@
#include <algorithm>
#define TOPLEVEL_EXPORT_VERSION 2
#define LOGM PROTO::toplevelExport->protoLog
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
g_pProtocolManager->m_pToplevelExportProtocolManager->bindManager(client, data, version, id);
}
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
g_pProtocolManager->m_pToplevelExportProtocolManager->displayDestroy();
}
void CToplevelExportProtocolManager::displayDestroy() {
wl_global_destroy(m_pGlobal);
}
CToplevelExportProtocolManager::CToplevelExportProtocolManager() {
#ifndef GLES32
Debug::log(WARN, "Toplevel sharing is not supported on LEGACY_RENDERER!");
return;
#endif
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &hyprland_toplevel_export_manager_v1_interface, TOPLEVEL_EXPORT_VERSION, this, bindManagerInt);
if (!m_pGlobal) {
Debug::log(ERR, "ToplevelExportManager could not start! Sharing windows will not work!");
return;
}
m_liDisplayDestroy.notify = handleDisplayDestroy;
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
Debug::log(LOG, "ToplevelExportManager started successfully!");
}
static void handleCaptureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) {
g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromHandle(handle));
}
static void handleCaptureToplevelWithWlr(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* handle) {
g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, PROTO::foreignToplevelWlr->windowFromHandleResource(handle));
}
static void handleDestroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) {
g_pProtocolManager->m_pToplevelExportProtocolManager->copyFrame(client, resource, buffer, ignore_damage);
}
static void handleDestroyFrame(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct hyprland_toplevel_export_manager_v1_interface toplevelExportManagerImpl = {
.capture_toplevel = handleCaptureToplevel,
.destroy = handleDestroy,
.capture_toplevel_with_wlr_toplevel_handle = handleCaptureToplevelWithWlr,
};
static const struct hyprland_toplevel_export_frame_v1_interface toplevelFrameImpl = {.copy = handleCopyFrame, .destroy = handleDestroyFrame};
//
static CScreencopyClient* clientFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_manager_v1_interface, &toplevelExportManagerImpl));
return (CScreencopyClient*)wl_resource_get_user_data(resource);
}
static SScreencopyFrame* frameFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_frame_v1_interface, &toplevelFrameImpl));
return (SScreencopyFrame*)wl_resource_get_user_data(resource);
}
void CToplevelExportProtocolManager::removeClient(CScreencopyClient* client, bool force) {
if (!force) {
if (!client || client->ref <= 0)
CToplevelExportClient::CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1> resource_) : resource(resource_) {
if (!good())
return;
if (--client->ref != 0)
return;
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));
});
lastMeasure.reset();
lastFrame.reset();
tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); });
}
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)
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() > 0)
frame->buffer->unlock();
removeClient(frame->client, force);
m_lFrames.remove(*frame);
}
void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, PHLWINDOW pWindow) {
const auto PCLIENT = clientFromResource(resource);
void CToplevelExportClient::captureToplevel(CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor_, PHLWINDOW handle) {
// create a frame
const auto PFRAME = &m_lFrames.emplace_back();
PFRAME->overlayCursor = !!overlay_cursor;
PFRAME->resource = wl_resource_create(client, &hyprland_toplevel_export_frame_v1_interface, wl_resource_get_version(resource), frame);
PFRAME->pWindow = pWindow;
const auto FRAME = PROTO::toplevelExport->m_vFrames.emplace_back(
makeShared<CToplevelExportFrame>(makeShared<CHyprlandToplevelExportFrameV1>(resource->client(), resource->version(), frame), overlayCursor_, handle));
if (!FRAME->good()) {
LOGM(ERR, "Couldn't alloc frame for sharing! (no memory)");
resource->noMemory();
PROTO::toplevelExport->destroyResource(FRAME.get());
return;
}
FRAME->self = FRAME;
FRAME->client = self;
}
void CToplevelExportClient::onTick() {
if (lastMeasure.getMillis() < 500)
return;
framesInLastHalfSecond = frameCounter;
frameCounter = 0;
lastMeasure.reset();
const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0;
const bool FRAMEAWAITING = std::ranges::any_of(PROTO::toplevelExport->m_vFrames, [&](const auto& frame) { return frame->client.get() == this; });
if (framesInLastHalfSecond > 3 && !sentScreencast) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)});
sentScreencast = true;
} else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)});
sentScreencast = false;
}
}
bool CToplevelExportClient::good() {
return resource->resource();
}
CToplevelExportFrame::~CToplevelExportFrame() {
if (buffer && buffer->locked())
buffer->unlock();
}
CToplevelExportFrame::CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) {
if (!good())
return;
overlayCursor = !!overlayCursor_;
if (!pWindow) {
Debug::log(ERR, "Client requested sharing of window handle {:x} which does not exist!", pWindow);
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
LOGM(ERR, "Client requested sharing of window handle {:x} which does not exist!", pWindow);
resource->sendFailed();
PROTO::toplevelExport->destroyResource(this);
return;
}
if (!pWindow->m_bIsMapped || pWindow->isHidden()) {
Debug::log(ERR, "Client requested sharing of window handle {:x} which is not shareable!", pWindow);
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
LOGM(ERR, "Client requested sharing of window handle {:x} which is not shareable!", pWindow);
resource->sendFailed();
PROTO::toplevelExport->destroyResource(this);
return;
}
if (!PFRAME->resource) {
Debug::log(ERR, "Couldn't alloc frame for sharing! (no memory)");
m_lFrames.remove(*PFRAME);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(PFRAME->resource, &toplevelFrameImpl, PFRAME, handleFrameResourceDestroy);
PFRAME->client = PCLIENT;
PCLIENT->ref++;
resource->setOnDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); });
resource->setDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); });
resource->setCopy([this](CHyprlandToplevelExportFrameV1* pFrame, wl_resource* res, int32_t ignoreDamage) { this->copy(pFrame, res, ignoreDamage); });
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
g_pHyprRenderer->makeEGLCurrent();
PFRAME->shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PMONITOR);
if (PFRAME->shmFormat == DRM_FORMAT_INVALID) {
Debug::log(ERR, "No format supported by renderer in capture toplevel");
hyprland_toplevel_export_frame_v1_send_failed(resource);
removeFrame(PFRAME);
shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PMONITOR);
if (shmFormat == DRM_FORMAT_INVALID) {
LOGM(ERR, "No format supported by renderer in capture toplevel");
resource->sendFailed();
PROTO::toplevelExport->destroyResource(this);
return;
}
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(PFRAME->shmFormat);
const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(shmFormat);
if (!PSHMINFO) {
Debug::log(ERR, "No pixel format supported by renderer in capture toplevel");
hyprland_toplevel_export_frame_v1_send_failed(resource);
removeFrame(PFRAME);
LOGM(ERR, "No pixel format supported by renderer in capture toplevel");
resource->sendFailed();
PROTO::toplevelExport->destroyResource(this);
return;
}
PFRAME->dmabufFormat = PMONITOR->output->state->state().drmFormat;
dmabufFormat = PMONITOR->output->state->state().drmFormat;
PFRAME->box = {0, 0, (int)(pWindow->m_vRealSize.value().x * PMONITOR->scale), (int)(pWindow->m_vRealSize.value().y * PMONITOR->scale)};
box = {0, 0, (int)(pWindow->m_vRealSize.value().x * PMONITOR->scale), (int)(pWindow->m_vRealSize.value().y * PMONITOR->scale)};
PFRAME->box.transform(wlTransformToHyprutils(PMONITOR->transform), PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y).round();
box.transform(wlTransformToHyprutils(PMONITOR->transform), PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y).round();
PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w);
shmStride = FormatUtils::minStride(PSHMINFO, box.w);
hyprland_toplevel_export_frame_v1_send_buffer(PFRAME->resource, FormatUtils::drmToShm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride);
resource->sendBuffer(FormatUtils::drmToShm(shmFormat), box.width, box.height, shmStride);
if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) {
hyprland_toplevel_export_frame_v1_send_linux_dmabuf(PFRAME->resource, PFRAME->dmabufFormat, PFRAME->box.width, PFRAME->box.height);
if (dmabufFormat != DRM_FORMAT_INVALID) {
resource->sendLinuxDmabuf(dmabufFormat, box.width, box.height);
}
hyprland_toplevel_export_frame_v1_send_buffer_done(PFRAME->resource);
resource->sendBufferDone();
}
void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) {
const auto PFRAME = frameFromResource(resource);
if (!PFRAME) {
Debug::log(ERR, "No frame in copyFrame??");
void CToplevelExportFrame::copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resource* buffer_, int32_t ignoreDamage) {
if (!good()) {
LOGM(ERR, "No frame in copyFrame??");
return;
}
const auto PWINDOW = PFRAME->pWindow.lock();
if (!validMapped(PWINDOW)) {
Debug::log(ERR, "Client requested sharing of window handle {:x} which is gone!", (uintptr_t)PWINDOW.get());
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
if (!validMapped(pWindow)) {
LOGM(ERR, "Client requested sharing of window handle {:x} which is gone!", pWindow);
resource->sendFailed();
PROTO::toplevelExport->destroyResource(this);
return;
}
if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden()) {
Debug::log(ERR, "Client requested sharing of window handle {:x} which is not shareable (2)!", PWINDOW);
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
if (!pWindow->m_bIsMapped || pWindow->isHidden()) {
LOGM(ERR, "Client requested sharing of window handle {:x} which is not shareable (2)!", pWindow);
resource->sendFailed();
PROTO::toplevelExport->destroyResource(this);
return;
}
const auto PBUFFER = CWLBufferResource::fromResource(buffer);
const auto PBUFFER = CWLBufferResource::fromResource(buffer_);
if (!PBUFFER) {
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
removeFrame(PFRAME);
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
PROTO::toplevelExport->destroyResource(this);
return;
}
PBUFFER->buffer->lock();
if (PBUFFER->buffer->size != PFRAME->box.size()) {
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
removeFrame(PFRAME);
if (PBUFFER->buffer->size != box.size()) {
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
PROTO::toplevelExport->destroyResource(this);
return;
}
if (PFRAME->buffer) {
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
removeFrame(PFRAME);
if (buffer) {
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
PROTO::toplevelExport->destroyResource(this);
return;
}
if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) {
PFRAME->bufferDMA = true;
bufferDMA = true;
if (attrs.format != PFRAME->dmabufFormat) {
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
removeFrame(PFRAME);
if (attrs.format != dmabufFormat) {
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
PROTO::toplevelExport->destroyResource(this);
return;
}
} else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) {
if (attrs.format != PFRAME->shmFormat) {
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
removeFrame(PFRAME);
if (attrs.format != shmFormat) {
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
PROTO::toplevelExport->destroyResource(this);
return;
} else if ((int)attrs.stride != PFRAME->shmStride) {
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
removeFrame(PFRAME);
} else if ((int)attrs.stride != shmStride) {
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
PROTO::toplevelExport->destroyResource(this);
return;
}
} else {
wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
removeFrame(PFRAME);
resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
PROTO::toplevelExport->destroyResource(this);
return;
}
PFRAME->buffer = PBUFFER->buffer;
buffer = PBUFFER->buffer;
m_vFramesAwaitingWrite.emplace_back(PFRAME);
PROTO::toplevelExport->m_vFramesAwaitingWrite.emplace_back(self);
}
void CToplevelExportProtocolManager::onOutputCommit(CMonitor* pMonitor) {
if (m_vFramesAwaitingWrite.empty())
return; // nothing to share
std::vector<SScreencopyFrame*> framesToRemove;
// share frame if correct output
for (auto& f : m_vFramesAwaitingWrite) {
const auto PWINDOW = f->pWindow.lock();
if (!validMapped(PWINDOW)) {
framesToRemove.push_back(f);
continue;
}
if (pMonitor != g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID))
continue;
CBox geometry = {PWINDOW->m_vRealPosition.value().x, PWINDOW->m_vRealPosition.value().y, PWINDOW->m_vRealSize.value().x, PWINDOW->m_vRealSize.value().y};
if (geometry.intersection({pMonitor->vecPosition, pMonitor->vecSize}).empty())
continue;
shareFrame(f);
f->client->lastFrame.reset();
++f->client->frameCounter;
framesToRemove.push_back(f);
}
for (auto& f : framesToRemove) {
removeFrame(f);
}
}
void CToplevelExportProtocolManager::shareFrame(SScreencopyFrame* frame) {
if (!frame->buffer || !validMapped(frame->pWindow))
void CToplevelExportFrame::share() {
if (!buffer || !validMapped(pWindow))
return;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t flags = 0;
if (frame->bufferDMA) {
if (!copyFrameDmabuf(frame, &now)) {
hyprland_toplevel_export_frame_v1_send_failed(frame->resource);
if (bufferDMA) {
if (!copyDmabuf(&now)) {
resource->sendFailed();
return;
}
} else {
if (!copyFrameShm(frame, &now)) {
hyprland_toplevel_export_frame_v1_send_failed(frame->resource);
if (!copyShm(&now)) {
resource->sendFailed();
return;
}
}
hyprland_toplevel_export_frame_v1_send_flags(frame->resource, flags);
sendDamage(frame);
resource->sendFlags((hyprlandToplevelExportFrameV1Flags)0);
if (!ignoreDamage) {
resource->sendDamage(0, 0, box.width, box.height);
}
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
hyprland_toplevel_export_frame_v1_send_ready(frame->resource, tvSecHi, tvSecLo, now.tv_nsec);
resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec);
}
void CToplevelExportProtocolManager::sendDamage(SScreencopyFrame* frame) {
// TODO: send proper dmg
hyprland_toplevel_export_frame_v1_send_damage(frame->resource, 0, 0, frame->box.width, frame->box.height);
}
bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) {
auto shm = frame->buffer->shm();
auto [pixelData, fmt, bufLen] = frame->buffer->beginDataPtr(0); // no need for end, cuz it's shm
bool CToplevelExportFrame::copyShm(timespec* now) {
auto shm = buffer->shm();
auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm
// render the client
const auto PMONITOR = g_pCompositor->getMonitorFromID(frame->pWindow->m_iMonitorID);
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
CRegion fakeDamage{0, 0, PMONITOR->vecPixelSize.x * 10, PMONITOR->vecPixelSize.y * 10};
g_pHyprRenderer->makeEGLCurrent();
@ -365,7 +249,7 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
CFramebuffer outFB;
outFB.alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : PMONITOR->output->state->state().drmFormat);
if (frame->overlayCursor) {
if (overlayCursor) {
g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock());
g_pPointerManager->damageCursor(PMONITOR->self.lock());
}
@ -376,12 +260,12 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0));
// render client at 0,0
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(frame->pWindow.lock()); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(frame->pWindow.lock(), PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
if (frame->overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - frame->pWindow->m_vRealPosition.value());
if (overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition.value());
const auto PFORMAT = FormatUtils::getPixelFormatFromDRM(shm.format);
if (!PFORMAT) {
@ -403,9 +287,9 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
glPixelStorei(GL_PACK_ALIGNMENT, 1);
auto glFormat = PFORMAT->flipRB ? GL_BGRA_EXT : GL_RGBA;
glReadPixels(0, 0, frame->box.width, frame->box.height, glFormat, PFORMAT->glType, pixelData);
glReadPixels(0, 0, box.width, box.height, glFormat, PFORMAT->glType, pixelData);
if (frame->overlayCursor) {
if (overlayCursor) {
g_pPointerManager->unlockSoftwareForMonitor(PMONITOR->self.lock());
g_pPointerManager->damageCursor(PMONITOR->self.lock());
}
@ -413,31 +297,112 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
return true;
}
bool CToplevelExportProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame, timespec* now) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(frame->pWindow->m_iMonitorID);
bool CToplevelExportFrame::copyDmabuf(timespec* now) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX};
if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer.lock()))
if (overlayCursor) {
g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock());
g_pPointerManager->damageCursor(PMONITOR->self.lock());
}
if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock()))
return false;
g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0));
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(frame->pWindow.lock()); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(frame->pWindow.lock(), PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
if (frame->overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - frame->pWindow->m_vRealPosition.value());
if (overlayCursor)
g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition.value());
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
g_pHyprRenderer->endRender();
if (overlayCursor) {
g_pPointerManager->unlockSoftwareForMonitor(PMONITOR->self.lock());
g_pPointerManager->damageCursor(PMONITOR->self.lock());
}
return true;
}
void CToplevelExportProtocolManager::onWindowUnmap(PHLWINDOW pWindow) {
for (auto& f : m_lFrames) {
if (f.pWindow.lock() == pWindow)
f.pWindow.reset();
bool CToplevelExportFrame::good() {
return resource->resource();
}
CToplevelExportProtocol::CToplevelExportProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CToplevelExportProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto CLIENT = m_vClients.emplace_back(makeShared<CToplevelExportClient>(makeShared<CHyprlandToplevelExportManagerV1>(client, ver, id)));
if (!CLIENT->good()) {
LOGM(LOG, "Failed to bind client! (out of memory)");
wl_client_post_no_memory(client);
m_vClients.pop_back();
return;
}
CLIENT->self = CLIENT;
LOGM(LOG, "Bound client successfully!");
}
void CToplevelExportProtocol::destroyResource(CToplevelExportClient* client) {
std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; });
std::erase_if(m_vFrames, [&](const auto& other) { return other->client.get() == client; });
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other->client.get() == client; });
}
void CToplevelExportProtocol::destroyResource(CToplevelExportFrame* frame) {
std::erase_if(m_vFrames, [&](const auto& other) { return other.get() == frame; });
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other.get() == frame; });
}
void CToplevelExportProtocol::onOutputCommit(CMonitor* pMonitor) {
if (m_vFramesAwaitingWrite.empty())
return; // nothing to share
std::vector<WP<CToplevelExportFrame>> framesToRemove;
// share frame if correct output
for (auto& f : m_vFramesAwaitingWrite) {
if (!f->pWindow || !validMapped(f->pWindow)) {
framesToRemove.push_back(f);
continue;
}
const auto PWINDOW = f->pWindow;
if (pMonitor != g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID))
continue;
CBox geometry = {PWINDOW->m_vRealPosition.value().x, PWINDOW->m_vRealPosition.value().y, PWINDOW->m_vRealSize.value().x, PWINDOW->m_vRealSize.value().y};
if (geometry.intersection({pMonitor->vecPosition, pMonitor->vecSize}).empty())
continue;
f->share();
f->client->lastFrame.reset();
++f->client->frameCounter;
framesToRemove.push_back(f);
}
for (auto& f : framesToRemove) {
destroyResource(f.get());
}
}
void CToplevelExportProtocol::onWindowUnmap(PHLWINDOW pWindow) {
for (auto& f : m_vFrames) {
if (f->pWindow == pWindow)
f->pWindow.reset();
}
}

View file

@ -1,7 +1,8 @@
#pragma once
#include "../defines.hpp"
#include "hyprland-toplevel-export-v1-protocol.h"
#include "hyprland-toplevel-export-v1.hpp"
#include "WaylandProtocol.hpp"
#include "Screencopy.hpp"
#include <list>
@ -10,32 +11,91 @@
class CMonitor;
class CWindow;
class CToplevelExportProtocolManager {
class CToplevelExportClient {
public:
CToplevelExportProtocolManager();
CToplevelExportClient(SP<CHyprlandToplevelExportManagerV1> resource_);
bool good();
WP<CToplevelExportClient> self;
eClientOwners clientOwner = CLIENT_TOPLEVEL_EXPORT;
CTimer lastFrame;
int frameCounter = 0;
private:
SP<CHyprlandToplevelExportManagerV1> resource;
int framesInLastHalfSecond = 0;
CTimer lastMeasure;
bool sentScreencast = false;
SP<HOOK_CALLBACK_FN> tickCallback;
void onTick();
void captureToplevel(CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, PHLWINDOW handle);
friend class CToplevelExportProtocol;
};
class CToplevelExportFrame {
public:
CToplevelExportFrame(SP<CHyprlandToplevelExportFrameV1> resource_, int32_t overlayCursor, PHLWINDOW pWindow);
~CToplevelExportFrame();
bool good();
SP<CToplevelExportFrame> self;
WP<CToplevelExportClient> client;
private:
SP<CHyprlandToplevelExportFrameV1> resource;
PHLWINDOW pWindow;
bool overlayCursor = false;
bool ignoreDamage = false;
bool lockedSWCursors = false;
WP<IHLBuffer> buffer;
bool bufferDMA = false;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;
int shmStride = 0;
CBox box = {};
void copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resource* buffer, int32_t ignoreDamage);
bool copyDmabuf(timespec* now);
bool copyShm(timespec* now);
void share();
friend class CToplevelExportProtocol;
};
class CToplevelExportProtocol : IWaylandProtocol {
public:
CToplevelExportProtocol(const wl_interface* iface, const int& ver, const std::string& name);
void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
void destroyResource(CToplevelExportClient* client);
void destroyResource(CToplevelExportFrame* frame);
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
void captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, PHLWINDOW handle);
void removeClient(CScreencopyClient* client, bool force = false);
void removeFrame(SScreencopyFrame* frame, bool force = false);
void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage);
void displayDestroy();
void onWindowUnmap(PHLWINDOW pWindow);
void onOutputCommit(CMonitor* pMonitor);
private:
wl_global* m_pGlobal = nullptr;
std::list<SScreencopyFrame> m_lFrames;
std::list<CScreencopyClient> m_lClients;
std::vector<SP<CToplevelExportClient>> m_vClients;
std::vector<SP<CToplevelExportFrame>> m_vFrames;
std::vector<SP<CToplevelExportFrame>> m_vFramesAwaitingWrite;
wl_listener m_liDisplayDestroy;
void shareFrame(CToplevelExportFrame* frame);
bool copyFrameDmabuf(CToplevelExportFrame* frame, timespec* now);
bool copyFrameShm(CToplevelExportFrame* frame, timespec* now);
void sendDamage(CToplevelExportFrame* frame);
std::vector<SScreencopyFrame*> m_vFramesAwaitingWrite;
void shareFrame(SScreencopyFrame* frame);
bool copyFrameDmabuf(SScreencopyFrame* frame, timespec* now);
bool copyFrameShm(SScreencopyFrame* frame, timespec* now);
void sendDamage(SScreencopyFrame* frame);
friend class CScreencopyClient;
friend class CToplevelExportClient;
friend class CToplevelExportFrame;
};
namespace PROTO {
inline UP<CToplevelExportProtocol> toplevelExport;
};

View file

@ -54,13 +54,13 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
});
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!surface || !surface->pending.buffer)
if (!surface || !surface->pending.texture)
return;
if (surface->pending.viewport.hasSource) {
auto& src = surface->pending.viewport.source;
if (src.w + src.x > surface->pending.buffer->size.x || src.h + src.y > surface->pending.buffer->size.y) {
if (src.w + src.x > surface->pending.bufferSize.x || src.h + src.y > surface->pending.bufferSize.y) {
resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit");
surface->pending.rejected = true;
return;

View file

@ -1,8 +1,9 @@
#include "VirtualPointer.hpp"
#include "core/Output.hpp"
#define LOGM PROTO::virtualPointer->protoLog
CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_) : resource(resource_) {
CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_, WP<CMonitor> boundOutput_) : boundOutput(boundOutput_), resource(resource_) {
if (!good())
return;
@ -112,10 +113,18 @@ void CVirtualPointerProtocol::bindManager(wl_client* client, void* data, uint32_
RESOURCE->setOnDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); });
RESOURCE->setDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); });
RESOURCE->setCreateVirtualPointer([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) { this->onCreatePointer(pMgr, seat, id); });
RESOURCE->setCreateVirtualPointer([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) { this->onCreatePointer(pMgr, seat, id, {}); });
RESOURCE->setCreateVirtualPointerWithOutput([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, wl_resource* output, uint32_t id) {
LOGM(WARN, "TODO: CreateWithOutput is not supported yet. Ignoring for now.");
this->onCreatePointer(pMgr, seat, id);
if (output) {
auto RES = CWLOutputResource::fromResource(output);
if (!RES) {
this->onCreatePointer(pMgr, seat, id, {});
return;
}
this->onCreatePointer(pMgr, seat, id, RES->monitor);
} else
this->onCreatePointer(pMgr, seat, id, {});
});
}
@ -127,9 +136,9 @@ void CVirtualPointerProtocol::destroyResource(CVirtualPointerV1Resource* pointer
std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == pointer; });
}
void CVirtualPointerProtocol::onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) {
void CVirtualPointerProtocol::onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id, WP<CMonitor> output) {
const auto RESOURCE = m_vPointers.emplace_back(makeShared<CVirtualPointerV1Resource>(makeShared<CZwlrVirtualPointerV1>(pMgr->client(), pMgr->version(), id)));
const auto RESOURCE = m_vPointers.emplace_back(makeShared<CVirtualPointerV1Resource>(makeShared<CZwlrVirtualPointerV1>(pMgr->client(), pMgr->version(), id), output));
if (!RESOURCE->good()) {
pMgr->noMemory();

View file

@ -8,10 +8,11 @@
#include "wlr-virtual-pointer-unstable-v1.hpp"
#include "../helpers/signal/Signal.hpp"
#include "../devices/IPointer.hpp"
#include "../helpers/Monitor.hpp"
class CVirtualPointerV1Resource {
public:
CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_);
CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_, WP<CMonitor> boundOutput_);
~CVirtualPointerV1Resource();
struct {
@ -39,6 +40,8 @@ class CVirtualPointerV1Resource {
std::string name;
WP<CMonitor> boundOutput;
private:
SP<CZwlrVirtualPointerV1> resource;
@ -60,7 +63,7 @@ class CVirtualPointerProtocol : public IWaylandProtocol {
private:
void onManagerResourceDestroy(wl_resource* res);
void destroyResource(CVirtualPointerV1Resource* pointer);
void onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id);
void onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id, WP<CMonitor> output);
//
std::vector<UP<CZwlrVirtualPointerManagerV1>> m_vManagers;

View file

@ -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");
}

View file

@ -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;
};

View file

@ -8,6 +8,20 @@
#define LOGM PROTO::xdgShell->protoLog
void SXDGPositionerState::setAnchor(xdgPositionerAnchor edges) {
anchor.setTop(edges == XDG_POSITIONER_ANCHOR_TOP || edges == XDG_POSITIONER_ANCHOR_TOP_LEFT || edges == XDG_POSITIONER_ANCHOR_TOP_RIGHT);
anchor.setLeft(edges == XDG_POSITIONER_ANCHOR_LEFT || edges == XDG_POSITIONER_ANCHOR_TOP_LEFT || edges == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT);
anchor.setBottom(edges == XDG_POSITIONER_ANCHOR_BOTTOM || edges == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT || edges == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT);
anchor.setRight(edges == XDG_POSITIONER_ANCHOR_RIGHT || edges == XDG_POSITIONER_ANCHOR_TOP_RIGHT || edges == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT);
}
void SXDGPositionerState::setGravity(xdgPositionerGravity edges) {
gravity.setTop(edges == XDG_POSITIONER_GRAVITY_TOP || edges == XDG_POSITIONER_GRAVITY_TOP_LEFT || edges == XDG_POSITIONER_GRAVITY_TOP_RIGHT);
gravity.setLeft(edges == XDG_POSITIONER_GRAVITY_LEFT || edges == XDG_POSITIONER_GRAVITY_TOP_LEFT || edges == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT);
gravity.setBottom(edges == XDG_POSITIONER_GRAVITY_BOTTOM || edges == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT || edges == XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
gravity.setRight(edges == XDG_POSITIONER_GRAVITY_RIGHT || edges == XDG_POSITIONER_GRAVITY_TOP_RIGHT || edges == XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
}
CXDGPopupResource::CXDGPopupResource(SP<CXdgPopup> resource_, SP<CXDGSurfaceResource> owner_, SP<CXDGSurfaceResource> surface_, SP<CXDGPositionerResource> positioner) :
surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) {
if (!good())
@ -333,12 +347,12 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
if (toplevel)
toplevel->current = toplevel->pending;
if (initialCommit && surface->pending.buffer) {
if (initialCommit && surface->pending.texture) {
resource->error(-1, "Buffer attached before initial commit");
return;
}
if (surface->current.buffer && !mapped) {
if (surface->current.texture && !mapped) {
// this forces apps to not draw CSD.
if (toplevel)
toplevel->setMaximized(true);
@ -349,10 +363,10 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBas
return;
}
if (!surface->current.buffer && mapped) {
if (!surface->current.texture && mapped) {
mapped = false;
surface->unmap();
events.unmap.emit();
surface->unmap();
return;
}
@ -429,10 +443,6 @@ CXDGSurfaceResource::~CXDGSurfaceResource() {
surface->resetRole();
}
eSurfaceRole CXDGSurfaceResource::role() {
return SURFACE_ROLE_XDG_SHELL;
}
bool CXDGSurfaceResource::good() {
return resource->resource();
}
@ -490,9 +500,9 @@ CXDGPositionerResource::CXDGPositionerResource(SP<CXdgPositioner> resource_, SP<
resource->setSetOffset([this](CXdgPositioner* r, int32_t x, int32_t y) { state.offset = {x, y}; });
resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.anchor = a; });
resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.setAnchor(a); });
resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.gravity = g; });
resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.setGravity(g); });
resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { state.constraintAdjustment = (uint32_t)a; });
@ -513,125 +523,96 @@ CXDGPositionerRules::CXDGPositionerRules(SP<CXDGPositionerResource> positioner)
state = positioner->state;
}
static Vector2D pointForAnchor(const CBox& box, const Vector2D& predictionSize, xdgPositionerAnchor anchor) {
switch (anchor) {
case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.0 - predictionSize.x / 2.0, 0.0};
case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.0 - predictionSize.x / 2.0, box.size().y};
case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0.0, box.size().y / 2.0 - predictionSize.y / 2.0};
case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F - predictionSize.y / 2.0};
case XDG_POSITIONER_ANCHOR_TOP_LEFT: return box.pos();
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0.0, box.size().y};
case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return box.pos() + Vector2D{box.size().x, 0.0};
case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y};
default: return box.pos();
}
return {};
}
CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& parentCoord) {
CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoord) {
Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord);
CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.requestedSize, state.anchor) + state.offset, state.requestedSize};
// padding
constraint.expand(-4);
bool success = predictedBox.inside(constraint);
auto anchorRect = state.anchorRect.copy().translate(parentCoord);
if (success)
return predictedBox.translate(-parentCoord - constraint.pos());
auto width = state.requestedSize.x;
auto height = state.requestedSize.y;
CBox test = predictedBox;
auto anchorX = state.anchor.left() ? anchorRect.x : state.anchor.right() ? anchorRect.extent().x : anchorRect.middle().x;
auto anchorY = state.anchor.top() ? anchorRect.y : state.anchor.bottom() ? anchorRect.extent().y : anchorRect.middle().y;
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)) {
// attempt to flip
const bool flipX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X;
const bool flipY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
auto countEdges = [constraint](const CBox& test) -> int {
int edgeCount = 0;
edgeCount += test.x < constraint.x ? 1 : 0;
edgeCount += test.x + test.w > constraint.x + constraint.w ? 1 : 0;
edgeCount += test.y < constraint.y ? 1 : 0;
edgeCount += test.y + test.h > constraint.y + constraint.h ? 1 : 0;
return edgeCount;
};
int edgeCount = countEdges(test);
auto calcEffectiveX = [&]() { return state.gravity.left() ? anchorX - width : state.gravity.right() ? anchorX : anchorX - width / 2; };
auto calcEffectiveY = [&]() { return state.gravity.top() ? anchorY - height : state.gravity.bottom() ? anchorY : anchorY - height / 2; };
if (flipX && edgeCount > countEdges(test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0.0})))
test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0.0});
if (flipY && edgeCount > countEdges(test.copy().translate(Vector2D{0.0, -predictedBox.h - state.anchorRect.h})))
test.translate(Vector2D{0.0, -predictedBox.h - state.anchorRect.h});
auto effectiveX = calcEffectiveX();
auto effectiveY = calcEffectiveY();
success = test.copy().expand(-1).inside(constraint);
// Note: the usage of offset is a guess which maintains compatibility with other compositors that were tested.
// It considers the offset when deciding whether or not to flip but does not actually flip the offset, instead
// applying it after the flip step.
if (success)
return test.translate(-parentCoord - constraint.pos());
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) {
auto flip = (state.gravity.left() && effectiveX + state.offset.x < constraint.x) || (state.gravity.right() && effectiveX + state.offset.x + width > constraint.extent().x);
if (flip) {
state.gravity ^= CEdges::LEFT | CEdges::RIGHT;
anchorX = state.anchor.left() ? anchorRect.extent().x : state.anchor.right() ? anchorRect.x : anchorX;
effectiveX = calcEffectiveX();
}
}
// 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_FLIP_Y) {
auto flip = (state.gravity.top() && effectiveY + state.offset.y < constraint.y) || (state.gravity.bottom() && effectiveY + state.offset.y + height > constraint.extent().y);
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 (flip) {
state.gravity ^= CEdges::TOP | CEdges::BOTTOM;
anchorY = state.anchor.top() ? anchorRect.extent().y : state.anchor.bottom() ? anchorRect.y : anchorY;
effectiveY = calcEffectiveY();
}
}
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;
effectiveX += state.offset.x;
effectiveY += state.offset.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;
// Slide order is important for the case where the window is too large to fit on screen.
// 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 (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;
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) {
if (effectiveX + width > constraint.extent().x)
effectiveX = constraint.extent().x - width;
success = test.copy().expand(-1).inside(constraint);
if (success)
return test.translate(-parentCoord - constraint.pos());
if (effectiveX < constraint.x)
effectiveX = constraint.x;
}
LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place");
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) {
if (effectiveY + height > constraint.extent().y)
effectiveY = constraint.extent().y - height;
return test.translate(-parentCoord - constraint.pos());
if (effectiveY < constraint.y)
effectiveY = constraint.y;
}
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) {
if (effectiveX < constraint.x) {
auto diff = constraint.x - effectiveX;
effectiveX = constraint.x;
width -= diff;
}
auto effectiveX2 = effectiveX + width;
if (effectiveX2 > constraint.extent().x)
width -= effectiveX2 - constraint.extent().x;
}
if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) {
if (effectiveY < constraint.y) {
auto diff = constraint.y - effectiveY;
effectiveY = constraint.y;
height -= diff;
}
auto effectiveY2 = effectiveY + height;
if (effectiveY2 > constraint.extent().y)
height -= effectiveY2 - constraint.extent().y;
}
return {effectiveX - parentCoord.x, effectiveY - parentCoord.y, width, height};
}
CXDGWMBase::CXDGWMBase(SP<CXdgWmBase> resource_) : resource(resource_) {
@ -682,7 +663,8 @@ CXDGWMBase::CXDGWMBase(SP<CXdgWmBase> resource_) : resource(resource_) {
}
RESOURCE->self = RESOURCE;
SURF->role = RESOURCE;
RESOURCE->surface = SURF;
SURF->role = makeShared<CXDGSurfaceRole>(RESOURCE);
surfaces.emplace_back(RESOURCE);
@ -779,3 +761,7 @@ void CXDGShellProtocol::onPopupDestroy(WP<CXDGPopupResource> popup) {
if (popup->surface)
grab->remove(popup->surface->surface.lock());
}
CXDGSurfaceRole::CXDGSurfaceRole(SP<CXDGSurfaceResource> xdg) : xdgSurface(xdg) {
;
}

View file

@ -4,10 +4,10 @@
#include <vector>
#include <cstdint>
#include <optional>
#include <hyprutils/math/Edges.hpp>
#include "WaylandProtocol.hpp"
#include "xdg-shell.hpp"
#include "../helpers/math/Math.hpp"
#include "../helpers/math/Math.hpp"
#include "../helpers/signal/Signal.hpp"
#include "types/SurfaceRole.hpp"
@ -22,19 +22,22 @@ class CWLSurfaceResource;
struct SXDGPositionerState {
Vector2D requestedSize;
CBox anchorRect;
xdgPositionerAnchor anchor = XDG_POSITIONER_ANCHOR_NONE;
xdgPositionerGravity gravity = XDG_POSITIONER_GRAVITY_NONE;
CEdges anchor;
CEdges gravity;
uint32_t constraintAdjustment = 0;
Vector2D offset;
bool reactive = false;
Vector2D parentSize;
void setAnchor(xdgPositionerAnchor edges);
void setGravity(xdgPositionerGravity edges);
};
class CXDGPositionerRules {
public:
CXDGPositionerRules(SP<CXDGPositionerResource> positioner);
CBox getPosition(const CBox& constraint, const Vector2D& parentPos);
CBox getPosition(CBox constraint, const Vector2D& parentPos);
private:
SXDGPositionerState state;
@ -138,15 +141,24 @@ class CXDGToplevelResource {
void applyState();
};
class CXDGSurfaceResource : public ISurfaceRole {
class CXDGSurfaceRole : public ISurfaceRole {
public:
CXDGSurfaceRole(SP<CXDGSurfaceResource> xdg);
virtual eSurfaceRole role() {
return SURFACE_ROLE_XDG_SHELL;
}
WP<CXDGSurfaceResource> xdgSurface;
};
class CXDGSurfaceResource {
public:
CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBase> owner_, SP<CWLSurfaceResource> surface_);
~CXDGSurfaceResource();
static SP<CXDGSurfaceResource> fromResource(wl_resource*);
virtual eSurfaceRole role();
bool good();
WP<CXDGWMBase> owner;

View file

@ -1,5 +1,6 @@
#include "Compositor.hpp"
#include "Output.hpp"
#include "Seat.hpp"
#include "../types/WLBuffer.hpp"
#include <algorithm>
#include <ranges>
@ -9,6 +10,7 @@
#include "../PresentationTime.hpp"
#include "../DRMSyncobj.hpp"
#include "../../render/Renderer.hpp"
#include <cstring>
#define LOGM PROTO::compositor->protoLog
@ -19,8 +21,6 @@ class CDefaultSurfaceRole : public ISurfaceRole {
}
};
SP<CDefaultSurfaceRole> defaultRole = makeShared<CDefaultSurfaceRole>();
CWLCallbackResource::CWLCallbackResource(SP<CWlCallback> resource_) : resource(resource_) {
;
}
@ -63,7 +63,7 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
resource->setData(this);
role = defaultRole;
role = makeShared<CDefaultSurfaceRole>();
resource->setDestroy([this](CWlSurface* r) { destroy(); });
resource->setOnDestroy([this](CWlSurface* r) { destroy(); });
@ -76,40 +76,41 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
pending.texture.reset();
} else {
auto res = CWLBufferResource::fromResource(buffer);
pending.buffer = res && res->buffer ? res->buffer.lock() : nullptr;
pending.buffer = res && res->buffer ? makeShared<CHLBufferReference>(res->buffer.lock(), self.lock()) : nullptr;
pending.size = res && res->buffer ? res->buffer->size : Vector2D{};
pending.texture = res && res->buffer ? res->buffer->texture : nullptr;
pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{};
}
Vector2D oldBufSize = current.buffer ? current.buffer->size : Vector2D{};
Vector2D newBufSize = pending.buffer ? pending.buffer->size : Vector2D{};
Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{};
Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{};
if (oldBufSize != newBufSize)
if (oldBufSize != newBufSize || current.buffer != pending.buffer)
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
bufferReleased = false;
});
resource->setCommit([this](CWlSurface* r) {
if (pending.buffer)
pending.bufferDamage.intersect(CBox{{}, pending.buffer->size});
if (pending.texture)
pending.bufferDamage.intersect(CBox{{}, pending.bufferSize});
if (!pending.buffer)
if (!pending.texture)
pending.size = {};
else if (pending.viewport.hasDestination)
pending.size = pending.viewport.destination;
else if (pending.viewport.hasSource)
pending.size = pending.viewport.source.size();
else {
Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.buffer->size.y, pending.buffer->size.x} : pending.buffer->size;
Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.bufferSize.y, pending.bufferSize.x} : pending.bufferSize;
pending.size = tfs / pending.scale;
}
pending.damage.intersect(CBox{{}, pending.size});
events.precommit.emit();
if (pending.rejected)
if (pending.rejected) {
dropPendingBuffer();
return;
}
if (stateLocks <= 0)
commitPendingState();
@ -151,12 +152,23 @@ CWLSurfaceResource::~CWLSurfaceResource() {
}
void CWLSurfaceResource::destroy() {
if (mapped)
if (mapped) {
events.unmap.emit();
unmap();
}
events.destroy.emit();
releaseBuffers(false);
PROTO::compositor->destroyResource(this);
}
void CWLSurfaceResource::dropPendingBuffer() {
pending.buffer.reset();
}
void CWLSurfaceResource::dropCurrentBuffer() {
current.buffer.reset();
}
SP<CWLSurfaceResource> CWLSurfaceResource::fromResource(wl_resource* res) {
auto data = (CWLSurfaceResource*)(((CWlSurface*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
@ -237,7 +249,7 @@ void CWLSurfaceResource::frame(timespec* now) {
}
void CWLSurfaceResource::resetRole() {
role = defaultRole;
role = makeShared<CDefaultSurfaceRole>();
}
void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data) {
@ -251,6 +263,8 @@ void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std
for (auto& c : n->subsurfaces) {
if (c->zIndex >= 0)
break;
if (c->surface.expired())
continue;
nodes2.push_back(c->surface.lock());
}
}
@ -263,7 +277,7 @@ void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std
for (auto& n : nodes) {
Vector2D offset = {};
if (n->role->role() == SURFACE_ROLE_SUBSURFACE) {
auto subsurface = (CWLSubsurfaceResource*)n->role.get();
auto subsurface = ((CSubsurfaceRole*)n->role.get())->subsurface.lock();
offset = subsurface->posRelativeToParent();
}
@ -274,6 +288,8 @@ void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std
for (auto& c : n->subsurfaces) {
if (c->zIndex < 0)
continue;
if (c->surface.expired())
continue;
nodes2.push_back(c->surface.lock());
}
}
@ -319,8 +335,6 @@ void CWLSurfaceResource::map() {
mapped = true;
events.map.emit();
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
frame(&now);
@ -335,7 +349,16 @@ void CWLSurfaceResource::unmap() {
mapped = false;
events.unmap.emit();
// release the buffers.
// this is necessary for XWayland to function correctly,
// as it does not unmap via the traditional commit(null buffer) method, but via the X11 protocol.
releaseBuffers();
}
void CWLSurfaceResource::releaseBuffers(bool onlyCurrent) {
if (!onlyCurrent)
dropPendingBuffer();
dropCurrentBuffer();
}
void CWLSurfaceResource::error(int code, const std::string& str) {
@ -360,18 +383,18 @@ CBox CWLSurfaceResource::extends() {
}
Vector2D CWLSurfaceResource::sourceSize() {
if (!current.buffer)
if (!current.texture)
return {};
if (current.viewport.hasSource)
return current.viewport.source.size();
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size;
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize;
return trc / current.scale;
}
CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
if (!current.buffer)
if (!current.texture)
return {};
CRegion surfaceDamage = current.damage;
@ -383,7 +406,7 @@ CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
if (current.viewport.hasSource)
surfaceDamage.translate(current.viewport.source.pos());
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size;
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.bufferSize.y, current.bufferSize.x} : current.bufferSize;
return surfaceDamage.scale(current.scale).transform(wlTransformToHyprutils(invertTransform(current.transform)), trc.x, trc.y).add(current.bufferDamage);
}
@ -406,23 +429,26 @@ void CWLSurfaceResource::commitPendingState() {
pending.damage.clear();
pending.bufferDamage.clear();
if (current.buffer && !bufferReleased) {
// without previous dolphin et al are weird vvv
//CRegion surfaceDamage =
// current.damage.copy().scale(current.scale).transform(current.transform, current.size.x, current.size.y).add(current.bufferDamage).add(previousBufferDamage);
current.buffer->update(CBox{{}, {INT32_MAX, INT32_MAX}}); // FIXME: figure this out to not use this hack. QT apps are wonky without this.
if (current.texture)
current.texture->m_eTransform = wlTransformToHyprutils(current.transform);
if (current.buffer && current.buffer->buffer) {
current.buffer->buffer->update(accumulateCurrentBufferDamage());
// if the surface is a cursor, update the shm buffer
// TODO: don't update the entire texture
if (role->role() == SURFACE_ROLE_CURSOR)
updateCursorShm();
// release the buffer if it's synchronous as update() has done everything thats needed
// so we can let the app know we're done.
if (current.buffer->isSynchronous()) {
current.buffer->sendReleaseWithSurface(self.lock());
bufferReleased = true;
}
if (current.buffer->buffer->isSynchronous())
dropCurrentBuffer();
}
// TODO: we should _accumulate_ and not replace above if sync
if (role->role() == SURFACE_ROLE_SUBSURFACE) {
auto subsurface = (CWLSubsurfaceResource*)role.get();
auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock();
if (subsurface->sync)
return;
@ -432,7 +458,7 @@ void CWLSurfaceResource::commitPendingState() {
breadthfirst(
[](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) {
if (surf->role->role() == SURFACE_ROLE_SUBSURFACE) {
auto subsurface = (CWLSubsurfaceResource*)surf->role.get();
auto subsurface = ((CSubsurfaceRole*)surf->role.get())->subsurface.lock();
if (!subsurface->sync)
return;
}
@ -442,28 +468,44 @@ void CWLSurfaceResource::commitPendingState() {
}
// for async buffers, we can only release the buffer once we are unrefing it from current.
if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) {
if (previousBuffer->lockedByBackend) {
previousBuffer->hlEvents.backendRelease = previousBuffer->events.backendRelease.registerListener([this, previousBuffer](std::any data) {
if (!self.expired()) // could be dead in the dtor
previousBuffer->sendReleaseWithSurface(self.lock());
else
previousBuffer->sendRelease();
previousBuffer->hlEvents.backendRelease.reset();
bufferReleased = true;
});
} else
previousBuffer->sendReleaseWithSurface(self.lock());
bufferReleased = true;
// if the backend took it, ref it with the lambda. Otherwise, the end of this scope will release it.
if (previousBuffer && previousBuffer->buffer && !previousBuffer->buffer->isSynchronous()) {
if (previousBuffer->buffer->lockedByBackend && !previousBuffer->buffer->hlEvents.backendRelease) {
previousBuffer->buffer->lock();
previousBuffer->buffer->unlockOnBufferRelease(self);
}
}
lastBuffer = current.buffer ? current.buffer->buffer : WP<IHLBuffer>{};
}
void CWLSurfaceResource::updateCursorShm() {
auto buf = current.buffer ? current.buffer->buffer : lastBuffer;
if (!buf)
return;
// TODO: actually use damage
auto& shmData = CCursorSurfaceRole::cursorPixelData(self.lock());
auto shmAttrs = buf->shm();
if (!shmAttrs.success) {
LOGM(TRACE, "updateCursorShm: ignoring, not a shm buffer");
return;
}
// no need to end, shm.
auto [pixelData, fmt, bufLen] = buf->beginDataPtr(0);
shmData.resize(bufLen);
memcpy(shmData.data(), pixelData, bufLen);
}
void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync) {
frame(when);
auto FEEDBACK = makeShared<CQueuedPresentationData>(self.lock());
FEEDBACK->attachMonitor(pMonitor);
FEEDBACK->discarded();
FEEDBACK->presented();
PROTO::presentation->queueData(FEEDBACK);
if (!pMonitor || !pMonitor->outTimeline || !syncobj || !needsExplicitSync)

View file

@ -87,10 +87,10 @@ class CWLSurfaceResource {
CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
int scale = 1;
SP<IHLBuffer> buffer;
SP<CHLBufferReference> buffer; // buffer ref will be released once the buffer is no longer locked. For checking if a buffer is attached to this state, check texture.
SP<CTexture> texture;
Vector2D offset;
Vector2D size;
Vector2D size, bufferSize;
struct {
bool hasDestination = false;
bool hasSource = false;
@ -116,7 +116,7 @@ class CWLSurfaceResource {
std::vector<WP<CMonitor>> enteredOutputs;
bool mapped = false;
std::vector<WP<CWLSubsurfaceResource>> subsurfaces;
WP<ISurfaceRole> role;
SP<ISurfaceRole> role;
WP<CViewportResource> viewportResource;
WP<CDRMSyncobjSurfaceResource> syncobj; // may not be present
@ -134,14 +134,21 @@ class CWLSurfaceResource {
SP<CWlSurface> resource;
wl_client* pClient = nullptr;
// tracks whether we should release the buffer
bool bufferReleased = false;
// this is for cursor dumb copy. Due to our (and wayland's...) architecture,
// this stupid-ass hack is used
WP<IHLBuffer> lastBuffer;
int stateLocks = 0;
void destroy();
void releaseBuffers(bool onlyCurrent = true);
void dropPendingBuffer();
void dropCurrentBuffer();
void commitPendingState();
void bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
void updateCursorShm();
friend class CWLPointerResource;
};
class CWLCompositorResource {

View file

@ -469,12 +469,12 @@ void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource
if (dragSurface) {
dnd.dndSurfaceDestroy = dragSurface->events.destroy.registerListener([this](std::any d) { abortDrag(); });
dnd.dndSurfaceCommit = dragSurface->events.commit.registerListener([this](std::any d) {
if (dnd.dndSurface->current.buffer && !dnd.dndSurface->mapped) {
if (dnd.dndSurface->current.texture && !dnd.dndSurface->mapped) {
dnd.dndSurface->map();
return;
}
if (dnd.dndSurface->current.buffer <= 0 && dnd.dndSurface->mapped) {
if (dnd.dndSurface->current.texture <= 0 && dnd.dndSurface->mapped) {
dnd.dndSurface->unmap();
return;
}
@ -660,13 +660,13 @@ void CWLDataDeviceProtocol::abortDrag() {
}
void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
if (!dnd.dndSurface || !dnd.dndSurface->current.buffer || !dnd.dndSurface->current.buffer->texture)
if (!dnd.dndSurface || !dnd.dndSurface->current.texture)
return;
const auto POS = g_pInputManager->getMouseCoordsInternal();
CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale);
g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.buffer->texture, &box, 1.F);
g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.texture, &box, 1.F);
box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F);
g_pHyprRenderer->damageBox(&box);

View file

@ -49,7 +49,7 @@ SP<CWlOutput> CWLOutputResource::getResource() {
}
void CWLOutputResource::updateState() {
if (!monitor)
if (!monitor || (owner && owner->defunct))
return;
if (resource->version() >= 2)
@ -84,6 +84,7 @@ void CWLOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver,
}
RESOURCE->self = RESOURCE;
RESOURCE->owner = self;
}
void CWLOutputProtocol::destroyResource(CWLOutputResource* resource) {

View file

@ -8,6 +8,7 @@
#include "../../helpers/signal/Signal.hpp"
class CMonitor;
class CWLOutputProtocol;
class CWLOutputResource {
public:
@ -20,7 +21,7 @@ class CWLOutputResource {
void updateState();
WP<CMonitor> monitor;
WP<CWLOutputProtocol> owner;
WP<CWLOutputResource> self;
private:
@ -40,6 +41,7 @@ class CWLOutputProtocol : public IWaylandProtocol {
void sendDone();
WP<CMonitor> monitor;
WP<CWLOutputProtocol> self;
// will mark the protocol for removal, will be removed when no. of bound outputs is 0 (or when overwritten by a new global)
void remove();
@ -61,5 +63,5 @@ class CWLOutputProtocol : public IWaylandProtocol {
};
namespace PROTO {
inline std::unordered_map<std::string, UP<CWLOutputProtocol>> outputs;
inline std::unordered_map<std::string, SP<CWLOutputProtocol>> outputs;
};

Some files were not shown because too many files have changed in this diff Show more