Merge branch 'hyprwm:main' into fix/cursor-min-padding

This commit is contained in:
drendog 2024-05-23 00:13:55 +02:00 committed by GitHub
commit 4e753b25a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
80 changed files with 5748 additions and 1370 deletions

View File

@ -16,7 +16,7 @@ project(Hyprland
set(HYPRLAND_VERSION ${VER})
set(PREFIX ${CMAKE_INSTALL_PREFIX})
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
configure_file(hyprland.pc.in hyprland.pc @ONLY)
configure_file(hyprland.pc.in hyprland.pc @ONLY)
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
@ -71,6 +71,7 @@ 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)
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Configuring Hyprland in Debug with CMake")
@ -89,9 +90,12 @@ include_directories(
"protocols/")
set(CMAKE_CXX_STANDARD 23)
add_compile_definitions(WLR_USE_UNSTABLE)
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith)
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value
-Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
message(STATUS "Checking deps...")
@ -109,7 +113,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET
wayland-server wayland-client wayland-cursor wayland-protocols
cairo pango pangocairo pixman-1
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm
hyprwayland-scanner>=0.3.6 hyprlang>=0.3.2 hyprcursor>=0.1.7
hyprwayland-scanner>=0.3.8 hyprlang>=0.3.2 hyprcursor>=0.1.7
)
file(GLOB_RECURSE SRCFILES "src/*.cpp")
@ -123,6 +127,8 @@ endif()
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
add_dependencies(Hyprland wlroots-hyprland)
set(USE_GPROF ON)
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags")
@ -130,8 +136,6 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Enabling ASan")
target_link_libraries(Hyprland asan)
pkg_check_modules(ffidep REQUIRED IMPORTED_TARGET libffi)
target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/libwayland-server.a PkgConfig::ffidep)
target_compile_options(Hyprland PUBLIC -fsanitize=address)
endif()
@ -150,8 +154,12 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
endif()
endif()
add_compile_options(-pg -no-pie -fno-builtin)
add_link_options(-pg -no-pie -fno-builtin)
add_compile_options(-fno-pie -fno-builtin)
add_link_options(-no-pie -fno-builtin)
if(USE_GPROF)
add_compile_options(-pg)
add_link_options(-pg)
endif()
endif()
check_include_file("execinfo.h" EXECINFOH)
@ -241,6 +249,13 @@ function(protocolNew protoPath protoName external)
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp)
endif()
endfunction()
function(protocolWayland)
execute_process(
COMMAND hyprwayland-scanner --wayland-enums ${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
target_sources(Hyprland PRIVATE protocols/wayland.cpp)
endfunction()
target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland/build/libwlroots.a
@ -254,7 +269,6 @@ target_link_libraries(Hyprland
protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true)
protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false)
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false)
protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false)
@ -266,6 +280,7 @@ protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer
protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true)
protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true)
protocolNew("protocols/kde-server-decoration.xml" "kde-server-decoration" true)
protocolNew("protocols/wlr-data-control-unstable-v1.xml" "wlr-data-control-unstable-v1" true)
protocolNew("subprojects/hyprland-protocols/protocols/hyprland-focus-grab-v1.xml" "hyprland-focus-grab-v1" true)
protocolNew("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true)
protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
@ -286,6 +301,10 @@ protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1
protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false)
protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false)
protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false)
protocolNew("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false)
protocolNew("unstable/primary-selection/primary-selection-unstable-v1.xml" "primary-selection-unstable-v1" false)
protocolWayland()
# tools
add_subdirectory(hyprctl)

View File

@ -46,17 +46,17 @@ pluginenv:
installheaders:
@if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi
# remove previous headers from hyprpm's dir
rm -fr ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols
mkdir -p ${PREFIX}/include/hyprland/wlroots-hyprland
mkdir -p ${PREFIX}/include/hyprland/wlr
mkdir -p ${PREFIX}/share/pkgconfig
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots-hyprland/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots-hyprland && cd ../../..
cd subprojects/wlroots-hyprland/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots-hyprland && cd ../../../..
cp ./protocols/*.h ${PREFIX}/include/hyprland/protocols
cp ./protocols/*.hpp ${PREFIX}/include/hyprland/protocols
cd subprojects/wlroots-hyprland/include/wlr && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../../..
cd subprojects/wlroots-hyprland/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../../..
cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi

View File

@ -13,11 +13,11 @@
]
},
"locked": {
"lastModified": 1713612213,
"narHash": "sha256-zJboXgWNpNhKyNF8H/3UYzWkx7w00TOCGKi3cwi+tsw=",
"lastModified": 1715791817,
"narHash": "sha256-J069Uhv/gCMFLX1dSh2f+9ZTM09r1Nv3oUfocCnWKow=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "cab4746180f210a3c1dd3d53e45c510e309e90e1",
"rev": "7c3aa03dffb53921e583ade3d4ae3f487e390e7e",
"type": "github"
},
"original": {
@ -61,11 +61,11 @@
]
},
"locked": {
"lastModified": 1713121246,
"narHash": "sha256-502X0Q0fhN6tJK7iEUA8CghONKSatW/Mqj4Wappd++0=",
"lastModified": 1715791527,
"narHash": "sha256-HhQ4zvGHrRjR63ltySSeg+x+0jb0lepiutWdnFhLRoo=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "78fcaa27ae9e1d782faa3ff06c8ea55ddce63706",
"rev": "969cb076e5b76f2e823aeca1937a3e1f159812ee",
"type": "github"
},
"original": {
@ -84,11 +84,11 @@
]
},
"locked": {
"lastModified": 1715287423,
"narHash": "sha256-B7AJIjOyWgVMKhu7DlOnWa0VprdhywUVHuB/j+EwSxM=",
"lastModified": 1716058375,
"narHash": "sha256-CwjWoVnBZE5SBpRx9dgSQGCr4Goxyfcyv3zZbOhVqzk=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "e2fc1c0eb8b392110588f478cce644348ead7271",
"rev": "3afed4364790aebe0426077631af1e164a9650cc",
"type": "github"
},
"original": {
@ -99,11 +99,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1715087517,
"narHash": "sha256-CLU5Tsg24Ke4+7sH8azHWXKd0CFd4mhLWfhYgUiDBpQ=",
"lastModified": 1716137900,
"narHash": "sha256-sowPU+tLQv8GlqtVtsXioTKeaQvlMz/pefcdwg8MvfM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b211b392b8486ee79df6cdfb1157ad2133427a29",
"rev": "6c0b7a92c30122196a761b440ac0d46d3d9954f1",
"type": "github"
},
"original": {
@ -152,11 +152,11 @@
]
},
"locked": {
"lastModified": 1714662532,
"narHash": "sha256-Pj2xGSYhapYbXL7sk7TTlOtCZcTfPQoL3fPbZeg7L4Y=",
"lastModified": 1716290197,
"narHash": "sha256-1u9Exrc7yx9qtES2brDh7/DDZ8w8ap1nboIOAtCgeuM=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "1f228ba2f1f254195c0b571302b37482861abee3",
"rev": "91e48d6acd8a5a611d26f925e51559ab743bc438",
"type": "github"
},
"original": {

View File

@ -90,24 +90,24 @@ std::vector<SInstanceData> instances() {
return result;
}
void request(std::string arg, int minArgs = 0) {
int request(std::string arg, int minArgs = 0) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
if (ARGS < minArgs) {
std::cout << "Not enough arguments, expected at least " << minArgs;
return;
return -1;
}
if (SERVERSOCKET < 0) {
std::cout << "Couldn't open a socket (1)";
return;
return 1;
}
if (instanceSignature.empty()) {
std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)";
return;
return 2;
}
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
@ -121,14 +121,14 @@ void request(std::string arg, int minArgs = 0) {
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
std::cout << "Couldn't connect to " << socketPath << ". (3)";
return;
return 3;
}
auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
if (sizeWritten < 0) {
std::cout << "Couldn't write (4)";
return;
return 4;
}
std::string reply = "";
@ -138,7 +138,7 @@ void request(std::string arg, int minArgs = 0) {
if (sizeWritten < 0) {
std::cout << "Couldn't read (5)";
return;
return 5;
}
reply += std::string(buffer, sizeWritten);
@ -147,7 +147,7 @@ void request(std::string arg, int minArgs = 0) {
sizeWritten = read(SERVERSOCKET, buffer, 8192);
if (sizeWritten < 0) {
std::cout << "Couldn't read (5)";
return;
return 5;
}
reply += std::string(buffer, sizeWritten);
}
@ -155,19 +155,21 @@ void request(std::string arg, int minArgs = 0) {
close(SERVERSOCKET);
std::cout << reply;
return 0;
}
void requestHyprpaper(std::string arg) {
int requestHyprpaper(std::string arg) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
if (SERVERSOCKET < 0) {
std::cout << "Couldn't open a socket (1)";
return;
return 1;
}
if (instanceSignature.empty()) {
std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)";
return;
return 2;
}
sockaddr_un serverAddress = {0};
@ -181,7 +183,7 @@ void requestHyprpaper(std::string arg) {
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
std::cout << "Couldn't connect to " << socketPath << ". (3)";
return;
return 3;
}
arg = arg.substr(arg.find_first_of('/') + 1); // strip flags
@ -191,7 +193,7 @@ void requestHyprpaper(std::string arg) {
if (sizeWritten < 0) {
std::cout << "Couldn't write (4)";
return;
return 4;
}
char buffer[8192] = {0};
@ -200,12 +202,14 @@ void requestHyprpaper(std::string arg) {
if (sizeWritten < 0) {
std::cout << "Couldn't read (5)";
return;
return 5;
}
close(SERVERSOCKET);
std::cout << std::string(buffer);
return 0;
}
void batchRequest(std::string arg, bool json) {
@ -384,33 +388,33 @@ int main(int argc, char** argv) {
if (fullRequest.contains("/--batch"))
batchRequest(fullRequest, json);
else if (fullRequest.contains("/hyprpaper"))
requestHyprpaper(fullRequest);
exitStatus = requestHyprpaper(fullRequest);
else if (fullRequest.contains("/switchxkblayout"))
request(fullRequest, 2);
exitStatus = request(fullRequest, 2);
else if (fullRequest.contains("/seterror"))
request(fullRequest, 1);
exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/setprop"))
request(fullRequest, 3);
exitStatus = request(fullRequest, 3);
else if (fullRequest.contains("/plugin"))
request(fullRequest, 1);
exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/dismissnotify"))
request(fullRequest, 0);
exitStatus = request(fullRequest, 0);
else if (fullRequest.contains("/notify"))
request(fullRequest, 2);
exitStatus = request(fullRequest, 2);
else if (fullRequest.contains("/output"))
request(fullRequest, 2);
exitStatus = request(fullRequest, 2);
else if (fullRequest.contains("/setcursor"))
request(fullRequest, 1);
exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/dispatch"))
request(fullRequest, 1);
exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/keyword"))
request(fullRequest, 2);
exitStatus = request(fullRequest, 2);
else if (fullRequest.contains("/decorations"))
request(fullRequest, 1);
exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/--help"))
std::cout << USAGE << std::endl;
else {
request(fullRequest);
exitStatus = request(fullRequest);
}
std::cout << std::endl;

View File

@ -4,4 +4,4 @@ Name: Hyprland
URL: https://github.com/hyprwm/Hyprland
Description: Hyprland header files
Version: @HYPRLAND_VERSION@
Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland -I${prefix}/hyprland/wlroots-hyprland
Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland -I${prefix}/hyprland/wlr

View File

@ -442,15 +442,18 @@ bool CPluginManager::updateHeaders(bool force) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment.");
const bool bShallow = HLVER.branch == "main" || HLVER.branch == "";
// let us give a bit of leg-room for shallowing
// due to timezones, etc.
const std::string SHALLOW_DATE =
removeBeginEndSpacesTabs(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+\%a \%b \%d \%H:\%M:\%S \%Y'");
if (m_bVerbose)
if (m_bVerbose && bShallow)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will shallow since: " + SHALLOW_DATE);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME + " --shallow-since='" + SHALLOW_DATE + "'");
std::string ret =
execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME + (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""));
if (!std::filesystem::exists(WORKINGDIR)) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Clone failed. Retrying without shallow.");

View File

@ -5,20 +5,9 @@ project('Hyprland', 'cpp', 'c',
'default_library=static',
'optimization=3',
'buildtype=release',
'debug=false'
# 'cpp_std=c++23' # not yet supported by meson, as of version 0.63.0
])
# clang v14.0.6 uses C++2b instead of C++23, so we've gotta account for that
# replace the following with a project default option once meson gets support for C++23
cpp_compiler = meson.get_compiler('cpp')
if cpp_compiler.has_argument('-std=c++23')
add_global_arguments('-std=c++23', language: 'cpp')
elif cpp_compiler.has_argument('-std=c++2b')
add_global_arguments('-std=c++2b', language: 'cpp')
else
error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)')
endif
'debug=false',
'cpp_std=c++23',
])
add_project_arguments(
[
@ -26,9 +15,11 @@ add_project_arguments(
'-Wno-unused-value',
'-Wno-missing-field-initializers',
'-Wno-narrowing',
'-Wno-pointer-arith',
],
language: 'cpp')
cpp_compiler = meson.get_compiler('cpp')
if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
@ -65,7 +56,7 @@ if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif
version_h = run_command('sh', '-c', 'scripts/generateVersion.sh')
version_h = run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
globber = run_command('find', 'src', '-name', '*.h*', check: true)
headers = globber.stdout().strip().split('\n')
@ -89,5 +80,5 @@ import('pkgconfig').generate(
url: 'https://github.com/hyprwm/Hyprland',
description: 'Hyprland header files',
install_dir: pkg_install_dir,
subdirs: ['', 'hyprland/protocols', 'hyprland/wlroots'],
subdirs: ['', 'hyprland/protocols', 'hyprland', 'hyprland/wlr'],
)

View File

@ -70,6 +70,9 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
postPatch = ''
# Fix hardcoded paths to /usr installation
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
# Remove extra @PREFIX@ to fix pkg-config paths
sed -i "s#@PREFIX@/##g" hyprland.pc.in
'';
DATE = date;

View File

@ -24,7 +24,6 @@ in {
inputs.hyprcursor.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprwayland-scanner.overlays.default
self.overlays.wayland-protocols
self.overlays.xwayland
# Hyprland packages themselves
@ -73,14 +72,4 @@ in {
'';
});
};
wayland-protocols = final: prev: {
wayland-protocols = prev.wayland-protocols.overrideAttrs (self: super: {
version = "1.35";
src = prev.fetchurl {
url = "https://gitlab.freedesktop.org/wayland/${super.pname}/-/releases/${self.version}/downloads/${super.pname}-${self.version}.tar.xz";
hash = "sha256-N6JxaigTPcgZNBxWiinSHoy3ITDlwSah/PyfQsI9las=";
};
});
};
}

View File

@ -17,14 +17,13 @@ wayland_scanner = find_program(
wayland_scanner_dep.get_variable('wayland_scanner'),
native: true,
)
hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.5', 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, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
['wlr-screencopy-unstable-v1.xml'],
@ -42,6 +41,7 @@ new_protocols = [
['wlr-output-management-unstable-v1.xml'],
['kde-server-decoration.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-data-control-unstable-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'],
@ -61,6 +61,8 @@ new_protocols = [
[wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'],
[wl_protocol_dir, 'stable/tablet/tablet-v2.xml'],
[wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'],
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/primary-selection/primary-selection-unstable-v1.xml'],
]
wl_protos_src = []
@ -97,12 +99,27 @@ foreach p : new_protocols
)
endforeach
wayland_server = dependency('wayland-server', version: '>=1.20.0')
wayland_server_dep = dependency('wayland-server', version: '>=1.20.0')
wayland_server_dir = wayland_server_dep.get_variable('pkgdatadir')
wl_server_protos = [
wayland_server_dir / 'wayland.xml'
]
wl_server_protos_gen = []
foreach p : wl_server_protos
wl_server_protos_gen += custom_target(
p.underscorify(),
input: p,
install: false,
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'],
)
endforeach
lib_server_protos = static_library(
'server_protos',
wl_protos_src + wl_protos_headers + new_wl_protos,
dependencies: wayland_server.partial_dependency(compile_args: true),
wl_protos_src + wl_protos_headers + new_wl_protos + wl_server_protos_gen,
dependencies: wayland_server_dep.partial_dependency(compile_args: true),
)
server_protos = declare_dependency(

View File

@ -0,0 +1,278 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_data_control_unstable_v1">
<copyright>
Copyright © 2018 Simon Ser
Copyright © 2019 Ivan Molodetskikh
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<description summary="control data devices">
This protocol allows a privileged client to control data devices. In
particular, the client will be able to manage the current selection and take
the role of a clipboard manager.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_data_control_manager_v1" version="2">
<description summary="manager to control data devices">
This interface is a manager that allows creating per-seat data device
controls.
</description>
<request name="create_data_source">
<description summary="create a new data source">
Create a new data source.
</description>
<arg name="id" type="new_id" interface="zwlr_data_control_source_v1"
summary="data source to create"/>
</request>
<request name="get_data_device">
<description summary="get a data device for a seat">
Create a data device that can be used to manage a seat's selection.
</description>
<arg name="id" type="new_id" interface="zwlr_data_control_device_v1"/>
<arg name="seat" type="object" interface="wl_seat"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_data_control_device_v1" version="2">
<description summary="manage a data device for a seat">
This interface allows a client to manage a seat's selection.
When the seat is destroyed, this object becomes inert.
</description>
<request name="set_selection">
<description summary="copy data to the selection">
This request asks the compositor to set the selection to the data from
the source on behalf of the client.
The given source may not be used in any further set_selection or
set_primary_selection requests. Attempting to use a previously used
source is a protocol error.
To unset the selection, set the source to NULL.
</description>
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
allow-null="true"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy this data device">
Destroys the data device object.
</description>
</request>
<event name="data_offer">
<description summary="introduce a new wlr_data_control_offer">
The data_offer event introduces a new wlr_data_control_offer object,
which will subsequently be used in either the
wlr_data_control_device.selection event (for the regular clipboard
selections) or the wlr_data_control_device.primary_selection event (for
the primary clipboard selections). Immediately following the
wlr_data_control_device.data_offer event, the new data_offer object
will send out wlr_data_control_offer.offer events to describe the MIME
types it offers.
</description>
<arg name="id" type="new_id" interface="zwlr_data_control_offer_v1"/>
</event>
<event name="selection">
<description summary="advertise new selection">
The selection event is sent out to notify the client of a new
wlr_data_control_offer for the selection for this device. The
wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
events are sent out immediately before this event to introduce the data
offer object. The selection event is sent to a client when a new
selection is set. The wlr_data_control_offer is valid until a new
wlr_data_control_offer or NULL is received. The client must destroy the
previous selection wlr_data_control_offer, if any, upon receiving this
event.
The first selection event is sent upon binding the
wlr_data_control_device object.
</description>
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
allow-null="true"/>
</event>
<event name="finished">
<description summary="this data control is no longer valid">
This data control object is no longer valid and should be destroyed by
the client.
</description>
</event>
<!-- Version 2 additions -->
<event name="primary_selection" since="2">
<description summary="advertise new primary selection">
The primary_selection event is sent out to notify the client of a new
wlr_data_control_offer for the primary selection for this device. The
wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
events are sent out immediately before this event to introduce the data
offer object. The primary_selection event is sent to a client when a
new primary selection is set. The wlr_data_control_offer is valid until
a new wlr_data_control_offer or NULL is received. The client must
destroy the previous primary selection wlr_data_control_offer, if any,
upon receiving this event.
If the compositor supports primary selection, the first
primary_selection event is sent upon binding the
wlr_data_control_device object.
</description>
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
allow-null="true"/>
</event>
<request name="set_primary_selection" since="2">
<description summary="copy data to the primary selection">
This request asks the compositor to set the primary selection to the
data from the source on behalf of the client.
The given source may not be used in any further set_selection or
set_primary_selection requests. Attempting to use a previously used
source is a protocol error.
To unset the primary selection, set the source to NULL.
The compositor will ignore this request if it does not support primary
selection.
</description>
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
allow-null="true"/>
</request>
<enum name="error" since="2">
<entry name="used_source" value="1"
summary="source given to set_selection or set_primary_selection was already used before"/>
</enum>
</interface>
<interface name="zwlr_data_control_source_v1" version="1">
<description summary="offer to transfer data">
The wlr_data_control_source object is the source side of a
wlr_data_control_offer. It is created by the source client in a data
transfer and provides a way to describe the offered data and a way to
respond to requests to transfer the data.
</description>
<enum name="error">
<entry name="invalid_offer" value="1"
summary="offer sent after wlr_data_control_device.set_selection"/>
</enum>
<request name="offer">
<description summary="add an offered MIME type">
This request adds a MIME type to the set of MIME types advertised to
targets. Can be called several times to offer multiple types.
Calling this after wlr_data_control_device.set_selection is a protocol
error.
</description>
<arg name="mime_type" type="string"
summary="MIME type offered by the data source"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy this source">
Destroys the data source object.
</description>
</request>
<event name="send">
<description summary="send the data">
Request for data from the client. Send the data as the specified MIME
type over the passed file descriptor, then close it.
</description>
<arg name="mime_type" type="string" summary="MIME type for the data"/>
<arg name="fd" type="fd" summary="file descriptor for the data"/>
</event>
<event name="cancelled">
<description summary="selection was cancelled">
This data source is no longer valid. The data source has been replaced
by another data source.
The client should clean up and destroy this data source.
</description>
</event>
</interface>
<interface name="zwlr_data_control_offer_v1" version="1">
<description summary="offer to transfer data">
A wlr_data_control_offer represents a piece of data offered for transfer
by another client (the source client). The offer describes the different
MIME types that the data can be converted to and provides the mechanism
for transferring the data directly from the source client.
</description>
<request name="receive">
<description summary="request that the data is transferred">
To transfer the offered data, the client issues this request and
indicates the MIME type it wants to receive. The transfer happens
through the passed file descriptor (typically created with the pipe
system call). The source client writes the data in the MIME type
representation requested and then closes the file descriptor.
The receiving client reads from the read end of the pipe until EOF and
then closes its end, at which point the transfer is complete.
This request may happen multiple times for different MIME types.
</description>
<arg name="mime_type" type="string"
summary="MIME type desired by receiver"/>
<arg name="fd" type="fd" summary="file descriptor for data transfer"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy this offer">
Destroys the data offer object.
</description>
</request>
<event name="offer">
<description summary="advertise offered MIME type">
Sent immediately after creating the wlr_data_control_offer object.
One event per offered MIME type.
</description>
<arg name="mime_type" type="string" summary="offered MIME type"/>
</event>
</interface>
</protocol>

View File

@ -4,6 +4,7 @@
#include "managers/CursorManager.hpp"
#include "managers/TokenManager.hpp"
#include "managers/PointerManager.hpp"
#include "managers/SeatManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include <random>
#include <unordered_set>
@ -17,6 +18,7 @@
#include "protocols/FractionalScale.hpp"
#include "protocols/PointerConstraints.hpp"
#include "protocols/LayerShell.hpp"
#include "protocols/XDGShell.hpp"
#include "desktop/LayerSurface.hpp"
#include <sys/types.h>
@ -222,16 +224,12 @@ void CCompositor::initServer() {
m_sWLRCompositor = wlr_compositor_create(m_sWLDisplay, 6, m_sWLRRenderer);
m_sWLRSubCompositor = wlr_subcompositor_create(m_sWLDisplay);
m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay);
// m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay);
wlr_data_control_manager_v1_create(m_sWLDisplay);
wlr_primary_selection_v1_device_manager_create(m_sWLDisplay);
// wlr_data_control_manager_v1_create(m_sWLDisplay);
// wlr_primary_selection_v1_device_manager_create(m_sWLDisplay);
wlr_viewporter_create(m_sWLDisplay);
m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay, 6);
m_sSeat.seat = wlr_seat_create(m_sWLDisplay, "seat0");
m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend);
if (!m_sWRLDRMLeaseMgr) {
Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager");
@ -254,14 +252,7 @@ void CCompositor::initServer() {
void CCompositor::initAllSignals() {
addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend");
addWLSignal(&m_sWLRXDGShell->events.new_toplevel, &Events::listen_newXDGToplevel, m_sWLRXDGShell, "XDG Shell");
addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend");
addWLSignal(&m_sSeat.seat->events.request_set_cursor, &Events::listen_requestMouse, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_start_drag, &Events::listen_requestDrag, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.start_drag, &Events::listen_startDrag, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_set_selection, &Events::listen_requestSetSel, &m_sSeat, "Seat");
addWLSignal(&m_sSeat.seat->events.request_set_primary_selection, &Events::listen_requestSetPrimarySel, &m_sSeat, "Seat");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
if (m_sWRLDRMLeaseMgr)
@ -273,14 +264,7 @@ void CCompositor::initAllSignals() {
void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_newOutput);
removeWLSignal(&Events::listen_newXDGToplevel);
removeWLSignal(&Events::listen_newInput);
removeWLSignal(&Events::listen_requestMouse);
removeWLSignal(&Events::listen_requestSetSel);
removeWLSignal(&Events::listen_requestDrag);
removeWLSignal(&Events::listen_startDrag);
removeWLSignal(&Events::listen_requestSetSel);
removeWLSignal(&Events::listen_requestSetPrimarySel);
removeWLSignal(&Events::listen_RendererDestroy);
if (m_sWRLDRMLeaseMgr)
@ -379,9 +363,8 @@ void CCompositor::cleanup() {
g_pHookSystem.reset();
g_pWatchdog.reset();
g_pXWaylandManager.reset();
if (m_sSeat.seat)
wlr_seat_destroy(m_sSeat.seat);
g_pPointerManager.reset();
g_pSeatManager.reset();
if (m_sWLRRenderer)
wlr_renderer_destroy(m_sWLRRenderer);
@ -412,6 +395,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the ProtocolManager!");
g_pProtocolManager = std::make_unique<CProtocolManager>();
Debug::log(LOG, "Creating the SeatManager!");
g_pSeatManager = std::make_unique<CSeatManager>();
Debug::log(LOG, "Creating the KeybindManager!");
g_pKeybindManager = std::make_unique<CKeybindManager>();
@ -806,22 +792,28 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW p
RASSERT(!pWindow->m_bIsX11, "Cannot call vectorWindowToSurface on an X11 window!");
const auto PSURFACE = pWindow->m_uSurface.xdg;
double subx, suby;
double subx, suby;
CBox geom = pWindow->m_pXDGSurface->current.geometry;
// calc for oversized windows... fucking bullshit, again.
CBox geom;
wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr());
geom.applyFromWlr();
// try popups first
const auto PPOPUP = pWindow->m_pPopupHead->at(pos);
const auto PFOUND =
wlr_xdg_surface_surface_at(PSURFACE, pos.x - pWindow->m_vRealPosition.value().x + geom.x, pos.y - pWindow->m_vRealPosition.value().y + geom.y, &subx, &suby);
wlr_surface* found = PPOPUP ? PPOPUP->m_sWLSurface.wlr() : nullptr;
if (PFOUND) {
if (!PPOPUP)
found = wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), pos.x - pWindow->m_vRealPosition.value().x + geom.x, pos.y - pWindow->m_vRealPosition.value().y + geom.y, &subx,
&suby);
else {
const auto OFF = PPOPUP->coordsRelativeToParent();
subx = pos.x - OFF.x + geom.x - pWindow->m_vRealPosition.goal().x;
suby = pos.y - OFF.y + geom.y - pWindow->m_vRealPosition.goal().y;
}
if (found) {
sl.x = subx;
sl.y = suby;
return PFOUND;
return found;
}
sl.x = pos.x - pWindow->m_vRealPosition.value().x;
@ -830,7 +822,7 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW p
sl.x += geom.x;
sl.y += geom.y;
return PSURFACE->surface;
return pWindow->m_pWLSurface.wlr();
}
Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindow, wlr_surface* pSurface) {
@ -840,12 +832,14 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo
if (pWindow->m_bIsX11)
return vec - pWindow->m_vRealPosition.goal();
const auto PSURFACE = pWindow->m_uSurface.xdg;
const auto PPOPUP = pWindow->m_pPopupHead->at(vec);
if (PPOPUP)
return vec - PPOPUP->coordsGlobal();
std::tuple<wlr_surface*, int, int> iterData = {pSurface, -1337, -1337};
wlr_xdg_surface_for_each_surface(
PSURFACE,
wlr_surface_for_each_surface(
pWindow->m_pWLSurface.wlr(),
[](wlr_surface* surf, int x, int y, void* data) {
const auto PDATA = (std::tuple<wlr_surface*, int, int>*)data;
if (surf == std::get<0>(*PDATA)) {
@ -855,9 +849,7 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo
},
&iterData);
CBox geom = {};
wlr_xdg_surface_get_geometry(PSURFACE, geom.pWlr());
geom.applyFromWlr();
CBox geom = pWindow->m_pXDGSurface->current.geometry;
if (std::get<1>(iterData) == -1337 && std::get<2>(iterData) == -1337)
return vec - pWindow->m_vRealPosition.goal();
@ -890,8 +882,8 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) {
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
static auto PSPECIALFALLTHROUGH = CConfigValue<Hyprlang::INT>("input:special_fallthrough");
if (g_pCompositor->m_sSeat.exclusiveClient) {
Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer.");
if (g_pSessionLockManager->isSessionLocked()) {
Debug::log(LOG, "Refusing a keyboard focus to a window because of a sessionlock");
return;
}
@ -919,7 +911,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) {
g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
}
wlr_seat_keyboard_notify_clear_focus(m_sSeat.seat);
g_pSeatManager->setKeyboardFocus(nullptr);
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""});
@ -939,7 +931,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) {
return;
}
if (m_pLastWindow.lock() == pWindow && m_sSeat.seat->keyboard_state.focused_surface == pSurface)
if (m_pLastWindow.lock() == pWindow && g_pSeatManager->state.keyboardFocus == pSurface)
return;
if (pWindow->m_bPinned)
@ -994,7 +986,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) {
pWindow->m_bIsUrgent = false;
// Send an event
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(pWindow) + "," + pWindow->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", pWindow->m_szClass + "," + pWindow->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)pWindow.get())});
EMIT_HOOK_EVENT("activeWindow", pWindow);
@ -1017,12 +1009,17 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) {
void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) {
if (m_sSeat.seat->keyboard_state.focused_surface == pSurface || (pWindowOwner && m_sSeat.seat->keyboard_state.focused_surface == pWindowOwner->m_pWLSurface.wlr()))
if (g_pSeatManager->state.keyboardFocus == pSurface || (pWindowOwner && g_pSeatManager->state.keyboardFocus == pWindowOwner->m_pWLSurface.wlr()))
return; // Don't focus when already focused on this.
if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface))
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);
return;
}
const auto PLASTSURF = m_pLastFocus;
// Unfocus last surface if should
@ -1030,7 +1027,7 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) {
g_pXWaylandManager->activateSurface(m_pLastFocus, false);
if (!pSurface) {
wlr_seat_keyboard_clear_focus(m_sSeat.seat);
g_pSeatManager->setKeyboardFocus(nullptr);
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); // unfocused
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""});
EMIT_HOOK_EVENT("keyboardFocus", (wlr_surface*)nullptr);
@ -1038,17 +1035,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) {
return;
}
if (const auto KEYBOARD = wlr_seat_get_keyboard(m_sSeat.seat); KEYBOARD) {
uint32_t keycodes[WLR_KEYBOARD_KEYS_CAP] = {0}; // TODO: maybe send valid, non-keybind codes?
wlr_seat_keyboard_notify_enter(m_sSeat.seat, pSurface, keycodes, 0, &KEYBOARD->modifiers);
wlr_seat_keyboard_focus_change_event event = {
.seat = m_sSeat.seat,
.old_surface = m_pLastFocus,
.new_surface = pSurface,
};
wl_signal_emit_mutable(&m_sSeat.seat->keyboard_state.events.focus_change, &event);
}
if (g_pSeatManager->keyboard)
g_pSeatManager->setKeyboardFocus(pSurface);
if (pWindowOwner)
Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface, pWindowOwner);
@ -1245,27 +1233,6 @@ PHLWINDOW CCompositor::getTopLeftWindowOnWorkspace(const int& id) {
return nullptr;
}
bool CCompositor::doesSeatAcceptInput(wlr_surface* surface) {
if (g_pSessionLockManager->isSessionLocked()) {
if (g_pSessionLockManager->isSurfaceSessionLock(surface))
return true;
if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource))
return true;
return false;
}
if (m_sSeat.exclusiveClient) {
if (surface && m_sSeat.exclusiveClient == wl_resource_get_client(surface->resource))
return true;
return false;
}
return true;
}
bool CCompositor::isWindowActive(PHLWINDOW pWindow) {
if (m_pLastWindow.expired() && !m_pLastFocus)
return false;
@ -1419,7 +1386,8 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
return nullptr;
// 0 -> history, 1 -> shared length
static auto PMETHOD = CConfigValue<Hyprlang::INT>("binds:focus_preferred_method");
static auto PMETHOD = CConfigValue<Hyprlang::INT>("binds:focus_preferred_method");
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
@ -1449,6 +1417,9 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen)
continue;
if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID)
continue;
const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();
const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y);
@ -1538,6 +1509,9 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
if (PWORKSPACE->m_bHasFullscreenWindow && !w->m_bIsFullscreen && !w->m_bCreatedOverFullscreen)
continue;
if (!*PMONITORFALLBACK && pWindow->m_iMonitorID != w->m_iMonitorID)
continue;
const auto DIST = w->middle().distance(pWindow->middle());
const auto ANGLE = vectorAngles(Vector2D{w->middle() - pWindow->middle()}, VECTORS.at(dir));
@ -2341,7 +2315,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) {
switch (mode) {
case MODE_CLASS_REGEX: {
const auto windowClass = g_pXWaylandManager->getAppIDClass(w);
const auto windowClass = w->m_szClass;
if (!std::regex_search(windowClass, regexCheck))
continue;
break;
@ -2353,7 +2327,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) {
break;
}
case MODE_TITLE_REGEX: {
const auto windowTitle = g_pXWaylandManager->getTitle(w);
const auto windowTitle = w->m_szTitle;
if (!std::regex_search(windowTitle, regexCheck))
continue;
break;
@ -2492,7 +2466,7 @@ void CCompositor::forceReportSizesToWindowsOnWorkspace(const int& wid) {
}
}
PHLWORKSPACE CCompositor::createNewWorkspace(const int& id, const int& monid, const std::string& name) {
PHLWORKSPACE CCompositor::createNewWorkspace(const int& id, const int& monid, const std::string& name, bool isEmtpy) {
const auto NAME = name == "" ? std::to_string(id) : name;
auto monID = monid;
@ -2503,7 +2477,7 @@ PHLWORKSPACE CCompositor::createNewWorkspace(const int& id, const int& monid, co
const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2;
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, monID, NAME, SPECIAL));
const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, monID, NAME, SPECIAL, isEmtpy));
PWORKSPACE->m_fAlpha.setValueAndWarp(0);

View File

@ -48,10 +48,8 @@ class CCompositor {
wlr_allocator* m_sWLRAllocator;
wlr_compositor* m_sWLRCompositor;
wlr_subcompositor* m_sWLRSubCompositor;
wlr_data_device_manager* m_sWLRDataDevMgr;
wlr_drm* m_sWRLDRM;
wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr;
wlr_xdg_shell* m_sWLRXDGShell;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
@ -87,8 +85,6 @@ class CCompositor {
std::vector<PHLWINDOWREF> m_vWindowFocusHistory; // first element is the most recently focused.
SSeat m_sSeat;
bool m_bReadyToProcess = false;
bool m_bSessionActive = true;
bool m_bDPMSStateON = true;
@ -132,7 +128,6 @@ class CCompositor {
PHLWINDOW getFirstWindowOnWorkspace(const int&);
PHLWINDOW getTopLeftWindowOnWorkspace(const int&);
PHLWINDOW getFullscreenWindowOnWorkspace(const int&);
bool doesSeatAcceptInput(wlr_surface*);
bool isWindowActive(PHLWINDOW);
void changeWindowZOrder(PHLWINDOW, bool);
void cleanupFadingOut(const int& monid);
@ -164,7 +159,7 @@ class CCompositor {
void closeWindow(PHLWINDOW);
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
void forceReportSizesToWindowsOnWorkspace(const int&);
PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = ""); // will be deleted next frame if left empty and unfocused!
PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = "", bool isEmtpy = true); // will be deleted next frame if left empty and unfocused!
void renameWorkspace(const int&, const std::string& name = "");
void setActiveMonitor(CMonitor*);
bool isWorkspaceSpecial(const int&);

View File

@ -6,6 +6,7 @@
#include "helpers/VarList.hpp"
#include "../protocols/LayerShell.hpp"
#include <cstdint>
#include <string.h>
#include <string>
#include <sys/stat.h>
@ -18,6 +19,7 @@
#include <iostream>
#include <sstream>
#include <ranges>
#include <xkbcommon/xkbcommon.h>
extern "C" char** environ;
@ -322,7 +324,8 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:disable_hyprland_logo", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:disable_splash_rendering", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:col.splash", Hyprlang::INT{0x55ffffff});
m_pConfig->addConfigValue("misc:splash_font_family", {"Sans"});
m_pConfig->addConfigValue("misc:splash_font_family", {STRVAL_EMPTY});
m_pConfig->addConfigValue("misc:font_family", {"Sans"});
m_pConfig->addConfigValue("misc:force_default_wallpaper", Hyprlang::INT{-1});
m_pConfig->addConfigValue("misc:vfr", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:vrr", Hyprlang::INT{0});
@ -346,11 +349,12 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:background_color", Hyprlang::INT{0xff111111});
m_pConfig->addConfigValue("misc:new_window_takes_over_fullscreen", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:initial_workspace_tracking", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:middle_click_paste", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:focus_removed_window", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:groupbar:enabled", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:groupbar:font_family", {"Sans"});
m_pConfig->addConfigValue("group:groupbar:font_family", {STRVAL_EMPTY});
m_pConfig->addConfigValue("group:groupbar:font_size", Hyprlang::INT{8});
m_pConfig->addConfigValue("group:groupbar:gradients", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:groupbar:height", Hyprlang::INT{14});
@ -358,6 +362,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("group:groupbar:render_titles", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:groupbar:scrolling", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:groupbar:text_color", Hyprlang::INT{0xffffffff});
m_pConfig->addConfigValue("group:groupbar:stacked", Hyprlang::INT{0});
m_pConfig->addConfigValue("debug:int", Hyprlang::INT{0});
m_pConfig->addConfigValue("debug:log_damage", Hyprlang::INT{0});
@ -370,6 +375,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("debug:manual_crash", Hyprlang::INT{0});
m_pConfig->addConfigValue("debug:suppress_errors", Hyprlang::INT{0});
m_pConfig->addConfigValue("debug:error_limit", Hyprlang::INT{5});
m_pConfig->addConfigValue("debug:error_position", Hyprlang::INT{0});
m_pConfig->addConfigValue("debug:watchdog_timeout", Hyprlang::INT{5});
m_pConfig->addConfigValue("debug:disable_scale_checks", Hyprlang::INT{0});
m_pConfig->addConfigValue("debug:colored_stdout_logs", Hyprlang::INT{1});
@ -489,6 +495,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("binds:ignore_group_lock", Hyprlang::INT{0});
m_pConfig->addConfigValue("binds:movefocus_cycles_fullscreen", Hyprlang::INT{1});
m_pConfig->addConfigValue("binds:disable_keybind_grabbing", Hyprlang::INT{0});
m_pConfig->addConfigValue("binds:window_direction_monitor_fallback", Hyprlang::INT{1});
m_pConfig->addConfigValue("gestures:workspace_swipe", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_fingers", Hyprlang::INT{3});
@ -1040,10 +1047,13 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
if (!valid(pWindow))
return std::vector<SWindowRule>();
// if the window is unmapped, don't process exec rules yet.
shadowExec = shadowExec || !pWindow->m_bIsMapped;
std::vector<SWindowRule> returns;
std::string title = g_pXWaylandManager->getTitle(pWindow);
std::string appidclass = g_pXWaylandManager->getAppIDClass(pWindow);
std::string title = pWindow->m_szTitle;
std::string appidclass = pWindow->m_szClass;
Debug::log(LOG, "Searching for matching rules for {} (title: {})", appidclass, title);
@ -1903,6 +1913,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
bool multiKey = false;
const auto BINDARGS = command.substr(4);
for (auto& arg : BINDARGS) {
@ -1920,6 +1931,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
transparent = true;
} else if (arg == 'i') {
ignoreMods = true;
} else if (arg == 's') {
multiKey = true;
} else {
return "bind: invalid flag";
}
@ -1938,10 +1951,21 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse))
return "bind: too many args";
std::set<xkb_keysym_t> KEYSYMS;
std::set<xkb_keysym_t> MODS;
if (multiKey) {
for (auto splitKey : CVarList(ARGS[1], 8, '&')) {
KEYSYMS.insert(xkb_keysym_from_name(splitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
}
for (auto splitMod : CVarList(ARGS[0], 8, '&')) {
MODS.insert(xkb_keysym_from_name(splitMod.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
}
}
const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]);
const auto MODSTR = ARGS[0];
const auto KEY = ARGS[1];
const auto KEY = multiKey ? "" : ARGS[1];
auto HANDLER = ARGS[2];
@ -1965,7 +1989,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
return "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod.";
}
if (KEY != "") {
if ((KEY != "") || multiKey) {
SParsedKey parsedKey = parseKey(KEY);
if (parsedKey.catchAll && m_szCurrentSubmap == "") {
@ -1973,8 +1997,8 @@ 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, parsedKey.keycode, parsedKey.catchAll, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse,
nonConsuming, transparent, ignoreMods});
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, release,
repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey});
}
return {};

View File

@ -210,10 +210,10 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
(uintptr_t)w.get(), (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (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,
escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (int64_t)w->m_iMonitorID,
escapeJSONStrings(g_pXWaylandManager->getAppIDClass(w)), escapeJSONStrings(g_pXWaylandManager->getTitle(w)), 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), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
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),
(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\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
@ -221,8 +221,8 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\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, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w),
g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen,
(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (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),
(uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w));
}
@ -1550,6 +1550,18 @@ std::string dispatchDismissNotify(eHyprCtlOutputFormat format, std::string reque
return "ok";
}
std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
std::string lockedStr = g_pSessionLockManager->isSessionLocked() ? "true" : "false";
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
lockedStr = std::format(R"#(
{{
"locked": {}
}}
)#",
lockedStr);
return lockedStr;
}
CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
@ -1569,6 +1581,7 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});

View File

@ -1,4 +1,6 @@
#include <pango/pangocairo.h>
#include "HyprDebugOverlay.hpp"
#include "config/ConfigValue.hpp"
#include "../Compositor.hpp"
void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float µs) {
@ -47,11 +49,6 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
if (!m_pMonitor)
return 0;
int yOffset = offset;
cairo_text_extents_t cairoExtents;
float maxX = 0;
std::string text = "";
// get avg fps
float avgFrametime = 0;
float maxFrametime = 0;
@ -105,23 +102,49 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
float varAnimMgrTick = maxAnimMgrTick - minAnimMgrTick;
avgAnimMgrTick /= m_dLastAnimationTicks.size() == 0 ? 1 : m_dLastAnimationTicks.size();
const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms
const float idealFPS = m_dLastFrametimes.size();
const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms
const float idealFPS = m_dLastFrametimes.size();
cairo_select_font_face(g_pDebugOverlay->m_pCairo, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
PangoLayout* layoutText = pango_cairo_create_layout(g_pDebugOverlay->m_pCairo);
PangoFontDescription* pangoFD = pango_font_description_new();
cairo_set_font_size(g_pDebugOverlay->m_pCairo, 10);
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL);
pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL);
float maxTextW = 0;
int fontSize = 0;
auto cr = g_pDebugOverlay->m_pCairo;
auto showText = [cr, layoutText, pangoFD, &maxTextW, &fontSize](const char* text, int size) {
if (fontSize != size) {
pango_font_description_set_absolute_size(pangoFD, size * PANGO_SCALE);
pango_layout_set_font_description(layoutText, pangoFD);
fontSize = size;
}
pango_layout_set_text(layoutText, text, -1);
pango_cairo_show_layout(cr, layoutText);
int textW = 0, textH = 0;
pango_layout_get_size(layoutText, &textW, &textH);
textW /= PANGO_SCALE;
textH /= PANGO_SCALE;
if (textW > maxTextW)
maxTextW = textW;
// move to next line
cairo_rel_move_to(cr, 0, fontSize + 1);
};
const int MARGIN_TOP = 8;
const int MARGIN_LEFT = 4;
cairo_move_to(cr, MARGIN_LEFT, MARGIN_TOP + offset);
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f);
yOffset += 10;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = m_pMonitor->szName;
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
maxX = cairoExtents.width;
cairo_set_font_size(g_pDebugOverlay->m_pCairo, 16);
std::string text;
showText(m_pMonitor->szName.c_str(), 10);
if (FPS > idealFPS * 0.95f)
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 0.2f, 1.f, 0.2f, 1.f);
@ -130,57 +153,35 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
else
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 0.2f, 0.2f, 1.f);
yOffset += 17;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::format("{} FPS", (int)FPS);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
maxX = cairoExtents.width;
showText(text.c_str(), 16);
cairo_set_font_size(g_pDebugOverlay->m_pCairo, 10);
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f);
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::format("Avg Frametime: {:.2f}ms (var {:.2f}ms)", avgFrametime, varFrametime);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
maxX = cairoExtents.width;
showText(text.c_str(), 10);
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::format("Avg Rendertime: {:.2f}ms (var {:.2f}ms)", avgRenderTime, varRenderTime);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
maxX = cairoExtents.width;
showText(text.c_str(), 10);
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::format("Avg Rendertime (No Overlay): {:.2f}ms (var {:.2f}ms)", avgRenderTimeNoOverlay, varRenderTimeNoOverlay);
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
maxX = cairoExtents.width;
showText(text.c_str(), 10);
yOffset += 11;
cairo_move_to(g_pDebugOverlay->m_pCairo, 0, yOffset);
text = std::format("Avg Anim Tick: {:.2f}ms (var {:.2f}ms) ({:.2f} TPS)", avgAnimMgrTick, varAnimMgrTick, 1.0 / (avgAnimMgrTick / 1000.0));
cairo_show_text(g_pDebugOverlay->m_pCairo, text.c_str());
cairo_text_extents(g_pDebugOverlay->m_pCairo, text.c_str(), &cairoExtents);
if (cairoExtents.width > maxX)
maxX = cairoExtents.width;
showText(text.c_str(), 10);
yOffset += 11;
pango_font_description_free(pangoFD);
g_object_unref(layoutText);
double posX = 0, posY = 0;
cairo_get_current_point(cr, &posX, &posY);
g_pHyprRenderer->damageBox(&m_wbLastDrawnBox);
m_wbLastDrawnBox = {(int)g_pCompositor->m_vMonitors.front()->vecPosition.x, (int)g_pCompositor->m_vMonitors.front()->vecPosition.y + offset - 1, (int)maxX + 2,
yOffset - offset + 2};
m_wbLastDrawnBox = {(int)g_pCompositor->m_vMonitors.front()->vecPosition.x + MARGIN_LEFT - 1, (int)g_pCompositor->m_vMonitors.front()->vecPosition.y + offset + MARGIN_TOP - 1,
(int)maxTextW + 2, posY - offset - MARGIN_TOP + 2};
g_pHyprRenderer->damageBox(&m_wbLastDrawnBox);
return yOffset - offset;
return posY - offset;
}
void CHyprDebugOverlay::renderData(CMonitor* pMonitor, float µs) {

View File

@ -1,6 +1,20 @@
#include <numeric>
#include <pango/pangocairo.h>
#include "HyprNotificationOverlay.hpp"
#include "../Compositor.hpp"
#include <pango/pangocairo.h>
#include "../config/ConfigValue.hpp"
inline auto iconBackendFromLayout(PangoLayout* layout) {
// preference: Nerd > FontAwesome > text
auto eIconBackendChecks = std::array<eIconBackend, 2>{ICONS_BACKEND_NF, ICONS_BACKEND_FA};
for (auto iconID : eIconBackendChecks) {
auto iconsText = std::accumulate(ICONS_ARRAY[iconID].begin(), ICONS_ARRAY[iconID].end(), std::string());
pango_layout_set_text(layout, iconsText.c_str(), -1);
if (pango_layout_get_unknown_glyphs_count(layout) == 0)
return iconID;
}
return ICONS_BACKEND_NONE;
}
CHyprNotificationOverlay::CHyprNotificationOverlay() {
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
@ -9,31 +23,6 @@ CHyprNotificationOverlay::CHyprNotificationOverlay() {
g_pHyprRenderer->damageBox(&m_bLastDamage);
});
// check for the icon backend
std::string fonts = execAndGet("fc-list");
std::string fontsLower = fonts;
std::transform(fontsLower.begin(), fontsLower.end(), fontsLower.begin(), [&](char& i) { return std::tolower(i); });
size_t index = 0;
if (index = fontsLower.find("nerd"); index != std::string::npos) {
m_eIconBackend = ICONS_BACKEND_NF;
} else if (index = fontsLower.find("font awesome"); index != std::string::npos) {
m_eIconBackend = ICONS_BACKEND_FA;
} else if (index = fontsLower.find("fontawesome"); index != std::string::npos) {
m_eIconBackend = ICONS_BACKEND_FA;
} else {
return;
}
const auto LASTNEWLINE = fonts.find_last_of('\n', index);
const auto COLON = fonts.find(':', LASTNEWLINE);
const auto COMMA = fonts.find(',', COLON);
const auto NEWLINE = fonts.find('\n', COLON);
const auto LASTCHAR = COMMA < NEWLINE ? COMMA : NEWLINE;
m_szIconFontName = fonts.substr(COLON + 2, LASTCHAR - (COLON + 2));
}
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
@ -81,25 +70,24 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
float offsetY = 10;
float maxWidth = 0;
const auto SCALE = pMonitor->scale;
const auto SCALE = pMonitor->scale;
const auto MONSIZE = pMonitor->vecTransformedSize;
cairo_text_extents_t cairoExtents;
int iconW = 0, iconH = 0;
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
cairo_select_font_face(m_pCairo, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
PangoLayout* layout = pango_cairo_create_layout(m_pCairo);
PangoFontDescription* pangoFD = pango_font_description_new();
const auto PBEZIER = g_pAnimationManager->getBezier("default");
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL);
pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL);
const auto iconBackendID = iconBackendFromLayout(layout);
const auto PBEZIER = g_pAnimationManager->getBezier("default");
for (auto& notif : m_dNotifications) {
const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD;
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
PangoLayout* pangoLayout = pango_cairo_create_layout(m_pCairo);
PangoFontDescription* pangoFD = pango_font_description_from_string(("Sans " + std::to_string(FONTSIZE * ICON_SCALE)).c_str());
pango_layout_set_font_description(pangoLayout, pangoFD);
cairo_set_font_size(m_pCairo, FONTSIZE);
const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD;
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
// first rect (bg, col)
const float FIRSTRECTANIMP =
@ -122,31 +110,37 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
const float THIRDRECTPERC = notif->started.getMillis() / notif->timeMs;
// get text size
cairo_text_extents(m_pCairo, notif->text.c_str(), &cairoExtents);
const auto ICON = ICONS_ARRAY[m_eIconBackend][notif->icon];
const auto ICON = ICONS_ARRAY[iconBackendID][notif->icon];
const auto ICONCOLOR = ICONS_COLORS[notif->icon];
pango_layout_set_text(pangoLayout, ICON.c_str(), -1);
pango_layout_set_font_description(pangoLayout, pangoFD);
pango_cairo_update_layout(m_pCairo, pangoLayout);
pango_layout_get_size(pangoLayout, &iconW, &iconH);
int iconW = 0, iconH = 0;
pango_font_description_set_absolute_size(pangoFD, PANGO_SCALE * FONTSIZE * ICON_SCALE);
pango_layout_set_font_description(layout, pangoFD);
pango_layout_set_text(layout, ICON.c_str(), -1);
pango_layout_get_size(layout, &iconW, &iconH);
iconW /= PANGO_SCALE;
iconH /= PANGO_SCALE;
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
int textW = 0, textH = 0;
pango_font_description_set_absolute_size(pangoFD, PANGO_SCALE * FONTSIZE);
pango_layout_set_font_description(layout, pangoFD);
pango_layout_set_text(layout, notif->text.c_str(), -1);
pango_layout_get_size(layout, &textW, &textH);
textW /= PANGO_SCALE;
textH /= PANGO_SCALE;
const auto NOTIFSIZE = Vector2D{cairoExtents.width + 20 + iconW + 2 * ICONPADFORNOTIF, cairoExtents.height + 10};
const auto NOTIFSIZE = Vector2D{textW + 20 + iconW + 2 * ICONPADFORNOTIF, textH + 10};
// draw rects
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y);
cairo_fill(m_pCairo);
cairo_set_source_rgb(m_pCairo, 0.f, 0.f, 0.f);
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC, offsetY, NOTIFSIZE.x * SECONDRECTPERC, NOTIFSIZE.y);
cairo_fill(m_pCairo);
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2);
cairo_fill(m_pCairo);
@ -164,26 +158,27 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
// draw icon
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY + std::round((NOTIFSIZE.y - iconH - 4) / 2.0));
pango_cairo_show_layout(m_pCairo, pangoLayout);
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY - 2 + std::round((NOTIFSIZE.y - iconH) / 2.0));
pango_layout_set_text(layout, ICON.c_str(), -1);
pango_cairo_show_layout(m_pCairo, layout);
}
// draw text
cairo_set_font_size(m_pCairo, FONTSIZE);
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY + FONTSIZE + (FONTSIZE / 10.0));
cairo_show_text(m_pCairo, notif->text.c_str());
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY - 2 + std::round((NOTIFSIZE.y - textH) / 2.0));
pango_layout_set_text(layout, notif->text.c_str(), -1);
pango_cairo_show_layout(m_pCairo, layout);
// adjust offset and move on
offsetY += NOTIFSIZE.y + 10;
if (maxWidth < NOTIFSIZE.x)
maxWidth = NOTIFSIZE.x;
pango_font_description_free(pangoFD);
g_object_unref(pangoLayout);
}
pango_font_description_free(pangoFD);
g_object_unref(layout);
// cleanup notifs
std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });

View File

@ -59,9 +59,6 @@ class CHyprNotificationOverlay {
Vector2D m_vecLastSize = Vector2D(-1, -1);
CTexture m_tTexture;
eIconBackend m_eIconBackend = ICONS_BACKEND_NONE;
std::string m_szIconFontName = "Sans";
};
inline std::unique_ptr<CHyprNotificationOverlay> g_pHyprNotificationOverlay;

View File

@ -2,6 +2,7 @@
#include "../Compositor.hpp"
#include "../events/Events.hpp"
#include "../protocols/LayerShell.hpp"
#include "../managers/SeatManager.hpp"
PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
@ -132,15 +133,16 @@ void CLayerSurface::onMap() {
const bool GRABSFOCUS = layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained
(g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained());
(g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained());
if (GRABSFOCUS) {
// TODO: use the new superb really very cool grab
g_pSeatManager->setGrab(nullptr);
g_pInputManager->releaseAllMouseButtons();
g_pCompositor->focusSurface(surface.wlr());
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, surface.wlr(), LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
g_pSeatManager->setPointerFocus(surface.wlr(), LOCAL);
g_pInputManager->m_bEmptyFocusCursorSet = false;
}
@ -304,15 +306,14 @@ void CLayerSurface::onCommit() {
realSize.setValueAndWarp(geometry.size());
}
if (layerSurface->current.interactivity && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained
if (layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained
&& !keyboardExclusive && mapped) {
g_pCompositor->focusSurface(layerSurface->surface);
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, layerSurface->surface, LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, 0, LOCAL.x, LOCAL.y);
g_pSeatManager->setPointerFocus(layerSurface->surface, LOCAL);
g_pInputManager->m_bEmptyFocusCursorSet = false;
} else if (!layerSurface->current.interactivity && (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) {
} else if (!layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) {
g_pInputManager->refocus();
}

View File

@ -2,6 +2,7 @@
#include "../config/ConfigValue.hpp"
#include "../Compositor.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/XDGShell.hpp"
#include <ranges>
CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) {
@ -12,14 +13,13 @@ CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) {
initAllSignals();
}
CPopup::CPopup(wlr_xdg_popup* popup, CPopup* pOwner) : m_pParent(pOwner), m_pWLR(popup) {
m_pWLR->base->data = this;
m_sWLSurface.assign(popup->base->surface, this);
CPopup::CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner) : m_pParent(pOwner), m_pResource(popup) {
m_sWLSurface.assign(popup->surface->surface, this);
m_pLayerOwner = pOwner->m_pLayerOwner;
m_pWindowOwner = pOwner->m_pWindowOwner;
m_vLastSize = {m_pWLR->current.geometry.width, m_pWLR->current.geometry.height};
m_vLastSize = popup->surface->current.geometry.size();
unconstrain();
initAllSignals();
@ -27,71 +27,33 @@ CPopup::CPopup(wlr_xdg_popup* popup, CPopup* pOwner) : m_pParent(pOwner), m_pWLR
CPopup::~CPopup() {
m_sWLSurface.unassign();
if (m_pWLR)
m_pWLR->base->data = nullptr;
hyprListener_commitPopup.removeCallback();
hyprListener_repositionPopup.removeCallback();
hyprListener_mapPopup.removeCallback();
hyprListener_unmapPopup.removeCallback();
hyprListener_newPopup.removeCallback();
hyprListener_destroyPopup.removeCallback();
}
static void onNewPopup(void* owner, void* data) {
const auto POPUP = (CPopup*)owner;
POPUP->onNewPopup((wlr_xdg_popup*)data);
}
static void onMapPopup(void* owner, void* data) {
const auto POPUP = (CPopup*)owner;
POPUP->onMap();
}
static void onDestroyPopup(void* owner, void* data) {
const auto POPUP = (CPopup*)owner;
POPUP->onDestroy();
}
static void onUnmapPopup(void* owner, void* data) {
const auto POPUP = (CPopup*)owner;
POPUP->onUnmap();
}
static void onCommitPopup(void* owner, void* data) {
const auto POPUP = (CPopup*)owner;
POPUP->onCommit();
}
static void onRepositionPopup(void* owner, void* data) {
const auto POPUP = (CPopup*)owner;
POPUP->onReposition();
}
void CPopup::initAllSignals() {
if (!m_pWLR) {
if (!m_pResource) {
if (!m_pWindowOwner.expired())
hyprListener_newPopup.initCallback(&m_pWindowOwner->m_uSurface.xdg->events.new_popup, ::onNewPopup, this, "CPopup Head");
listeners.newPopup = m_pWindowOwner->m_pXDGSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
else if (!m_pLayerOwner.expired())
listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<wlr_xdg_popup*>(d)); });
listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
else
ASSERT(false);
return;
}
hyprListener_repositionPopup.initCallback(&m_pWLR->events.reposition, ::onRepositionPopup, this, "CPopup");
hyprListener_destroyPopup.initCallback(&m_pWLR->events.destroy, ::onDestroyPopup, this, "CPopup");
hyprListener_mapPopup.initCallback(&m_sWLSurface.wlr()->events.map, ::onMapPopup, this, "CPopup");
hyprListener_unmapPopup.initCallback(&m_sWLSurface.wlr()->events.unmap, ::onUnmapPopup, this, "CPopup");
hyprListener_commitPopup.initCallback(&m_sWLSurface.wlr()->events.commit, ::onCommitPopup, this, "CPopup");
hyprListener_newPopup.initCallback(&m_pWLR->base->events.new_popup, ::onNewPopup, this, "CPopup");
listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); });
listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); });
listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); });
listeners.dismissed = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); });
listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); });
listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); });
listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
}
void CPopup::onNewPopup(wlr_xdg_popup* popup) {
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
const auto POPUP = m_vChildren.emplace_back(std::make_unique<CPopup>(popup, this)).get();
Debug::log(LOG, "New popup at wlr {:x} and hl {:x}", (uintptr_t)popup, (uintptr_t)POPUP);
Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP);
}
void CPopup::onDestroy() {
@ -104,8 +66,9 @@ void CPopup::onDestroy() {
}
void CPopup::onMap() {
m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height};
const auto COORDS = coordsGlobal();
m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height};
const auto COORDS = coordsGlobal();
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);
CBox box;
wlr_surface_get_extends(m_sWLSurface.wlr(), box.pWlr());
@ -118,15 +81,18 @@ void CPopup::onMap() {
m_pSubsurfaceHead = std::make_unique<CSubsurface>(this);
unconstrain();
//unconstrain();
sendScale();
wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output);
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
}
void CPopup::onUnmap() {
m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height};
if (!m_pResource || !m_pResource->surface)
return;
m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height};
const auto COORDS = coordsGlobal();
CBox box;
@ -140,16 +106,27 @@ void CPopup::onUnmap() {
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
// damage all children
breadthfirst(
[this](CPopup* p, void* data) {
if (!p->m_pResource)
return;
auto box = CBox{p->coordsGlobal(), p->size()};
g_pHyprRenderer->damageBox(&box);
},
nullptr);
}
void CPopup::onCommit(bool ignoreSiblings) {
if (m_pWLR->base->initial_commit) {
wlr_xdg_surface_schedule_configure(m_pWLR->base);
if (m_pResource->surface->initialCommit) {
m_pResource->surface->scheduleConfigure();
return;
}
if (!m_pWindowOwner.expired() && (!m_pWindowOwner->m_bIsMapped || !m_pWindowOwner->m_pWorkspace->m_bVisible)) {
m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height};
m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height};
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
if (*PLOGDAMAGE)
@ -157,16 +134,17 @@ void CPopup::onCommit(bool ignoreSiblings) {
return;
}
if (!m_pWLR->base->surface->mapped)
if (!m_pResource->surface->mapped)
return;
const auto COORDS = coordsGlobal();
const auto COORDSLOCAL = coordsRelativeToParent();
if (m_vLastSize != Vector2D{m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height} || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) {
if (m_vLastSize != Vector2D{m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height} || m_bRequestedReposition ||
m_vLastPos != COORDSLOCAL) {
CBox box = {localToGlobal(m_vLastPos), m_vLastSize};
g_pHyprRenderer->damageBox(&box);
m_vLastSize = {m_pWLR->base->current.geometry.width, m_pWLR->base->current.geometry.height};
m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height};
box = {COORDS, m_vLastSize};
g_pHyprRenderer->damageBox(&box);
@ -202,20 +180,22 @@ void CPopup::unconstrain() {
return;
CBox box = {PMONITOR->vecPosition.x - COORDS.x, PMONITOR->vecPosition.y - COORDS.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
wlr_xdg_popup_unconstrain_from_box(m_pWLR, box.pWlr());
m_pResource->applyPositioning(box, COORDS - PMONITOR->vecPosition);
}
Vector2D CPopup::coordsRelativeToParent() {
Vector2D offset;
CPopup* current = this;
if (!m_pResource)
return {};
offset -= {m_pWLR->base->current.geometry.x, m_pWLR->base->current.geometry.y};
CPopup* current = this;
offset -= current->m_pResource->surface->current.geometry.pos();
while (current->m_pParent) {
while (current->m_pParent && current->m_pResource) {
offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy};
offset += {current->m_pWLR->current.geometry.x, current->m_pWLR->current.geometry.y};
offset += current->m_pResource->geometry.pos();
current = current->m_pParent;
}
@ -309,15 +289,21 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
breadthfirst([](CPopup* popup, void* data) { ((std::vector<CPopup*>*)data)->push_back(popup); }, &popups);
for (auto& p : popups | std::views::reverse) {
if (!p->m_pWLR)
if (!p->m_pResource)
continue;
if (!allowsInput) {
const auto BOX = CBox{p->coordsGlobal(), p->size()};
const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{};
const Vector2D size = p->m_pResource ? p->m_pResource->geometry.size() : p->size();
const auto BOX = CBox{p->coordsGlobal() + offset, size};
if (BOX.containsPoint(globalCoords))
return p;
} else {
const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input}.translate(p->coordsGlobal());
const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{};
const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input}
.intersect(CBox{{}, {p->m_sWLSurface.wlr()->current.width, p->m_sWLSurface.wlr()->current.height}})
.translate(p->coordsGlobal() + offset);
if (REGION.containsPoint(globalCoords))
return p;
}

View File

@ -5,6 +5,8 @@
#include "Subsurface.hpp"
#include "../helpers/signal/Listener.hpp"
class CXDGPopupResource;
class CPopup {
public:
// dummy head nodes
@ -12,7 +14,7 @@ class CPopup {
CPopup(PHLLS pOwner);
// real nodes
CPopup(wlr_xdg_popup* popup, CPopup* pOwner);
CPopup(SP<CXDGPopupResource> popup, CPopup* pOwner);
~CPopup();
@ -21,7 +23,7 @@ class CPopup {
Vector2D size();
void onNewPopup(wlr_xdg_popup* popup);
void onNewPopup(SP<CXDGPopupResource> popup);
void onDestroy();
void onMap();
void onUnmap();
@ -45,31 +47,29 @@ class CPopup {
PHLLSREF m_pLayerOwner;
// T2 owners
CPopup* m_pParent = nullptr;
CPopup* m_pParent = nullptr;
wlr_xdg_popup* m_pWLR = nullptr;
WP<CXDGPopupResource> m_pResource;
Vector2D m_vLastSize = {};
Vector2D m_vLastPos = {};
Vector2D m_vLastSize = {};
Vector2D m_vLastPos = {};
bool m_bRequestedReposition = false;
bool m_bRequestedReposition = false;
bool m_bInert = false;
bool m_bInert = false;
//
std::vector<std::unique_ptr<CPopup>> m_vChildren;
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
// signals
DYNLISTENER(newPopup);
DYNLISTENER(destroyPopup);
DYNLISTENER(mapPopup);
DYNLISTENER(unmapPopup);
DYNLISTENER(commitPopup);
DYNLISTENER(repositionPopup);
struct {
CHyprSignalListener newPopup;
CHyprSignalListener destroy;
CHyprSignalListener map;
CHyprSignalListener unmap;
CHyprSignalListener commit;
CHyprSignalListener dismissed;
CHyprSignalListener reposition;
} listeners;
void initAllSignals();

View File

@ -120,8 +120,6 @@ void CWLSurface::destroy() {
if (g_pCompositor && g_pCompositor->m_pLastFocus == m_pWLRSurface)
g_pCompositor->m_pLastFocus = nullptr;
if (g_pInputManager && g_pInputManager->m_pLastMouseSurface == m_pWLRSurface)
g_pInputManager->m_pLastMouseSurface = nullptr;
if (g_pHyprRenderer && g_pHyprRenderer->m_sLastCursorData.surf == this)
g_pHyprRenderer->m_sLastCursorData.surf.reset();

View File

@ -6,6 +6,7 @@
#include "../config/ConfigValue.hpp"
#include <any>
#include "../managers/TokenManager.hpp"
#include "../protocols/XDGShell.hpp"
PHLWINDOW CWindow::create() {
PHLWINDOW pWindow = SP<CWindow>(new CWindow);
@ -27,6 +28,39 @@ PHLWINDOW CWindow::create() {
return pWindow;
}
PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) {
PHLWINDOW pWindow = SP<CWindow>(new CWindow(resource));
pWindow->m_pSelf = pWindow;
resource->toplevel->window = pWindow;
pWindow->m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_fBorderFadeAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER);
pWindow->m_fBorderAngleAnimationProgress.create(g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER);
pWindow->m_fAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW);
pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(pWindow));
pWindow->addWindowDeco(std::make_unique<CHyprBorderDecoration>(pWindow));
pWindow->m_pWLSurface.assign(pWindow->m_pXDGSurface->surface, pWindow);
return pWindow;
}
CWindow::CWindow(SP<CXDGSurfaceResource> resource) : m_pXDGSurface(resource) {
listeners.map = m_pXDGSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); });
listeners.ack = m_pXDGSurface->events.ack.registerListener([this](std::any d) { onAck(std::any_cast<uint32_t>(d)); });
listeners.unmap = m_pXDGSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); });
listeners.destroy = m_pXDGSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); });
listeners.commit = m_pXDGSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); });
listeners.updateState = m_pXDGSurface->toplevel->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); });
listeners.updateMetadata = m_pXDGSurface->toplevel->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); });
}
CWindow::CWindow() {
;
}
@ -53,9 +87,9 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
const int BORDERSIZE = getRealBorderSize();
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {{m_vRealPosition.value().x - PMONITOR->vecPosition.x, m_vRealPosition.value().y - PMONITOR->vecPosition.y},
{PMONITOR->vecSize.x - (m_vRealPosition.value().x - PMONITOR->vecPosition.x), PMONITOR->vecSize.y - (m_vRealPosition.value().y - PMONITOR->vecPosition.y)}};
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR)
return {{m_vRealPosition.value().x - PMONITOR->vecPosition.x, m_vRealPosition.value().y - PMONITOR->vecPosition.y},
{PMONITOR->vecSize.x - (m_vRealPosition.value().x - PMONITOR->vecPosition.x), PMONITOR->vecSize.y - (m_vRealPosition.value().y - PMONITOR->vecPosition.y)}};
}
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
@ -74,21 +108,24 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
if (m_pWLSurface.exists() && !m_bIsX11) {
if (m_pWLSurface.exists() && !m_bIsX11 && m_pPopupHead) {
CBox surfaceExtents = {0, 0, 0, 0};
// TODO: this could be better, perhaps make a getFullWindowRegion?
wlr_xdg_surface_for_each_popup_surface(
m_uSurface.xdg,
[](wlr_surface* surf, int sx, int sy, void* data) {
m_pPopupHead->breadthfirst(
[](CPopup* popup, void* data) {
if (!popup->m_sWLSurface.wlr())
return;
CBox* pSurfaceExtents = (CBox*)data;
if (sx < pSurfaceExtents->x)
pSurfaceExtents->x = sx;
if (sy < pSurfaceExtents->y)
pSurfaceExtents->y = sy;
if (sx + surf->current.width > pSurfaceExtents->width)
pSurfaceExtents->width = sx + surf->current.width - pSurfaceExtents->x;
if (sy + surf->current.height > pSurfaceExtents->height)
pSurfaceExtents->height = sy + surf->current.height - pSurfaceExtents->y;
CBox surf = CBox{popup->coordsRelativeToParent(), popup->size()};
if (surf.x < pSurfaceExtents->x)
pSurfaceExtents->x = surf.x;
if (surf.y < pSurfaceExtents->y)
pSurfaceExtents->y = surf.y;
if (surf.x + surf.w > pSurfaceExtents->width)
pSurfaceExtents->width = surf.x + surf.w - pSurfaceExtents->x;
if (surf.y + surf.h > pSurfaceExtents->height)
pSurfaceExtents->height = surf.y + surf.h - pSurfaceExtents->y;
},
&surfaceExtents);
@ -110,8 +147,8 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
CBox CWindow::getFullWindowBoundingBox() {
if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR)
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
}
auto maxExtents = getFullWindowExtents();
@ -258,10 +295,10 @@ bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoor
pid_t CWindow::getPID() {
pid_t PID = -1;
if (!m_bIsX11) {
if (!m_uSurface.xdg)
if (!m_pXDGSurface || !m_pXDGSurface->owner /* happens at unmap */)
return -1;
wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr);
wl_client_get_credentials(m_pXDGSurface->owner->client(), &PID, nullptr, nullptr);
} else {
if (!m_uSurface.xwayland)
return -1;
@ -511,8 +548,8 @@ void CWindow::onMap() {
g_pCompositor->m_vWindowFocusHistory.push_back(m_pSelf);
hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this,
"CWindow");
if (m_bIsX11)
hyprListener_unmapWindow.initCallback(&m_uSurface.xwayland->surface->events.unmap, &Events::listener_unmapWindow, this, "CWindow");
m_vReportedSize = m_vPendingReportedSize;
m_bAnimatingIn = true;
@ -793,26 +830,14 @@ bool CWindow::isInCurvedCorner(double x, double y) {
return false;
}
void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) {
const auto DATA = (SExtensionFindingData*)data;
CBox box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height};
if (box.containsPoint(DATA->vec))
*DATA->found = surface;
}
// checks if the wayland window has a popup at pos
bool CWindow::hasPopupAt(const Vector2D& pos) {
if (m_bIsX11)
return false;
wlr_surface* resultSurf = nullptr;
Vector2D origin = m_vRealPosition.value();
SExtensionFindingData data = {origin, pos, &resultSurf};
wlr_xdg_surface_for_each_popup_surface(m_uSurface.xdg, findExtensionForVector2D, &data);
CPopup* popup = m_pPopupHead->at(pos);
return resultSurf;
return popup && popup->m_sWLSurface.wlr();
}
void CWindow::applyGroupRules() {
@ -1096,11 +1121,11 @@ bool CWindow::opaque() {
if (m_bIsX11)
return !m_uSurface.xwayland->has_alpha;
if (m_uSurface.xdg->surface->opaque)
if (m_pXDGSurface->surface->opaque)
return true;
const auto EXTENTS = pixman_region32_extents(&m_uSurface.xdg->surface->opaque_region);
if (EXTENTS->x2 - EXTENTS->x1 >= m_uSurface.xdg->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_uSurface.xdg->surface->current.buffer_height)
const auto EXTENTS = pixman_region32_extents(&m_pXDGSurface->surface->opaque_region);
if (EXTENTS->x2 - EXTENTS->x1 >= m_pXDGSurface->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_pXDGSurface->surface->current.buffer_height)
return true;
return false;
@ -1162,10 +1187,10 @@ void CWindow::setSuspended(bool suspend) {
if (suspend == m_bSuspended)
return;
if (m_bIsX11)
if (m_bIsX11 || !m_pXDGSurface->toplevel)
return;
wlr_xdg_toplevel_set_suspended(m_uSurface.xdg->toplevel, suspend);
m_pXDGSurface->toplevel->setSuspeneded(suspend);
m_bSuspended = suspend;
}
@ -1221,12 +1246,21 @@ void CWindow::onWorkspaceAnimUpdate() {
}
int CWindow::popupsCount() {
if (m_bIsX11)
return 0;
int no = -1;
m_pPopupHead->breadthfirst([](CPopup* p, void* d) { *((int*)d) += 1; }, &no);
return no;
}
int CWindow::surfacesCount() {
if (m_bIsX11)
return 1;
int no = 0;
wlr_xdg_surface_for_each_popup_surface(
m_uSurface.xdg, [](wlr_surface* s, int x, int y, void* data) { *(int*)data += 1; }, &no);
wlr_surface_for_each_surface(
m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { *((int*)data) += 1; }, &no);
return no;
}
@ -1278,6 +1312,9 @@ std::unordered_map<std::string, std::string> CWindow::getEnv() {
}
void CWindow::activate(bool force) {
if (g_pCompositor->m_pLastWindow == m_pSelf)
return;
static auto PFOCUSONACTIVATE = CConfigValue<Hyprlang::INT>("misc:focus_on_activate");
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)this)});
@ -1295,3 +1332,98 @@ void CWindow::activate(bool force) {
g_pCompositor->focusWindow(m_pSelf.lock());
g_pCompositor->warpCursorTo(middle());
}
void CWindow::onUpdateState() {
if (!m_pXDGSurface)
return;
if (m_pXDGSurface->toplevel->state.requestsFullscreen && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) {
bool fs = m_pXDGSurface->toplevel->state.requestsFullscreen.value();
if (fs != m_bIsFullscreen && m_pXDGSurface->mapped)
g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_FULL);
if (!m_pXDGSurface->mapped)
m_bWantsInitialFullscreen = fs;
}
if (m_pXDGSurface->toplevel->state.requestsMaximize && !(m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) {
bool fs = m_pXDGSurface->toplevel->state.requestsMaximize.value();
if (fs != m_bIsFullscreen && m_pXDGSurface->mapped)
g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_MAXIMIZED);
}
}
void CWindow::onUpdateMeta() {
const auto NEWTITLE = fetchTitle();
if (m_szTitle != NEWTITLE) {
m_szTitle = NEWTITLE;
g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)this)});
EMIT_HOOK_EVENT("windowTitle", m_pSelf.lock());
if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)});
EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock());
}
updateDynamicRules();
g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock());
updateToplevel();
Debug::log(LOG, "Window {:x} set title to {}", (uintptr_t)this, m_szTitle);
}
const auto NEWCLASS = fetchClass();
if (m_szClass != NEWCLASS) {
m_szClass = NEWCLASS;
if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)});
EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock());
}
updateDynamicRules();
g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock());
updateToplevel();
Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass);
}
}
std::string CWindow::fetchTitle() {
if (!m_bIsX11) {
if (m_pXDGSurface && m_pXDGSurface->toplevel)
return m_pXDGSurface->toplevel->state.title;
} else {
if (m_uSurface.xwayland && m_uSurface.xwayland->title)
return m_uSurface.xwayland->title;
}
return "";
}
std::string CWindow::fetchClass() {
if (!m_bIsX11) {
if (m_pXDGSurface && m_pXDGSurface->toplevel)
return m_pXDGSurface->toplevel->state.appid;
} else {
if (m_uSurface.xwayland && m_uSurface.xwayland->_class)
return m_uSurface.xwayland->_class;
}
return "";
}
void CWindow::onAck(uint32_t serial) {
const auto SERIAL = std::find_if(m_vPendingSizeAcks.rbegin(), m_vPendingSizeAcks.rend(), [serial](const auto& e) { return e.first == serial; });
if (SERIAL == m_vPendingSizeAcks.rend())
return;
m_pPendingSizeAck = *SERIAL;
std::erase_if(m_vPendingSizeAcks, [&](const auto& el) { return el.first <= SERIAL->first; });
}

View File

@ -14,6 +14,8 @@
#include "DesktopTypes.hpp"
#include "../helpers/signal/Signal.hpp"
class CXDGSurfaceResource;
enum eIdleInhibitMode {
IDLEINHIBIT_NONE = 0,
IDLEINHIBIT_ALWAYS,
@ -196,9 +198,12 @@ struct SInitialWorkspaceToken {
class CWindow {
public:
static PHLWINDOW create(SP<CXDGSurfaceResource>);
// xwl
static PHLWINDOW create();
private:
CWindow(SP<CXDGSurfaceResource> resource);
CWindow();
public:
@ -233,9 +238,9 @@ class CWindow {
} events;
union {
wlr_xdg_surface* xdg;
wlr_xwayland_surface* xwayland;
} m_uSurface;
WP<CXDGSurfaceResource> m_pXDGSurface;
// this is the position and size of the "bounding box"
Vector2D m_vPosition = Vector2D(0, 0);
@ -261,7 +266,7 @@ class CWindow {
// this is used for pseudotiling
bool m_bIsPseudotiled = false;
Vector2D m_vPseudoSize = Vector2D(0, 0);
Vector2D m_vPseudoSize = Vector2D(1280, 720);
bool m_bFirstMap = false; // for layouts
bool m_bIsFloating = false;
@ -271,6 +276,7 @@ class CWindow {
bool m_bWasMaximized = false;
uint64_t m_iMonitorID = -1;
std::string m_szTitle = "";
std::string m_szClass = "";
std::string m_szInitialTitle = "";
std::string m_szInitialClass = "";
PHLWORKSPACE m_pWorkspace;
@ -385,7 +391,7 @@ class CWindow {
// For the list lookup
bool operator==(const CWindow& rhs) {
return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
return m_pXDGSurface == rhs.m_pXDGSurface && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
m_bFadingOut == rhs.m_bFadingOut;
}
@ -424,6 +430,7 @@ class CWindow {
int workspaceID();
bool onSpecialWorkspace();
void activate(bool force = false);
int surfacesCount();
int getRealBorderSize();
void updateSpecialRenderData();
@ -450,6 +457,13 @@ class CWindow {
void switchWithWindowInGroup(PHLWINDOW pWindow);
void setAnimationsToMove();
void onWorkspaceAnimUpdate();
void onUpdateState();
void onUpdateMeta();
std::string fetchTitle();
std::string fetchClass();
// listeners
void onAck(uint32_t serial);
//
std::unordered_map<std::string, std::string> getEnv();
@ -457,6 +471,17 @@ class CWindow {
//
PHLWINDOWREF m_pSelf;
// make private once we move listeners to inside CWindow
struct {
CHyprSignalListener map;
CHyprSignalListener ack;
CHyprSignalListener unmap;
CHyprSignalListener commit;
CHyprSignalListener destroy;
CHyprSignalListener updateState;
CHyprSignalListener updateMetadata;
} listeners;
private:
// For hidden windows and stuff
bool m_bHidden = false;
@ -520,7 +545,7 @@ struct std::formatter<PHLWINDOW, CharT> : std::formatter<CharT> {
if (formatMonitor)
std::format_to(out, ", monitor: {}", w->m_iMonitorID);
if (formatClass)
std::format_to(out, ", class: {}", g_pXWaylandManager->getAppIDClass(w));
std::format_to(out, ", class: {}", w->m_szClass);
return std::format_to(out, "]");
}
};

View File

@ -2,17 +2,18 @@
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special);
PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special, bool isEmtpy) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special, isEmtpy);
workspace->init(workspace);
return workspace;
}
CWorkspace::CWorkspace(int id, int monitorID, std::string name, bool special) {
CWorkspace::CWorkspace(int id, int monitorID, std::string name, bool special, bool isEmtpy) {
m_iMonitorID = monitorID;
m_iID = id;
m_szName = name;
m_bIsSpecialWorkspace = special;
m_bWasCreatedEmtpy = isEmtpy;
}
void CWorkspace::init(PHLWORKSPACE self) {
@ -44,8 +45,9 @@ void CWorkspace::init(PHLWORKSPACE self) {
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
m_bPersistent = WORKSPACERULE.isPersistent;
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
g_pKeybindManager->spawn(*cmd);
if (self->m_bWasCreatedEmtpy)
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
g_pKeybindManager->spawn(*cmd);
g_pEventManager->postEvent({"createworkspace", m_szName});
g_pEventManager->postEvent({"createworkspacev2", std::format("{},{}", m_iID, m_szName)});

View File

@ -15,9 +15,9 @@ class CWindow;
class CWorkspace {
public:
static PHLWORKSPACE create(int id, int monitorID, std::string name, bool special = false);
static PHLWORKSPACE create(int id, int monitorID, std::string name, bool special = false, bool isEmtpy = true);
// use create() don't use this
CWorkspace(int id, int monitorID, std::string name, bool special = false);
CWorkspace(int id, int monitorID, std::string name, bool special = false, bool isEmpty = true);
~CWorkspace();
// Workspaces ID-based have IDs > 0
@ -58,6 +58,8 @@ class CWorkspace {
// last monitor (used on reconnect)
std::string m_szLastMonitor = "";
bool m_bWasCreatedEmtpy = true;
bool m_bPersistent = false;
// Inert: destroyed and invalid. If this is true, release the ptr you have.

View File

@ -131,8 +131,17 @@ void IKeyboard::updateLEDs() {
leds |= (1 << i);
}
updateLEDs(leds);
}
void IKeyboard::updateLEDs(uint32_t leds) {
auto keyboard = wlr();
if (!keyboard || keyboard->xkb_state == nullptr)
return;
if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock()))
return;
wlr_keyboard_led_update(wlr(), leds);
wlr_keyboard_led_update(keyboard, leds);
}

View File

@ -42,6 +42,7 @@ class IKeyboard : public IHID {
void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
std::string getActiveLayout();
void updateLEDs();
void updateLEDs(uint32_t leds);
bool active = false;
bool enabled = true;

View File

@ -15,12 +15,6 @@
// //
// ---------------------------------------------------- //
void Events::listener_requestMouse(wl_listener* listener, void* data) {
const auto EVENT = (wlr_seat_pointer_request_set_cursor_event*)data;
g_pInputManager->processMouseRequest(EVENT);
}
void Events::listener_newInput(wl_listener* listener, void* data) {
const auto DEVICE = (wlr_input_device*)data;

View File

@ -18,9 +18,6 @@ namespace Events {
// Layer events
LISTENER(newLayerSurface);
// Surface XDG (window)
LISTENER(newXDGToplevel);
// Window events
DYNLISTENFUNC(commitWindow);
DYNLISTENFUNC(mapWindow);
@ -46,7 +43,6 @@ namespace Events {
LISTENER(newVirtPtr);
// Various
LISTENER(requestMouse);
LISTENER(requestSetSel);
LISTENER(requestSetPrimarySel);
@ -63,16 +59,6 @@ namespace Events {
LISTENER(readyXWayland);
LISTENER(surfaceXWayland);
// Drag & Drop
LISTENER(requestDrag);
LISTENER(startDrag);
DYNLISTENFUNC(destroyDrag);
DYNLISTENFUNC(mapDragIcon);
DYNLISTENFUNC(unmapDragIcon);
DYNLISTENFUNC(destroyDragIcon);
DYNLISTENFUNC(commitDragIcon);
// Renderer destroy
LISTENER(RendererDestroy);

View File

@ -25,16 +25,6 @@ void Events::listener_leaseRequest(wl_listener* listener, void* data) {
}
}
void Events::listener_requestSetPrimarySel(wl_listener* listener, void* data) {
const auto EVENT = (wlr_seat_request_set_primary_selection_event*)data;
wlr_seat_set_primary_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial);
}
void Events::listener_requestSetSel(wl_listener* listener, void* data) {
const auto EVENT = (wlr_seat_request_set_selection_event*)data;
wlr_seat_set_selection(g_pCompositor->m_sSeat.seat, EVENT->source, EVENT->serial);
}
void Events::listener_readyXWayland(wl_listener* listener, void* data) {
#ifndef NO_XWAYLAND
const auto XCBCONNECTION = xcb_connect(g_pXWaylandManager->m_sWLRXWayland->display_name, NULL);
@ -58,7 +48,7 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) {
free(reply);
}
wlr_xwayland_set_seat(g_pXWaylandManager->m_sWLRXWayland, g_pCompositor->m_sSeat.seat);
//wlr_xwayland_set_seat(g_pXWaylandManager->m_sWLRXWayland, g_pCompositor->m_sSeat.seat);
g_pCursorManager->setXWaylandCursor(g_pXWaylandManager->m_sWLRXWayland);
@ -79,88 +69,6 @@ void Events::listener_readyXWayland(wl_listener* listener, void* data) {
#endif
}
void Events::listener_requestDrag(wl_listener* listener, void* data) {
const auto E = (wlr_seat_request_start_drag_event*)data;
if (!wlr_seat_validate_pointer_grab_serial(g_pCompositor->m_sSeat.seat, E->origin, E->serial)) {
Debug::log(LOG, "Ignoring drag and drop request: serial mismatch.");
wlr_data_source_destroy(E->drag->source);
return;
}
wlr_seat_start_pointer_drag(g_pCompositor->m_sSeat.seat, E->drag, E->serial);
}
void Events::listener_startDrag(wl_listener* listener, void* data) {
if (g_pInputManager->m_sDrag.drag)
return; // don't handle multiple drags
g_pInputManager->m_sDrag.drag = (wlr_drag*)data;
wlr_drag* wlrDrag = (wlr_drag*)data;
Debug::log(LOG, "Started drag {:x}", (uintptr_t)wlrDrag);
wlrDrag->data = data;
g_pInputManager->m_sDrag.hyprListener_destroy.initCallback(&wlrDrag->events.destroy, &Events::listener_destroyDrag, &g_pInputManager->m_sDrag, "Drag");
if (wlrDrag->icon) {
Debug::log(LOG, "Drag started with an icon {:x}", (uintptr_t)wlrDrag->icon);
g_pInputManager->m_sDrag.dragIcon = wlrDrag->icon;
wlrDrag->icon->data = g_pInputManager->m_sDrag.dragIcon;
g_pInputManager->m_sDrag.hyprListener_mapIcon.initCallback(&wlrDrag->icon->surface->events.map, &Events::listener_mapDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_unmapIcon.initCallback(&wlrDrag->icon->surface->events.unmap, &Events::listener_unmapDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_destroyIcon.initCallback(&wlrDrag->icon->events.destroy, &Events::listener_destroyDragIcon, &g_pInputManager->m_sDrag, "DragIcon");
g_pInputManager->m_sDrag.hyprListener_commitIcon.initCallback(&wlrDrag->icon->surface->events.commit, &Events::listener_commitDragIcon, &g_pInputManager->m_sDrag,
"DragIcon");
}
}
void Events::listener_destroyDrag(void* owner, void* data) {
Debug::log(LOG, "Drag destroyed.");
if (g_pInputManager->m_sDrag.drag && g_pInputManager->m_sDrag.dragIcon && g_pInputManager->m_sDrag.dragIcon->surface)
g_pHyprRenderer->damageBox(g_pInputManager->m_sDrag.pos.x - 2, g_pInputManager->m_sDrag.pos.y - 2, g_pInputManager->m_sDrag.dragIcon->surface->current.width + 4,
g_pInputManager->m_sDrag.dragIcon->surface->current.height + 4);
g_pInputManager->m_sDrag.drag = nullptr;
g_pInputManager->m_sDrag.dragIcon = nullptr;
g_pInputManager->m_sDrag.hyprListener_destroy.removeCallback();
g_pCompositor->focusWindow(g_pCompositor->m_pLastWindow.lock(),
g_pCompositor->m_pLastWindow.lock() ? g_pXWaylandManager->getWindowSurface(g_pCompositor->m_pLastWindow.lock()) : nullptr);
}
void Events::listener_mapDragIcon(void* owner, void* data) {
Debug::log(LOG, "Drag icon mapped.");
g_pInputManager->m_sDrag.iconMapped = true;
}
void Events::listener_unmapDragIcon(void* owner, void* data) {
Debug::log(LOG, "Drag icon unmapped.");
g_pInputManager->m_sDrag.iconMapped = false;
}
void Events::listener_destroyDragIcon(void* owner, void* data) {
Debug::log(LOG, "Drag icon destroyed.");
g_pInputManager->m_sDrag.dragIcon = nullptr;
g_pInputManager->m_sDrag.hyprListener_commitIcon.removeCallback();
g_pInputManager->m_sDrag.hyprListener_destroyIcon.removeCallback();
g_pInputManager->m_sDrag.hyprListener_mapIcon.removeCallback();
g_pInputManager->m_sDrag.hyprListener_unmapIcon.removeCallback();
}
void Events::listener_commitDragIcon(void* owner, void* data) {
g_pInputManager->updateDragIcon();
Debug::log(LOG, "Drag icon committed.");
}
void Events::listener_RendererDestroy(wl_listener* listener, void* data) {
Debug::log(LOG, "!!Renderer destroyed!!");
}

View File

@ -4,9 +4,11 @@
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/TokenManager.hpp"
#include "../managers/SeatManager.hpp"
#include "../render/Renderer.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/XDGShell.hpp"
// ------------------------------------------------------------ //
// __ _______ _ _ _____ ______ _______ //
@ -24,11 +26,10 @@ void addViewCoords(void* pWindow, int* x, int* y) {
*y += PWINDOW->m_vRealPosition.goal().y;
if (!PWINDOW->m_bIsX11 && PWINDOW->m_bIsMapped) {
wlr_box geom;
wlr_xdg_surface_get_geometry(PWINDOW->m_uSurface.xdg, &geom);
Vector2D pos = PWINDOW->m_pXDGSurface->current.geometry.pos();
*x -= geom.x;
*y -= geom.y;
*x -= pos.x;
*y -= pos.y;
}
}
@ -66,11 +67,11 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bIsMapped = true;
PWINDOW->m_bReadyToDelete = false;
PWINDOW->m_bFadingOut = false;
PWINDOW->m_szTitle = g_pXWaylandManager->getTitle(PWINDOW);
PWINDOW->m_szTitle = PWINDOW->fetchTitle();
PWINDOW->m_iX11Type = PWINDOW->m_bIsX11 ? (PWINDOW->m_uSurface.xwayland->override_redirect ? 2 : 1) : 1;
PWINDOW->m_bFirstMap = true;
PWINDOW->m_szInitialTitle = PWINDOW->m_szTitle;
PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW);
PWINDOW->m_szInitialClass = PWINDOW->fetchClass();
// check for token
std::string requestedWorkspace = "";
@ -110,9 +111,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (g_pInputManager->m_bLastFocusOnLS) // waybar fix
g_pInputManager->releaseAllMouseButtons();
// Set all windows tiled regardless of anything
g_pXWaylandManager->setWindowStyleTiled(PWINDOW, WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | WLR_EDGE_BOTTOM);
// checks if the window wants borders and sets the appropriate flag
g_pXWaylandManager->checkBorders(PWINDOW);
@ -145,10 +143,8 @@ 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_uSurface.xdg->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && PWINDOW->m_uSurface.xdg->toplevel->requested.fullscreen) ||
(PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen);
PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false);
bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen);
bool requestsFakeFullscreen = false;
bool requestsMaximize = false;
bool overridingNoFullscreen = false;
@ -507,19 +503,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_fDimPercent.setValueAndWarp(0);
}
if (!PWINDOW->m_bIsX11) {
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW.get(), "XDG Window Late");
PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_minimize, &Events::listener_requestMinimize, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_requestMove.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_move, &Events::listener_requestMove, PWINDOW.get(), "XDG Window Late");
PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(),
"XDG Window Late");
PWINDOW->hyprListener_ackConfigure.initCallback(&PWINDOW->m_uSurface.xdg->events.ack_configure, &Events::listener_ackConfigure, PWINDOW.get(), "XDG Window Late");
} else {
if (PWINDOW->m_bIsX11) {
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(),
"XWayland Window Late");
PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW.get(),
@ -572,7 +556,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (*PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) {
// don't swallow ourselves
std::regex rgx(*PSWALLOWREGEX);
if (!std::regex_match(g_pXWaylandManager->getAppIDClass(PWINDOW), rgx)) {
if (!std::regex_match(PWINDOW->m_szClass, rgx)) {
// check parent
int ppid = getPPIDof(PWINDOW->getPID());
@ -614,12 +598,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
if (finalFound) {
bool valid = std::regex_match(g_pXWaylandManager->getAppIDClass(finalFound), rgx);
bool valid = std::regex_match(PWINDOW->m_szClass, rgx);
if (std::string{*PSWALLOWEXREGEX} != STRVAL_EMPTY) {
std::regex exc(*PSWALLOWEXREGEX);
valid = valid && !std::regex_match(g_pXWaylandManager->getTitle(finalFound), exc);
valid = valid && !std::regex_match(PWINDOW->m_szTitle, exc);
}
// check if it's the window we want & not exempt from getting swallowed
@ -643,7 +627,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->szName, PWINDOW->m_vRealPosition.goal(), PWINDOW->m_vRealSize.goal());
auto workspaceID = requestedWorkspace != "" ? requestedWorkspace : PWORKSPACE->m_szName;
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, g_pXWaylandManager->getAppIDClass(PWINDOW), PWINDOW->m_szTitle)});
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, PWINDOW->m_szClass, PWINDOW->m_szTitle)});
EMIT_HOOK_EVENT("openWindow", PWINDOW);
// apply data from default decos. Borders, shadows.
@ -668,7 +652,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform);
if (g_pCompositor->m_sSeat.mouse.expired() || !g_pInputManager->isConstrained())
if (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained())
g_pInputManager->sendMotionEventsToFocused();
// fix some xwayland apps that don't behave nicely
@ -704,15 +688,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW);
if (!PWINDOW->m_bIsX11) {
Debug::log(LOG, "Unregistered late callbacks XDG");
PWINDOW->hyprListener_setTitleWindow.removeCallback();
PWINDOW->hyprListener_requestMaximize.removeCallback();
PWINDOW->hyprListener_requestMinimize.removeCallback();
PWINDOW->hyprListener_requestMove.removeCallback();
PWINDOW->hyprListener_requestResize.removeCallback();
PWINDOW->hyprListener_fullscreenWindow.removeCallback();
PWINDOW->hyprListener_ackConfigure.removeCallback();
} else {
Debug::log(LOG, "Unregistered late callbacks XWL");
PWINDOW->hyprListener_fullscreenWindow.removeCallback();
PWINDOW->hyprListener_activateX11.removeCallback();
@ -803,29 +778,15 @@ void Events::listener_unmapWindow(void* owner, void* data) {
PWINDOW->onUnmap();
}
void Events::listener_ackConfigure(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
const auto E = (wlr_xdg_surface_configure*)data;
// find last matching serial
const auto SERIAL = std::find_if(PWINDOW->m_vPendingSizeAcks.rbegin(), PWINDOW->m_vPendingSizeAcks.rend(), [&](const auto& e) { return e.first == E->serial; });
if (SERIAL == PWINDOW->m_vPendingSizeAcks.rend())
return;
PWINDOW->m_pPendingSizeAck = *SERIAL;
std::erase_if(PWINDOW->m_vPendingSizeAcks, [&](const auto& el) { return el.first == SERIAL->first; });
}
void Events::listener_commitWindow(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
if (!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->initial_commit) {
if (!PWINDOW->m_bIsX11 && PWINDOW->m_pXDGSurface->initialCommit) {
Vector2D predSize = g_pLayoutManager->getCurrentLayout()->predictSizeForNewWindow(PWINDOW);
Debug::log(LOG, "Layout predicts size {} for {}", predSize, PWINDOW);
wlr_xdg_toplevel_set_size(PWINDOW->m_uSurface.xdg->toplevel, predSize.x, predSize.y);
PWINDOW->m_pXDGSurface->toplevel->setSize(predSize);
return;
}
@ -840,8 +801,8 @@ void Events::listener_commitWindow(void* owner, void* data) {
}
if (!PWINDOW->m_bIsX11 && !PWINDOW->m_bIsFullscreen && PWINDOW->m_bIsFloating) {
const auto MINSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.min_width, PWINDOW->m_uSurface.xdg->toplevel->current.min_height};
const auto MAXSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height};
const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize;
const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize;
if (MAXSIZE > Vector2D{1, 1}) {
const auto REALSIZE = PWINDOW->m_vRealSize.goal();
@ -860,7 +821,7 @@ void Events::listener_commitWindow(void* owner, void* data) {
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goal() + DELTA / 2.0;
PWINDOW->m_vRealSize = newSize;
g_pXWaylandManager->setWindowSize(PWINDOW, newSize, true);
g_pXWaylandManager->setWindowSize(PWINDOW, newSize);
g_pHyprRenderer->damageWindow(PWINDOW);
}
}
@ -917,11 +878,13 @@ void Events::listener_destroyWindow(void* owner, void* data) {
PWINDOW->hyprListener_associateX11.removeCallback();
PWINDOW->hyprListener_dissociateX11.removeCallback();
PWINDOW->listeners = {};
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);
PWINDOW->m_bReadyToDelete = true;
PWINDOW->m_uSurface.xdg = nullptr;
PWINDOW->m_pXDGSurface.reset();
if (!PWINDOW->m_bFadingOut) {
Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW);
@ -935,31 +898,14 @@ void Events::listener_setTitleWindow(void* owner, void* data) {
if (!validMapped(PWINDOW))
return;
const auto NEWTITLE = g_pXWaylandManager->getTitle(PWINDOW);
if (NEWTITLE == PWINDOW->m_szTitle)
return;
PWINDOW->m_szTitle = NEWTITLE;
g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)PWINDOW.get())});
EMIT_HOOK_EVENT("windowTitle", PWINDOW);
if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { // if it's the active, let's post an event to update others
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(PWINDOW) + "," + PWINDOW->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)PWINDOW.get())});
EMIT_HOOK_EVENT("activeWindow", PWINDOW);
}
PWINDOW->updateDynamicRules();
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
PWINDOW->updateToplevel();
Debug::log(LOG, "Window {:x} set title to {}", PWINDOW, PWINDOW->m_szTitle);
PWINDOW->onUpdateMeta();
}
void Events::listener_fullscreenWindow(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
// x11 only
if (!PWINDOW->m_bIsMapped) {
PWINDOW->m_bWantsInitialFullscreen = true;
return;
@ -970,41 +916,13 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
bool requestedFullState = false;
if (!PWINDOW->m_bIsX11) {
const auto REQUESTED = &PWINDOW->m_uSurface.xdg->toplevel->requested;
if (!PWINDOW->m_uSurface.xwayland->surface->mapped)
return;
if (REQUESTED->fullscreen && PWINDOW->m_bIsFullscreen) {
const auto PWORKSPACE = PWINDOW->m_pWorkspace;
if (PWORKSPACE->m_efFullscreenMode != FULLSCREEN_FULL) {
// Store that we were maximized
PWINDOW->m_bWasMaximized = true;
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_MAXIMIZED);
g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_FULL);
} else
PWINDOW->m_bWasMaximized = false;
} else if (REQUESTED->fullscreen != PWINDOW->m_bIsFullscreen && !PWINDOW->m_bFakeFullscreenState) {
g_pCompositor->setWindowFullscreen(PWINDOW, REQUESTED->fullscreen, FULLSCREEN_FULL);
if (PWINDOW->m_bWasMaximized && !REQUESTED->fullscreen) {
// Was maximized before the fullscreen request, return now back to maximized instead of normal
g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_MAXIMIZED);
}
}
if (!PWINDOW->m_bFakeFullscreenState)
g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL);
// Disable the maximize flag when we receive a de-fullscreen request
PWINDOW->m_bWasMaximized &= REQUESTED->fullscreen;
requestedFullState = REQUESTED->fullscreen;
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
} else {
if (!PWINDOW->m_uSurface.xwayland->surface->mapped)
return;
if (!PWINDOW->m_bFakeFullscreenState)
g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL);
requestedFullState = PWINDOW->m_uSurface.xwayland->fullscreen;
}
requestedFullState = PWINDOW->m_uSurface.xwayland->fullscreen;
if (!requestedFullState && PWINDOW->m_bFakeFullscreenState) {
g_pXWaylandManager->setWindowFullscreen(PWINDOW, false); // fixes for apps expecting a de-fullscreen (e.g. ff)
@ -1144,9 +1062,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) {
const Vector2D DELTA = PWINDOW->m_vRealSize.goal() - PWINDOW->m_vRealSize.goal() / PMONITOR->scale;
PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goal() / PMONITOR->scale);
PWINDOW->m_vRealPosition.setValueAndWarp(PWINDOW->m_vRealPosition.goal() + DELTA / 2.0);
}
}
@ -1178,7 +1094,7 @@ void Events::listener_associateX11(void* owner, void* data) {
PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW.get(), "XWayland Window");
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW.get(), "XWayland Window");
PWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PWINDOW), PWINDOW);
PWINDOW->m_pWLSurface.assign(PWINDOW->m_uSurface.xwayland->surface, PWINDOW);
}
void Events::listener_dissociateX11(void* owner, void* data) {
@ -1212,23 +1128,6 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW.get(), "XWayland Window");
}
void Events::listener_newXDGToplevel(wl_listener* listener, void* data) {
// A window got opened
const auto XDGTOPLEVEL = (wlr_xdg_toplevel*)data;
const auto XDGSURFACE = XDGTOPLEVEL->base;
Debug::log(LOG, "New XDG Toplevel created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null");
const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(CWindow::create());
PNEWWINDOW->m_uSurface.xdg = XDGSURFACE;
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW.get(), "XDG Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW.get(), "XDG Window");
PNEWWINDOW->hyprListener_commitWindow.initCallback(&XDGSURFACE->surface->events.commit, &Events::listener_commitWindow, PNEWWINDOW.get(), "XDG Window");
PNEWWINDOW->m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(PNEWWINDOW), PNEWWINDOW);
}
void Events::listener_requestMaximize(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
@ -1241,7 +1140,6 @@ void Events::listener_requestMaximize(void* owner, void* data) {
g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen,
FULLSCREEN_MAXIMIZED); // this will be rejected if there already is a fullscreen window
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
} else {
if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1)
return;
@ -1270,17 +1168,3 @@ void Events::listener_requestMinimize(void* owner, void* data) {
EMIT_HOOK_EVENT("minimize", (std::vector<std::any>{PWINDOW, (int64_t)(1)}));
}
}
void Events::listener_requestMove(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
// ignore
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
}
void Events::listener_requestResize(void* owner, void* data) {
PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock();
// ignore
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
}

View File

@ -284,16 +284,24 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
} else if (in.starts_with("empty")) {
const bool same_mon = in.substr(5).contains("m");
const bool next = in.substr(5).contains("n");
if (same_mon || next) {
if (!g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "Empty monitor workspace on monitor null!");
return WORKSPACE_INVALID;
if ((same_mon || next) && !g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "Empty monitor workspace on monitor null!");
return WORKSPACE_INVALID;
}
std::set<int> invalidWSes;
if (same_mon) {
for (auto& rule : g_pConfigManager->getAllWorkspaceRules()) {
const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor);
if (PMONITOR && (PMONITOR->ID != g_pCompositor->m_pLastMonitor->ID))
invalidWSes.insert(rule.workspaceId);
}
}
int id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0;
while (++id < INT_MAX) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id);
if (!PWORKSPACE || (g_pCompositor->getWindowsOnWorkspace(id) == 0 && (!same_mon || PWORKSPACE->m_iMonitorID == g_pCompositor->m_pLastMonitor->ID)))
if (!invalidWSes.contains(id) && (!PWORKSPACE || g_pCompositor->getWindowsOnWorkspace(id) == 0))
return id;
}
} else if (in.starts_with("prev")) {

View File

@ -7,6 +7,7 @@
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
namespace Systemd {
int SdBooted(void) {

View File

@ -58,33 +58,6 @@ struct SExtensionFindingData {
wlr_surface** found;
};
struct SSeat {
wlr_seat* seat = nullptr;
wl_client* exclusiveClient = nullptr;
WP<IPointer> mouse;
WP<IKeyboard> keyboard;
};
struct SDrag {
wlr_drag* drag = nullptr;
DYNLISTENER(destroy);
// Icon
bool iconMapped = false;
wlr_drag_icon* dragIcon = nullptr;
Vector2D pos;
DYNLISTENER(destroyIcon);
DYNLISTENER(mapIcon);
DYNLISTENER(unmapIcon);
DYNLISTENER(commitIcon);
};
struct SSwipeGesture {
PHLWORKSPACE pWorkspaceBegin = nullptr;

View File

@ -1,3 +1,4 @@
#include <pango/pangocairo.h>
#include "HyprError.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
@ -58,8 +59,11 @@ void CHyprError::createQueued() {
cairo_paint(CAIRO);
cairo_restore(CAIRO);
const auto LINECOUNT = Hyprlang::INT{1} + std::count(m_szQueued.begin(), m_szQueued.end(), '\n');
static auto LINELIMIT = CConfigValue<Hyprlang::INT>("debug:error_limit");
const auto LINECOUNT = Hyprlang::INT{1} + std::count(m_szQueued.begin(), m_szQueued.end(), '\n');
static auto LINELIMIT = CConfigValue<Hyprlang::INT>("debug:error_limit");
static auto BAR_POSITION = CConfigValue<Hyprlang::INT>("debug:error_position");
const bool TOPBAR = *BAR_POSITION == 0;
const auto VISLINECOUNT = std::min(LINECOUNT, *LINELIMIT);
const auto EXTRALINES = (VISLINECOUNT < LINECOUNT) ? 1 : 0;
@ -68,11 +72,11 @@ void CHyprError::createQueued() {
const double PAD = 10 * SCALE;
const double X = PAD;
const double Y = PAD;
const double WIDTH = PMONITOR->vecPixelSize.x - PAD * 2;
const double HEIGHT = (FONTSIZE + 2 * (FONTSIZE / 10.0)) * (VISLINECOUNT + EXTRALINES) + 3;
const double RADIUS = PAD > HEIGHT / 2 ? HEIGHT / 2 - 1 : PAD;
const double X = PAD;
const double Y = TOPBAR ? PAD : PMONITOR->vecPixelSize.y - HEIGHT - PAD;
m_bDamageBox = {0, 0, (int)PMONITOR->vecPixelSize.x, (int)HEIGHT + (int)PAD * 2};
@ -91,31 +95,43 @@ void CHyprError::createQueued() {
// 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);
cairo_select_font_face(CAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(CAIRO, FONTSIZE);
cairo_set_source_rgba(CAIRO, textColor.r, textColor.g, textColor.b, textColor.a);
float yoffset = FONTSIZE;
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
PangoLayout* layoutText = pango_cairo_create_layout(CAIRO);
PangoFontDescription* pangoFD = pango_font_description_new();
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
pango_font_description_set_absolute_size(pangoFD, FONTSIZE * PANGO_SCALE);
pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL);
pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL);
pango_layout_set_font_description(layoutText, pangoFD);
float yoffset = TOPBAR ? 0 : Y - PAD;
int renderedcnt = 0;
while (m_szQueued != "" && renderedcnt < VISLINECOUNT) {
while (!m_szQueued.empty() && renderedcnt < VISLINECOUNT) {
std::string current = m_szQueued.substr(0, m_szQueued.find('\n'));
if (const auto NEWLPOS = m_szQueued.find('\n'); NEWLPOS != std::string::npos)
m_szQueued = m_szQueued.substr(NEWLPOS + 1);
else
m_szQueued = "";
cairo_move_to(CAIRO, PAD + 1 + RADIUS, yoffset + PAD + 1);
cairo_show_text(CAIRO, current.c_str());
pango_layout_set_text(layoutText, current.c_str(), -1);
pango_cairo_show_layout(CAIRO, layoutText);
yoffset += FONTSIZE + (FONTSIZE / 10.f);
renderedcnt++;
}
if (VISLINECOUNT < LINECOUNT) {
std::string moreString = std::format("({} more...)", LINECOUNT - VISLINECOUNT);
cairo_move_to(CAIRO, PAD + 1 + RADIUS, yoffset + PAD + 1);
cairo_show_text(CAIRO, moreString.c_str());
pango_layout_set_text(layoutText, moreString.c_str(), -1);
pango_cairo_show_layout(CAIRO, layoutText);
}
m_szQueued = "";
pango_font_description_free(pangoFD);
g_object_unref(layoutText);
cairo_surface_flush(CAIROSURFACE);
// copy the data to an OpenGL texture we have

View File

@ -51,10 +51,7 @@ extern "C" {
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/util/log.h>

View File

@ -4,6 +4,7 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "../config/ConfigValue.hpp"
#include "../desktop/Window.hpp"
#include "../protocols/XDGShell.hpp"
void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) {
if (pWindow->m_bIsFloating) {
@ -140,8 +141,12 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) {
// TODO: detect a popup in a more consistent way.
if ((desiredGeometry.x == 0 && desiredGeometry.y == 0) || !visible || !pWindow->m_bIsX11) {
// if it's not, fall back to the center placement
pWindow->m_vRealPosition = PMONITOR->vecPosition + Vector2D((PMONITOR->vecSize.x - desiredGeometry.width) / 2.f, (PMONITOR->vecSize.y - desiredGeometry.height) / 2.f);
// if the pos isn't set, fall back to the center placement if it's not a child, otherwise middle of parent if available
if (!pWindow->m_bIsX11 && pWindow->m_pXDGSurface->toplevel->parent && validMapped(pWindow->m_pXDGSurface->toplevel->parent->window))
pWindow->m_vRealPosition = pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealPosition.goal() +
pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealSize.goal() / 2.F - desiredGeometry.size() / 2.F;
else
pWindow->m_vRealPosition = PMONITOR->vecPosition + desiredGeometry.size() / 2.F;
} else {
// if it is, we respect where it wants to put itself, but apply monitor offset if outside
// most of these are popups
@ -518,7 +523,7 @@ void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) {
CBox wb = {pWindow->m_vRealPosition.goal() + (pWindow->m_vRealSize.goal() - pWindow->m_vLastFloatingSize) / 2.f, pWindow->m_vLastFloatingSize};
wb.round();
if (DELTALESSTHAN(pWindow->m_vRealSize.value().x, pWindow->m_vLastFloatingSize.x, 10) &&
if (!(pWindow->m_bIsFloating && pWindow->m_bIsPseudotiled) && DELTALESSTHAN(pWindow->m_vRealSize.value().x, pWindow->m_vLastFloatingSize.x, 10) &&
DELTALESSTHAN(pWindow->m_vRealSize.value().y, pWindow->m_vLastFloatingSize.y, 10)) {
wb = {wb.pos() + Vector2D{10, 10}, wb.size() - Vector2D{20, 20}};
}
@ -693,7 +698,7 @@ Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) {
else
sizePredicted = predictSizeForNewWindowFloating(pWindow);
Vector2D maxSize = Vector2D{pWindow->m_uSurface.xdg->toplevel->pending.max_width, pWindow->m_uSurface.xdg->toplevel->pending.max_height};
Vector2D maxSize = pWindow->m_pXDGSurface->toplevel->pending.maxSize;
if ((maxSize.x > 0 && maxSize.x < sizePredicted.x) || (maxSize.y > 0 && maxSize.y < sizePredicted.y))
sizePredicted = {};

View File

@ -6,8 +6,11 @@
#include "TokenManager.hpp"
#include "../protocols/ShortcutsInhibit.hpp"
#include "../devices/IKeyboard.hpp"
#include "../managers/SeatManager.hpp"
#include <optional>
#include <regex>
#include <string>
#include <tuple>
#include <sys/ioctl.h>
@ -386,8 +389,7 @@ bool CKeybindManager::onKeyEvent(std::any event, SP<IKeyboard> pKeyboard) {
bool foundInPressedKeys = false;
for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) {
if (it->keycode == KEYCODE) {
if (it->submapAtPress == m_szCurrentSelectedSubmap)
handleKeybinds(MODS, *it, false);
handleKeybinds(MODS, *it, false);
foundInPressedKeys = true;
suppressEvent = !it->sent;
it = m_dPressedKeys.erase(it);
@ -523,7 +525,7 @@ void CKeybindManager::onSwitchOffEvent(const std::string& switchName) {
int repeatKeyHandler(void* data) {
SKeybind** ppActiveKeybind = (SKeybind**)data;
if (!*ppActiveKeybind || g_pCompositor->m_sSeat.keyboard.expired())
if (!*ppActiveKeybind || g_pSeatManager->keyboard.expired())
return 0;
const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find((*ppActiveKeybind)->handler);
@ -531,16 +533,53 @@ int repeatKeyHandler(void* data) {
Debug::log(LOG, "Keybind repeat triggered, calling dispatcher.");
DISPATCHER->second((*ppActiveKeybind)->arg);
wl_event_source_timer_update(g_pKeybindManager->m_pActiveKeybindEventSource, 1000 / g_pCompositor->m_sSeat.keyboard->repeatRate);
wl_event_source_timer_update(g_pKeybindManager->m_pActiveKeybindEventSource, 1000 / g_pSeatManager->keyboard->wlr()->repeat_info.rate);
return 0;
}
eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::set<xkb_keysym_t> keybindKeysyms, const std::set<xkb_keysym_t> pressedKeysyms) {
// Returns whether two sets of keysyms are equal, partially equal, or not
// matching. (Partially matching means that pressed is a subset of bound)
std::set<xkb_keysym_t> boundKeysNotPressed;
std::set<xkb_keysym_t> pressedKeysNotBound;
std::set_difference(keybindKeysyms.begin(), keybindKeysyms.end(), pressedKeysyms.begin(), pressedKeysyms.end(),
std::inserter(boundKeysNotPressed, boundKeysNotPressed.begin()));
std::set_difference(pressedKeysyms.begin(), pressedKeysyms.end(), keybindKeysyms.begin(), keybindKeysyms.end(),
std::inserter(pressedKeysNotBound, pressedKeysNotBound.begin()));
if (boundKeysNotPressed.empty() && pressedKeysNotBound.empty())
return MK_FULL_MATCH;
if (boundKeysNotPressed.size() && pressedKeysNotBound.empty())
return MK_PARTIAL_MATCH;
return MK_NO_MATCH;
}
eMultiKeyCase CKeybindManager::mkBindMatches(const SKeybind keybind) {
if (mkKeysymSetMatches(keybind.sMkMods, m_sMkMods) != MK_FULL_MATCH)
return MK_NO_MATCH;
return mkKeysymSetMatches(keybind.sMkKeys, m_sMkKeys);
}
bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) {
bool found = false;
if (g_pCompositor->m_sSeat.exclusiveClient)
Debug::log(LOG, "Keybind handling only locked (inhibitor)");
if (pressed) {
if (keycodeToModifier(key.keycode))
m_sMkMods.insert(key.keysym);
else
m_sMkKeys.insert(key.keysym);
} else {
if (keycodeToModifier(key.keycode))
m_sMkMods.erase(key.keysym);
else
m_sMkKeys.erase(key.keysym);
}
static auto PDISABLEINHIBIT = CConfigValue<Hyprlang::INT>("binds:disable_keybind_grabbing");
@ -559,18 +598,23 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
if (!k.locked && g_pSessionLockManager->isSessionLocked())
continue;
if (!IGNORECONDITIONS &&
((modmask != k.modmask && !k.ignoreMods) || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || k.submap != m_szCurrentSelectedSubmap || k.shadowed))
if (!IGNORECONDITIONS && ((modmask != k.modmask && !k.ignoreMods) || k.submap != m_szCurrentSelectedSubmap || k.shadowed))
continue;
if (!key.keyName.empty()) {
if (k.multiKey) {
switch (mkBindMatches(k)) {
case MK_NO_MATCH: continue;
case MK_PARTIAL_MATCH: found = true; continue;
case MK_FULL_MATCH: found = true;
}
} else if (!key.keyName.empty()) {
if (key.keyName != k.key)
continue;
} else if (k.keycode != 0) {
if (key.keycode != k.keycode)
continue;
} else if (k.catchAll) {
if (found)
if (found || key.submapAtPress != m_szCurrentSelectedSubmap)
continue;
} else {
// oMg such performance hit!!11!
@ -654,7 +698,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
m_pActiveKeybind = &k;
m_pActiveKeybindEventSource = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, repeatKeyHandler, &m_pActiveKeybind);
const auto PACTIVEKEEB = g_pCompositor->m_sSeat.keyboard.lock();
const auto PACTIVEKEEB = g_pSeatManager->keyboard.lock();
wl_event_source_timer_update(m_pActiveKeybindEventSource, PACTIVEKEEB->repeatDelay);
}
@ -676,25 +720,29 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint3
if (k.handler == "global" || k.transparent)
continue; // can't be shadowed
const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
if (k.multiKey && (mkBindMatches(k) == MK_FULL_MATCH))
shadow = true;
else {
const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
for (auto& pk : m_dPressedKeys) {
if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
shadow = true;
for (auto& pk : m_dPressedKeys) {
if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
shadow = true;
if (pk.keysym == doesntHave && doesntHave != 0) {
shadow = false;
break;
if (pk.keysym == doesntHave && doesntHave != 0) {
shadow = false;
break;
}
}
}
if (pk.keycode != 0 && pk.keycode == k.keycode) {
shadow = true;
if (pk.keycode != 0 && pk.keycode == k.keycode) {
shadow = true;
if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
shadow = false;
break;
if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
shadow = false;
break;
}
}
}
}
@ -1103,7 +1151,7 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) {
pMonitor = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
g_pCompositor->setActiveMonitor(pMonitor);
} else {
pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName);
pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName, false);
pMonitor = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
}
@ -1159,7 +1207,7 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
if (pWorkspace) {
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
} else {
pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName);
pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName, false);
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
}
@ -1172,8 +1220,9 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
}
void CKeybindManager::moveFocusTo(std::string args) {
static auto PFULLCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_fullscreen");
char arg = args[0];
static auto PFULLCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_fullscreen");
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
char arg = args[0];
if (!isDirection(args)) {
Debug::log(ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg);
@ -1182,7 +1231,9 @@ void CKeybindManager::moveFocusTo(std::string args) {
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
if (!PLASTWINDOW) {
tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg));
if (*PMONITORFALLBACK)
tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg));
return;
}
@ -1198,7 +1249,7 @@ void CKeybindManager::moveFocusTo(std::string args) {
Debug::log(LOG, "No window found in direction {}, looking for a monitor", arg);
if (tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)))
if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)))
return;
static auto PNOFALLBACK = CConfigValue<Hyprlang::INT>("general:no_focus_fallback");
@ -1269,6 +1320,8 @@ void CKeybindManager::moveActiveTo(std::string args) {
moveActiveToWorkspaceSilent(PNEWMONITOR->activeWorkspace->getConfigName());
else
moveActiveToWorkspace(PNEWMONITOR->activeWorkspace->getConfigName());
return;
}
if (!isDirection(args)) {
@ -1308,6 +1361,10 @@ void CKeybindManager::moveActiveTo(std::string args) {
return;
}
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
if (!*PMONITORFALLBACK)
return;
// Otherwise, we always want to move to the next monitor in that direction
const auto PMONITORTOCHANGETO = g_pCompositor->getMonitorInDirection(arg);
if (!PMONITORTOCHANGETO)
@ -1902,46 +1959,42 @@ void CKeybindManager::pass(std::string regexp) {
return;
}
const auto KEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat);
if (!KEYBOARD) {
if (!g_pSeatManager->keyboard) {
Debug::log(ERR, "No kb in pass?");
return;
}
const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsX11;
const auto SL = Vector2D(g_pCompositor->m_sSeat.seat->pointer_state.sx, g_pCompositor->m_sSeat.seat->pointer_state.sy);
uint32_t keycodes[32] = {0};
const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsX11;
const auto LASTSRF = g_pCompositor->m_pLastFocus;
// pass all mf shit
if (!XWTOXW) {
if (g_pKeybindManager->m_uLastCode != 0)
wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), keycodes, 0, &KEYBOARD->modifiers);
g_pSeatManager->setKeyboardFocus(PWINDOW->m_pWLSurface.wlr());
else
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), 1, 1);
g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface.wlr(), {1, 1});
}
wlr_keyboard_modifiers kbmods = {g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0};
wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &kbmods);
g_pSeatManager->sendKeyboardMods(g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0);
if (g_pKeybindManager->m_iPassPressed == 1) {
if (g_pKeybindManager->m_uLastCode != 0)
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_PRESSED);
g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED);
else
wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED);
g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED);
} else if (g_pKeybindManager->m_iPassPressed == 0)
if (g_pKeybindManager->m_uLastCode != 0)
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_RELEASED);
g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED);
else
wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED);
g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED);
else {
// dynamic call of the dispatcher
if (g_pKeybindManager->m_uLastCode != 0) {
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_PRESSED);
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WLR_BUTTON_RELEASED);
g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED);
g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED);
} else {
wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED);
wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED);
g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED);
g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED);
}
}
@ -1949,22 +2002,24 @@ void CKeybindManager::pass(std::string regexp) {
return;
// Massive hack:
// this will make wlroots NOT send the leave event to XWayland apps, provided we are not on an XWayland window already.
// this will make g_pSeatManager NOT send the leave event to XWayland apps, provided we are not on an XWayland window already.
// please kill me
if (PWINDOW->m_bIsX11) {
if (g_pKeybindManager->m_uLastCode != 0) {
g_pCompositor->m_sSeat.seat->keyboard_state.focused_client = nullptr;
g_pCompositor->m_sSeat.seat->keyboard_state.focused_surface = nullptr;
g_pSeatManager->state.keyboardFocus = nullptr;
g_pSeatManager->state.keyboardFocusResource.reset();
} else {
g_pCompositor->m_sSeat.seat->pointer_state.focused_client = nullptr;
g_pCompositor->m_sSeat.seat->pointer_state.focused_surface = nullptr;
g_pSeatManager->state.pointerFocus = nullptr;
g_pSeatManager->state.pointerFocusResource.reset();
}
}
const auto SL = PWINDOW->m_vRealPosition.goal() - g_pInputManager->getMouseCoordsInternal();
if (g_pKeybindManager->m_uLastCode != 0)
wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers);
g_pSeatManager->setKeyboardFocus(LASTSRF);
else
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), SL.x, SL.y);
g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface.wlr(), SL);
}
void CKeybindManager::layoutmsg(std::string msg) {

View File

@ -2,6 +2,7 @@
#include "../defines.hpp"
#include <deque>
#include <set>
#include "../Compositor.hpp"
#include <unordered_map>
#include <functional>
@ -13,20 +14,23 @@ class CPluginSystem;
class IKeyboard;
struct SKeybind {
std::string key = "";
uint32_t keycode = 0;
bool catchAll = false;
uint32_t modmask = 0;
std::string handler = "";
std::string arg = "";
bool locked = false;
std::string submap = "";
bool release = false;
bool repeat = false;
bool mouse = false;
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
std::string key = "";
std::set<xkb_keysym_t> sMkKeys = {};
uint32_t keycode = 0;
bool catchAll = false;
uint32_t modmask = 0;
std::set<xkb_keysym_t> sMkMods = {};
std::string handler = "";
std::string arg = "";
bool locked = false;
std::string submap = "";
bool release = false;
bool repeat = false;
bool mouse = false;
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
bool multiKey = false;
// DO NOT INITIALIZE
bool shadowed = false;
@ -57,6 +61,12 @@ struct SParsedKey {
bool catchAll = false;
};
enum eMultiKeyCase {
MK_NO_MATCH = 0,
MK_PARTIAL_MATCH,
MK_FULL_MATCH
};
class CKeybindManager {
public:
CKeybindManager();
@ -105,6 +115,11 @@ class CKeybindManager {
bool handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool);
std::set<xkb_keysym_t> m_sMkKeys = {};
std::set<xkb_keysym_t> m_sMkMods = {};
eMultiKeyCase mkBindMatches(const SKeybind);
eMultiKeyCase mkKeysymSetMatches(const std::set<xkb_keysym_t>, const std::set<xkb_keysym_t>);
bool handleInternalKeybinds(xkb_keysym_t);
bool handleVT(xkb_keysym_t);

View File

@ -3,6 +3,7 @@
#include "../config/ConfigValue.hpp"
#include "../protocols/PointerGestures.hpp"
#include "../protocols/FractionalScale.hpp"
#include "SeatManager.hpp"
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/interface.h>
#include <wlr/render/wlr_renderer.h>
@ -168,11 +169,13 @@ SP<CPointerManager::SMonitorPointerState> CPointerManager::stateFor(SP<CMonitor>
}
void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale) {
damageIfSoftware();
if (buf == currentCursorImage.pBuffer) {
if (hotspot != currentCursorImage.hotspot || scale != currentCursorImage.scale) {
currentCursorImage.hotspot = hotspot;
currentCursorImage.scale = scale;
updateCursorBackend();
damageIfSoftware();
}
return;
@ -192,14 +195,18 @@ void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot,
currentCursorImage.scale = scale;
updateCursorBackend();
damageIfSoftware();
}
void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot) {
damageIfSoftware();
if (surf == currentCursorImage.surface) {
if (hotspot != currentCursorImage.hotspot || (surf && surf->wlr() ? surf->wlr()->current.scale : 1.F) != currentCursorImage.scale) {
currentCursorImage.hotspot = hotspot;
currentCursorImage.scale = surf && surf->wlr() ? surf->wlr()->current.scale : 1.F;
updateCursorBackend();
damageIfSoftware();
}
return;
@ -216,14 +223,16 @@ void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot
currentCursorImage.hyprListener_commitSurface.initCallback(
&surf->wlr()->events.commit,
[this](void* owner, void* data) {
damageIfSoftware();
currentCursorImage.size = {currentCursorImage.surface->wlr()->current.buffer_width, currentCursorImage.surface->wlr()->current.buffer_height};
currentCursorImage.scale = currentCursorImage.surface && currentCursorImage.surface->wlr() ? currentCursorImage.surface->wlr()->current.scale : 1.F;
recheckEnteredOutputs();
updateCursorBackend();
damageIfSoftware();
},
nullptr, "CPointerManager");
if (surf->wlr()->current.buffer) {
if (wlr_surface_has_buffer(surf->wlr())) {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_surface_send_frame_done(surf->wlr(), &now);
@ -234,6 +243,7 @@ void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot
recheckEnteredOutputs();
updateCursorBackend();
damageIfSoftware();
}
void CPointerManager::recheckEnteredOutputs() {
@ -275,6 +285,8 @@ void CPointerManager::recheckEnteredOutputs() {
}
void CPointerManager::resetCursorImage(bool apply) {
damageIfSoftware();
if (currentCursorImage.surface) {
for (auto& m : g_pCompositor->m_vMonitors) {
wlr_surface_send_leave(currentCursorImage.surface->wlr(), m->output);
@ -308,6 +320,11 @@ void CPointerManager::resetCursorImage(bool apply) {
return;
for (auto& ms : monitorStates) {
if (!ms->monitor || !ms->monitor->m_bEnabled || !ms->monitor->dpmsStatus) {
Debug::log(TRACE, "Not updating hw cursors: disabled / dpms off display");
continue;
}
if (ms->cursorFrontBuffer) {
if (ms->monitor->output->impl->set_cursor)
ms->monitor->output->impl->set_cursor(ms->monitor->output, nullptr, 0, 0);
@ -323,6 +340,11 @@ void CPointerManager::updateCursorBackend() {
for (auto& m : g_pCompositor->m_vMonitors) {
auto state = stateFor(m);
if (!m->m_bEnabled || !m->dpmsStatus) {
Debug::log(TRACE, "Not updating hw cursors: disabled / dpms off display");
continue;
}
if (state->softwareLocks > 0 || *PNOHW || !attemptHardwareCursor(state)) {
Debug::log(TRACE, "Output {} rejected hardware cursors, falling back to sw", m->szName);
state->box = getCursorBoxLogicalForMonitor(state->monitor.lock());
@ -774,7 +796,7 @@ void CPointerManager::attachPointer(SP<IPointer> pointer) {
});
listener->frame = pointer->pointerEvents.frame.registerListener([this] (std::any e) {
wlr_seat_pointer_notify_frame(g_pCompositor->m_sSeat.seat);
g_pSeatManager->sendPointerFrame();
});
listener->swipeBegin = pointer->pointerEvents.swipeBegin.registerListener([this] (std::any e) {
@ -865,7 +887,7 @@ void CPointerManager::attachTouch(SP<ITouch> touch) {
});
listener->frame = touch->touchEvents.frame.registerListener([this] (std::any e) {
wlr_seat_touch_notify_frame(g_pCompositor->m_sSeat.seat);
g_pSeatManager->sendTouchFrame();
});
// clang-format on
@ -940,3 +962,7 @@ void CPointerManager::damageCursor(SP<CMonitor> pMonitor) {
return;
}
}
Vector2D CPointerManager::cursorSizeLogical() {
return currentCursorImage.size / currentCursorImage.scale;
}

View File

@ -52,6 +52,7 @@ class CPointerManager {
//
Vector2D position();
Vector2D cursorSizeLogical();
private:
void recheckPointerPosition();

View File

@ -28,9 +28,20 @@
#include "../protocols/Tablet.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp"
#include "../protocols/XDGShell.hpp"
#include "../protocols/DataDeviceWlr.hpp"
#include "../protocols/PrimarySelection.hpp"
#include "../protocols/core/Seat.hpp"
#include "../protocols/core/DataDevice.hpp"
CProtocolManager::CProtocolManager() {
// Core
PROTO::seat = std::make_unique<CWLSeatProtocol>(&wl_seat_interface, 9, "WLSeat");
PROTO::data = std::make_unique<CWLDataDeviceProtocol>(&wl_data_device_manager_interface, 3, "WLDataDevice");
// Extensions
PROTO::tearing = std::make_unique<CTearingControlProtocol>(&wp_tearing_control_manager_v1_interface, 1, "TearingControl");
PROTO::fractional = std::make_unique<CFractionalScaleProtocol>(&wp_fractional_scale_manager_v1_interface, 1, "FractionalScale");
PROTO::xdgOutput = std::make_unique<CXDGOutputProtocol>(&zxdg_output_manager_v1_interface, 3, "XDGOutput");
@ -59,6 +70,9 @@ CProtocolManager::CProtocolManager() {
PROTO::tablet = std::make_unique<CTabletV2Protocol>(&zwp_tablet_manager_v2_interface, 1, "TabletV2");
PROTO::layerShell = std::make_unique<CLayerShellProtocol>(&zwlr_layer_shell_v1_interface, 5, "LayerShell");
PROTO::presentation = std::make_unique<CPresentationProtocol>(&wp_presentation_interface, 1, "Presentation");
PROTO::xdgShell = std::make_unique<CXDGShellProtocol>(&xdg_wm_base_interface, 6, "XDGShell");
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");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.

View File

@ -0,0 +1,576 @@
#include "SeatManager.hpp"
#include "../protocols/core/Seat.hpp"
#include "../protocols/core/DataDevice.hpp"
#include "../protocols/DataDeviceWlr.hpp"
#include "../protocols/PrimarySelection.hpp"
#include "../Compositor.hpp"
#include "../devices/IKeyboard.hpp"
#include <algorithm>
#include <ranges>
CSeatManager::CSeatManager() {
listeners.newSeatResource = PROTO::seat->events.newSeatResource.registerListener([this](std::any res) { onNewSeatResource(std::any_cast<SP<CWLSeatResource>>(res)); });
}
CSeatManager::SSeatResourceContainer::SSeatResourceContainer(SP<CWLSeatResource> res) {
resource = res;
listeners.destroy = res->events.destroy.registerListener(
[this](std::any data) { std::erase_if(g_pSeatManager->seatResources, [this](const auto& e) { return e->resource.expired() || e->resource == resource; }); });
}
void CSeatManager::onNewSeatResource(SP<CWLSeatResource> resource) {
seatResources.emplace_back(makeShared<SSeatResourceContainer>(resource));
}
SP<CSeatManager::SSeatResourceContainer> CSeatManager::containerForResource(SP<CWLSeatResource> seatResource) {
for (auto& c : seatResources) {
if (c->resource == seatResource)
return c;
}
return nullptr;
}
uint32_t CSeatManager::nextSerial(SP<CWLSeatResource> seatResource) {
if (!seatResource)
return 0;
auto container = containerForResource(seatResource);
ASSERT(container);
auto serial = wl_display_next_serial(g_pCompositor->m_sWLDisplay);
container->serials.emplace_back(serial);
if (container->serials.size() > MAX_SERIAL_STORE_LEN)
container->serials.erase(container->serials.begin());
return serial;
}
bool CSeatManager::serialValid(SP<CWLSeatResource> seatResource, uint32_t serial) {
if (!seatResource)
return false;
auto container = containerForResource(seatResource);
ASSERT(container);
for (auto it = container->serials.begin(); it != container->serials.end(); ++it) {
if (*it == serial) {
container->serials.erase(it);
return true;
}
}
return false;
}
void CSeatManager::updateCapabilities(uint32_t capabilities) {
PROTO::seat->updateCapabilities(capabilities);
}
void CSeatManager::setMouse(SP<IPointer> MAUZ) {
if (mouse == MAUZ)
return;
mouse = MAUZ;
}
void CSeatManager::setKeyboard(SP<IKeyboard> KEEB) {
if (keyboard == KEEB)
return;
if (keyboard)
keyboard->active = false;
keyboard = KEEB;
if (KEEB)
KEEB->active = true;
updateActiveKeyboardData();
}
void CSeatManager::updateActiveKeyboardData() {
if (keyboard)
PROTO::seat->updateRepeatInfo(keyboard->wlr()->repeat_info.rate, keyboard->wlr()->repeat_info.delay);
PROTO::seat->updateKeymap();
}
void CSeatManager::setKeyboardFocus(wlr_surface* surf) {
if (state.keyboardFocus == surf)
return;
if (!keyboard || !keyboard->wlr()) {
Debug::log(ERR, "BUG THIS: setKeyboardFocus without a valid keyboard set");
return;
}
hyprListener_keyboardSurfaceDestroy.removeCallback();
if (state.keyboardFocusResource) {
// we will iterate over all bound wl_seat
// resources here, because some idiotic apps (e.g. those based on smithay)
// tend to bind wl_seat twice.
// I can't be arsed to actually pass all events to all seat resources, so we will
// only pass enter and leave.
// If you have an issue with that, fix your app.
auto client = state.keyboardFocusResource->client();
for (auto& s : seatResources) {
if (s->resource->client() != client)
continue;
for (auto& k : s->resource->keyboards) {
if (!k)
continue;
k->sendLeave();
}
}
}
state.keyboardFocusResource.reset();
state.keyboardFocus = surf;
if (!surf) {
events.keyboardFocusChange.emit();
return;
}
auto client = wl_resource_get_client(surf->resource);
for (auto& r : seatResources | std::views::reverse) {
if (r->resource->client() != client)
continue;
state.keyboardFocusResource = r->resource;
for (auto& k : r->resource->keyboards) {
if (!k)
continue;
k->sendEnter(surf);
k->sendMods(keyboard->wlr()->modifiers.depressed, keyboard->wlr()->modifiers.latched, keyboard->wlr()->modifiers.locked, keyboard->wlr()->modifiers.group);
}
}
hyprListener_keyboardSurfaceDestroy.initCallback(
&surf->events.destroy, [this](void* owner, void* data) { setKeyboardFocus(nullptr); }, nullptr, "CSeatManager");
events.keyboardFocusChange.emit();
}
void CSeatManager::sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state_) {
if (!state.keyboardFocusResource)
return;
for (auto& k : state.keyboardFocusResource->keyboards) {
if (!k)
continue;
k->sendKey(timeMs, key, state_);
}
}
void CSeatManager::sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
if (!state.keyboardFocusResource)
return;
for (auto& k : state.keyboardFocusResource->keyboards) {
if (!k)
continue;
k->sendMods(depressed, latched, locked, group);
}
}
void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) {
if (state.pointerFocus == surf)
return;
if (!mouse || !mouse->wlr()) {
Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set");
return;
}
hyprListener_pointerSurfaceDestroy.removeCallback();
if (state.pointerFocusResource) {
auto client = state.pointerFocusResource->client();
for (auto& s : seatResources) {
if (s->resource->client() != client)
continue;
for (auto& p : s->resource->pointers) {
if (!p)
continue;
p->sendLeave();
}
}
}
auto lastPointerFocusResource = state.pointerFocusResource;
state.pointerFocusResource.reset();
state.pointerFocus = surf;
if (!surf) {
sendPointerFrame(lastPointerFocusResource);
events.pointerFocusChange.emit();
return;
}
auto client = wl_resource_get_client(surf->resource);
for (auto& r : seatResources | std::views::reverse) {
if (r->resource->client() != client)
continue;
state.pointerFocusResource = r->resource;
for (auto& p : r->resource->pointers) {
if (!p)
continue;
p->sendEnter(surf, local);
}
}
if (state.pointerFocusResource != lastPointerFocusResource)
sendPointerFrame(lastPointerFocusResource);
sendPointerFrame();
hyprListener_pointerSurfaceDestroy.initCallback(
&surf->events.destroy, [this](void* owner, void* data) { setPointerFocus(nullptr, {}); }, nullptr, "CSeatManager");
events.pointerFocusChange.emit();
}
void CSeatManager::sendPointerMotion(uint32_t timeMs, const Vector2D& local) {
if (!state.pointerFocusResource)
return;
for (auto& p : state.pointerFocusResource->pointers) {
if (!p)
continue;
p->sendMotion(timeMs, local);
}
lastLocalCoords = local;
}
void CSeatManager::sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state_) {
if (!state.pointerFocusResource)
return;
for (auto& p : state.pointerFocusResource->pointers) {
if (!p)
continue;
p->sendButton(timeMs, key, state_);
}
}
void CSeatManager::sendPointerFrame() {
if (!state.pointerFocusResource)
return;
sendPointerFrame(state.pointerFocusResource);
}
void CSeatManager::sendPointerFrame(WP<CWLSeatResource> pResource) {
if (!pResource)
return;
for (auto& p : pResource->pointers) {
if (!p)
continue;
p->sendFrame();
}
}
void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, wl_pointer_axis_source source,
wl_pointer_axis_relative_direction relative) {
if (!state.pointerFocusResource)
return;
for (auto& p : state.pointerFocusResource->pointers) {
if (!p)
continue;
p->sendAxis(timeMs, axis, value);
p->sendAxisSource(source);
p->sendAxisRelativeDirection(axis, relative);
if (source == 0)
p->sendAxisDiscrete(axis, discrete);
if (value == 0)
p->sendAxisStop(timeMs, axis);
}
}
void CSeatManager::sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (state.touchFocus == surf)
return;
hyprListener_touchSurfaceDestroy.removeCallback();
if (state.touchFocusResource) {
auto client = state.touchFocusResource->client();
for (auto& s : seatResources) {
if (s->resource->client() != client)
continue;
for (auto& t : s->resource->touches) {
if (!t)
continue;
t->sendUp(timeMs, id);
}
}
}
state.touchFocusResource.reset();
state.touchFocus = surf;
if (!surf) {
events.touchFocusChange.emit();
return;
}
auto client = wl_resource_get_client(surf->resource);
for (auto& r : seatResources | std::views::reverse) {
if (r->resource->client() != client)
continue;
state.touchFocusResource = r->resource;
for (auto& t : r->resource->touches) {
if (!t)
continue;
t->sendDown(surf, timeMs, id, local);
}
}
hyprListener_touchSurfaceDestroy.initCallback(
&surf->events.destroy, [this, timeMs, id](void* owner, void* data) { sendTouchUp(timeMs + 10, id); }, nullptr, "CSeatManager");
events.touchFocusChange.emit();
}
void CSeatManager::sendTouchUp(uint32_t timeMs, int32_t id) {
sendTouchDown(nullptr, timeMs, id, {});
}
void CSeatManager::sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!state.touchFocusResource)
return;
for (auto& t : state.touchFocusResource->touches) {
if (!t)
continue;
t->sendMotion(timeMs, id, local);
}
}
void CSeatManager::sendTouchFrame() {
if (!state.touchFocusResource)
return;
for (auto& t : state.touchFocusResource->touches) {
if (!t)
continue;
t->sendFrame();
}
}
void CSeatManager::sendTouchCancel() {
if (!state.touchFocusResource)
return;
for (auto& t : state.touchFocusResource->touches) {
if (!t)
continue;
t->sendCancel();
}
}
void CSeatManager::sendTouchShape(int32_t id, const Vector2D& shape) {
if (!state.touchFocusResource)
return;
for (auto& t : state.touchFocusResource->touches) {
if (!t)
continue;
t->sendShape(id, shape);
}
}
void CSeatManager::sendTouchOrientation(int32_t id, double angle) {
if (!state.touchFocusResource)
return;
for (auto& t : state.touchFocusResource->touches) {
if (!t)
continue;
t->sendOrientation(id, angle);
}
}
void CSeatManager::refocusGrab() {
if (!seatGrab)
return;
if (seatGrab->surfs.size() > 0) {
// try to find a surf in focus first
const auto MOUSE = g_pInputManager->getMouseCoordsInternal();
for (auto& s : seatGrab->surfs) {
auto hlSurf = CWLSurface::surfaceFromWlr(s);
if (!hlSurf)
continue;
auto b = hlSurf->getSurfaceBoxGlobal();
if (!b.has_value())
continue;
if (!b->containsPoint(MOUSE))
continue;
if (seatGrab->keyboard)
setKeyboardFocus(s);
if (seatGrab->pointer)
setPointerFocus(s, MOUSE - b->pos());
return;
}
wlr_surface* surf = seatGrab->surfs.at(0);
if (seatGrab->keyboard)
setKeyboardFocus(surf);
if (seatGrab->pointer)
setPointerFocus(surf, {});
}
}
void CSeatManager::onSetCursor(SP<CWLSeatResource> seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot) {
if (!state.pointerFocusResource || !seatResource || seatResource->client() != state.pointerFocusResource->client()) {
Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the client ain't in focus");
return;
}
// TODO: fix this. Probably should be done in the CWlPointer as the serial could be lost by us.
// if (!serialValid(seatResource, serial)) {
// Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the serial is invalid");
// return;
// }
events.setCursor.emit(SSetCursorEvent{surf, hotspot});
}
SP<CWLSeatResource> CSeatManager::seatResourceForClient(wl_client* client) {
return PROTO::seat->seatResourceForClient(client);
}
void CSeatManager::setCurrentSelection(SP<IDataSource> source) {
if (source == selection.currentSelection) {
Debug::log(WARN, "[seat] duplicated setCurrentSelection?");
return;
}
selection.destroySelection.reset();
if (selection.currentSelection)
selection.currentSelection->cancelled();
if (!source)
PROTO::data->setSelection(nullptr);
selection.currentSelection = source;
if (source) {
selection.destroySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentSelection(nullptr); });
PROTO::data->setSelection(source);
PROTO::dataWlr->setSelection(source, false);
}
}
void CSeatManager::setCurrentPrimarySelection(SP<IDataSource> source) {
if (source == selection.currentPrimarySelection) {
Debug::log(WARN, "[seat] duplicated setCurrentPrimarySelection?");
return;
}
selection.destroyPrimarySelection.reset();
if (selection.currentPrimarySelection)
selection.currentPrimarySelection->cancelled();
if (!source)
PROTO::primarySelection->setSelection(nullptr);
selection.currentPrimarySelection = source;
if (source) {
selection.destroyPrimarySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentPrimarySelection(nullptr); });
PROTO::primarySelection->setSelection(source);
PROTO::dataWlr->setSelection(source, true);
}
}
void CSeatManager::setGrab(SP<CSeatGrab> grab) {
if (seatGrab) {
auto oldGrab = seatGrab;
seatGrab.reset();
g_pInputManager->refocus();
if (oldGrab->onEnd)
oldGrab->onEnd();
}
if (!grab)
return;
seatGrab = grab;
refocusGrab();
}
void CSeatManager::resendEnterEvents() {
wlr_surface* kb = state.keyboardFocus;
wlr_surface* pt = state.pointerFocus;
auto last = lastLocalCoords;
setKeyboardFocus(nullptr);
setPointerFocus(nullptr, {});
setKeyboardFocus(kb);
setPointerFocus(pt, last);
}
bool CSeatGrab::accepts(wlr_surface* surf) {
return std::find(surfs.begin(), surfs.end(), surf) != surfs.end();
}
void CSeatGrab::add(wlr_surface* surf) {
surfs.push_back(surf);
}
void CSeatGrab::remove(wlr_surface* surf) {
std::erase(surfs, surf);
if ((keyboard && g_pSeatManager->state.keyboardFocus == surf) || (pointer && g_pSeatManager->state.pointerFocus == surf))
g_pSeatManager->refocusGrab();
}
void CSeatGrab::setCallback(std::function<void()> onEnd_) {
onEnd = onEnd_;
}
void CSeatGrab::clear() {
surfs.clear();
}

View File

@ -0,0 +1,160 @@
#pragma once
#include <wayland-server-protocol.h>
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
#include "../helpers/signal/Signal.hpp"
#include "../helpers/Vector2D.hpp"
#include "../protocols/types/DataDevice.hpp"
#include <vector>
constexpr size_t MAX_SERIAL_STORE_LEN = 100;
struct wlr_surface;
class CWLSeatResource;
class IPointer;
class IKeyboard;
/*
A seat grab defines a restricted set of surfaces that can be focused.
Only one grab can be active at a time
when a grab is removed, refocus() will happen
Different from a constraint.
When first set with setGrab, SeatManager will try to find a surface that is at the mouse pointer to focus,
from first added to last added. If none are, first is focused.
*/
class CSeatGrab {
public:
bool accepts(wlr_surface* surf);
void add(wlr_surface* surf);
void remove(wlr_surface* surf);
void setCallback(std::function<void()> onEnd_);
void clear();
bool keyboard = false;
bool pointer = false;
bool touch = false;
bool removeOnInput = true; // on hard input e.g. click outside, remove
private:
std::vector<wlr_surface*> surfs; // read-only
std::function<void()> onEnd;
friend class CSeatManager;
};
class CSeatManager {
public:
CSeatManager();
void updateCapabilities(uint32_t capabilities); // in IHID caps
void setMouse(SP<IPointer> mouse);
void setKeyboard(SP<IKeyboard> keeb);
void updateActiveKeyboardData(); // updates the clients with the keymap and repeat info
void setKeyboardFocus(wlr_surface* surf);
void sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state);
void sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
void setPointerFocus(wlr_surface* surf, const Vector2D& local);
void sendPointerMotion(uint32_t timeMs, const Vector2D& local);
void sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state);
void sendPointerFrame();
void sendPointerFrame(WP<CWLSeatResource> pResource);
void sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, wl_pointer_axis_source source, wl_pointer_axis_relative_direction relative);
void sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, const Vector2D& local);
void sendTouchUp(uint32_t timeMs, int32_t id);
void sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local);
void sendTouchFrame();
void sendTouchCancel();
void sendTouchShape(int32_t id, const Vector2D& shape);
void sendTouchOrientation(int32_t id, double angle);
void resendEnterEvents();
uint32_t nextSerial(SP<CWLSeatResource> seatResource);
// pops the serial if it was valid, meaning it is consumed.
bool serialValid(SP<CWLSeatResource> seatResource, uint32_t serial);
void onSetCursor(SP<CWLSeatResource> seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot);
SP<CWLSeatResource> seatResourceForClient(wl_client* client);
struct {
wlr_surface* keyboardFocus = nullptr;
WP<CWLSeatResource> keyboardFocusResource;
wlr_surface* pointerFocus = nullptr;
WP<CWLSeatResource> pointerFocusResource;
wlr_surface* touchFocus = nullptr;
WP<CWLSeatResource> touchFocusResource;
} state;
struct SSetCursorEvent {
wlr_surface* surf = nullptr;
Vector2D hotspot;
};
struct {
CSignal keyboardFocusChange;
CSignal pointerFocusChange;
CSignal touchFocusChange;
CSignal setCursor; // SSetCursorEvent
} events;
struct {
WP<IDataSource> currentSelection;
CHyprSignalListener destroySelection;
WP<IDataSource> currentPrimarySelection;
CHyprSignalListener destroyPrimarySelection;
} selection;
void setCurrentSelection(SP<IDataSource> source);
void setCurrentPrimarySelection(SP<IDataSource> source);
// do not write to directly, use set...
WP<IPointer> mouse;
WP<IKeyboard> keyboard;
void setGrab(SP<CSeatGrab> grab); // nullptr removes
SP<CSeatGrab> seatGrab;
private:
struct SSeatResourceContainer {
SSeatResourceContainer(SP<CWLSeatResource>);
WP<CWLSeatResource> resource;
std::vector<uint32_t> serials; // old -> new
struct {
CHyprSignalListener destroy;
} listeners;
};
std::vector<SP<SSeatResourceContainer>> seatResources;
void onNewSeatResource(SP<CWLSeatResource> resource);
SP<SSeatResourceContainer> containerForResource(SP<CWLSeatResource> seatResource);
void refocusGrab();
struct {
CHyprSignalListener newSeatResource;
} listeners;
Vector2D lastLocalCoords;
DYNLISTENER(keyboardSurfaceDestroy);
DYNLISTENER(pointerSurfaceDestroy);
DYNLISTENER(touchSurfaceDestroy);
friend struct SSeatResourceContainer;
friend class CSeatGrab;
};
inline UP<CSeatManager> g_pSeatManager;

View File

@ -2,6 +2,7 @@
#include "../Compositor.hpp"
#include "../events/Events.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/XDGShell.hpp"
#define OUTPUT_MANAGER_VERSION 3
#define OUTPUT_DONE_DEPRECATED_SINCE_VERSION 3
@ -34,27 +35,32 @@ CHyprXWaylandManager::~CHyprXWaylandManager() {
}
wlr_surface* CHyprXWaylandManager::getWindowSurface(PHLWINDOW pWindow) {
if (pWindow->m_bIsX11)
return pWindow->m_uSurface.xwayland->surface;
return pWindow->m_uSurface.xdg->surface;
return pWindow->m_pWLSurface.wlr();
}
void CHyprXWaylandManager::activateSurface(wlr_surface* pSurface, bool activate) {
if (!pSurface)
return;
if (wlr_xdg_surface_try_from_wlr_surface(pSurface)) {
if (const auto PSURF = wlr_xdg_surface_try_from_wlr_surface(pSurface); PSURF && PSURF->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL)
wlr_xdg_toplevel_set_activated(PSURF->toplevel, activate);
} else if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) {
if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) {
const auto XSURF = wlr_xwayland_surface_try_from_wlr_surface(pSurface);
wlr_xwayland_surface_activate(XSURF, activate);
if (activate && !XSURF->override_redirect)
wlr_xwayland_surface_restack(XSURF, nullptr, XCB_STACK_MODE_ABOVE);
}
// TODO:
// this cannot be nicely done until we rewrite wlr_surface
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_bIsX11 || !w->m_bIsMapped)
continue;
if (w->m_pWLSurface.wlr() != pSurface)
continue;
w->m_pXDGSurface->toplevel->setActive(activate);
}
}
void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) {
@ -68,8 +74,8 @@ void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) {
}
wlr_xwayland_surface_activate(pWindow->m_uSurface.xwayland, activate);
} else
wlr_xdg_toplevel_set_activated(pWindow->m_uSurface.xdg->toplevel, activate);
} else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel)
pWindow->m_pXDGSurface->toplevel->setActive(activate);
if (activate) {
g_pCompositor->m_pLastFocus = getWindowSurface(pWindow);
@ -95,64 +101,15 @@ void CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow, CBox* pbox) {
pbox->width = pWindow->m_uSurface.xwayland->width;
pbox->height = pWindow->m_uSurface.xwayland->height;
}
} else {
wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, pbox->pWlr());
pbox->applyFromWlr();
}
}
std::string CHyprXWaylandManager::getTitle(PHLWINDOW pWindow) {
try {
if (pWindow->m_bIsX11) {
if (!pWindow->m_bIsMapped)
return "";
if (pWindow->m_uSurface.xwayland && pWindow->m_uSurface.xwayland->title) {
return std::string(pWindow->m_uSurface.xwayland->title);
}
} else if (pWindow->m_uSurface.xdg) {
if (pWindow->m_bFadingOut || !pWindow->m_uSurface.xdg)
return "";
if (pWindow->m_uSurface.xdg->toplevel && pWindow->m_uSurface.xdg->toplevel->title) {
return std::string(pWindow->m_uSurface.xdg->toplevel->title);
}
} else {
return "";
}
} catch (...) { Debug::log(ERR, "Error in getTitle (probably null title)"); }
return "";
}
std::string CHyprXWaylandManager::getAppIDClass(PHLWINDOW pWindow) {
try {
if (pWindow->m_bIsX11) {
if (!pWindow->m_bIsMapped)
return "";
if (pWindow->m_uSurface.xwayland && pWindow->m_uSurface.xwayland->_class) {
return std::string(pWindow->m_uSurface.xwayland->_class);
}
} else if (pWindow->m_uSurface.xdg) {
if (pWindow->m_bFadingOut || !pWindow->m_uSurface.xdg)
return "";
if (pWindow->m_uSurface.xdg->toplevel && pWindow->m_uSurface.xdg->toplevel->app_id) {
return std::string(pWindow->m_uSurface.xdg->toplevel->app_id);
}
} else
return "";
} catch (std::logic_error& e) { Debug::log(ERR, "Error in getAppIDClass: {}", e.what()); }
return "";
} else if (pWindow->m_pXDGSurface)
*pbox = pWindow->m_pXDGSurface->current.geometry;
}
void CHyprXWaylandManager::sendCloseWindow(PHLWINDOW pWindow) {
if (pWindow->m_bIsX11)
wlr_xwayland_surface_close(pWindow->m_uSurface.xwayland);
else
wlr_xdg_toplevel_send_close(pWindow->m_uSurface.xdg->toplevel);
else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel)
pWindow->m_pXDGSurface->toplevel->close();
}
void CHyprXWaylandManager::setWindowSize(PHLWINDOW pWindow, Vector2D size, bool force) {
@ -189,23 +146,12 @@ void CHyprXWaylandManager::setWindowSize(PHLWINDOW pWindow, Vector2D size, bool
if (pWindow->m_bIsX11)
wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, windowPos.x, windowPos.y, size.x, size.y);
else
pWindow->m_vPendingSizeAcks.push_back(std::make_pair<>(wlr_xdg_toplevel_set_size(pWindow->m_uSurface.xdg->toplevel, size.x, size.y), size.floor()));
}
void CHyprXWaylandManager::setWindowStyleTiled(PHLWINDOW pWindow, uint32_t edgez) {
if (pWindow->m_bIsX11)
return;
wlr_xdg_toplevel_set_tiled(pWindow->m_uSurface.xdg->toplevel, edgez);
wlr_xdg_toplevel_set_maximized(pWindow->m_uSurface.xdg->toplevel, true);
else if (pWindow->m_pXDGSurface->toplevel)
pWindow->m_vPendingSizeAcks.push_back(std::make_pair<>(pWindow->m_pXDGSurface->toplevel->setSize(size), size.floor()));
}
wlr_surface* CHyprXWaylandManager::surfaceAt(PHLWINDOW pWindow, const Vector2D& client, Vector2D& surface) {
if (pWindow->m_bIsX11)
return wlr_surface_surface_at(pWindow->m_uSurface.xwayland->surface, client.x, client.y, &surface.x, &surface.y);
return wlr_xdg_surface_surface_at(pWindow->m_uSurface.xdg, client.x, client.y, &surface.x, &surface.y);
return wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), client.x, client.y, &surface.x, &surface.y);
}
bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) {
@ -251,10 +197,10 @@ bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) {
if (SIZEHINTS && (pWindow->m_uSurface.xwayland->parent || ((SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height))))
return true;
} else {
const auto PSTATE = pending ? &pWindow->m_uSurface.xdg->toplevel->pending : &pWindow->m_uSurface.xdg->toplevel->current;
const auto PSTATE = pending ? &pWindow->m_pXDGSurface->toplevel->pending : &pWindow->m_pXDGSurface->toplevel->current;
if ((PSTATE->min_width != 0 && PSTATE->min_height != 0 && (PSTATE->min_width == PSTATE->max_width || PSTATE->min_height == PSTATE->max_height)) ||
pWindow->m_uSurface.xdg->toplevel->parent)
if (pWindow->m_pXDGSurface->toplevel->parent ||
(PSTATE->minSize.x != 0 && PSTATE->minSize.y != 0 && (PSTATE->minSize.x == PSTATE->maxSize.x || PSTATE->minSize.y == PSTATE->maxSize.y)))
return true;
}
@ -297,20 +243,19 @@ void CHyprXWaylandManager::checkBorders(PHLWINDOW pWindow) {
void CHyprXWaylandManager::setWindowFullscreen(PHLWINDOW pWindow, bool fullscreen) {
if (pWindow->m_bIsX11)
wlr_xwayland_surface_set_fullscreen(pWindow->m_uSurface.xwayland, fullscreen);
else
wlr_xdg_toplevel_set_fullscreen(pWindow->m_uSurface.xdg->toplevel, fullscreen);
else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel)
pWindow->m_pXDGSurface->toplevel->setFullscreen(fullscreen);
}
Vector2D CHyprXWaylandManager::getMaxSizeForWindow(PHLWINDOW pWindow) {
if (!validMapped(pWindow))
return Vector2D(99999, 99999);
if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_uSurface.xdg->toplevel) ||
pWindow->m_sAdditionalConfigData.noMaxSize)
if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel) || pWindow->m_sAdditionalConfigData.noMaxSize)
return Vector2D(99999, 99999);
auto MAXSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->max_width, pWindow->m_uSurface.xwayland->size_hints->max_height) :
Vector2D(pWindow->m_uSurface.xdg->toplevel->current.max_width, pWindow->m_uSurface.xdg->toplevel->current.max_height);
pWindow->m_pXDGSurface->toplevel->current.maxSize;
if (MAXSIZE.x < 5)
MAXSIZE.x = 99999;
@ -324,11 +269,11 @@ Vector2D CHyprXWaylandManager::getMinSizeForWindow(PHLWINDOW pWindow) {
if (!validMapped(pWindow))
return Vector2D(0, 0);
if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_uSurface.xdg->toplevel))
if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel))
return Vector2D(0, 0);
auto MINSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->min_width, pWindow->m_uSurface.xwayland->size_hints->min_height) :
Vector2D(pWindow->m_uSurface.xdg->toplevel->current.min_width, pWindow->m_uSurface.xdg->toplevel->current.min_height);
pWindow->m_pXDGSurface->toplevel->current.minSize;
MINSIZE = MINSIZE.clamp({1, 1});

View File

@ -17,11 +17,8 @@ class CHyprXWaylandManager {
void activateSurface(wlr_surface*, bool);
void activateWindow(PHLWINDOW, bool);
void getGeometryForWindow(PHLWINDOW, CBox*);
std::string getTitle(PHLWINDOW);
std::string getAppIDClass(PHLWINDOW);
void sendCloseWindow(PHLWINDOW);
void setWindowSize(PHLWINDOW, Vector2D, bool force = false);
void setWindowStyleTiled(PHLWINDOW, uint32_t);
void setWindowFullscreen(PHLWINDOW, bool);
wlr_surface* surfaceAt(PHLWINDOW, const Vector2D&, Vector2D&);
bool shouldBeFloated(PHLWINDOW, bool pending = false);

View File

@ -14,6 +14,9 @@
#include "../../protocols/VirtualKeyboard.hpp"
#include "../../protocols/VirtualPointer.hpp"
#include "../../protocols/LayerShell.hpp"
#include "../../protocols/core/Seat.hpp"
#include "../../protocols/core/DataDevice.hpp"
#include "../../protocols/XDGShell.hpp"
#include "../../devices/Mouse.hpp"
#include "../../devices/VirtualPointer.hpp"
@ -22,6 +25,7 @@
#include "../../devices/TouchDevice.hpp"
#include "../../managers/PointerManager.hpp"
#include "../../managers/SeatManager.hpp"
CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
@ -30,10 +34,10 @@ CInputManager::CInputManager() {
auto event = std::any_cast<CCursorShapeProtocol::SSetShapeEvent>(data);
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
if (wl_resource_get_client(event.pMgr->resource()) != g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client)
if (wl_resource_get_client(event.pMgr->resource()) != g_pSeatManager->state.pointerFocusResource->client())
return;
Debug::log(LOG, "cursorImage request: shape {} -> {}", (uint32_t)event.shape, event.shapeName);
@ -52,6 +56,7 @@ CInputManager::CInputManager() {
PROTO::virtualKeyboard->events.newKeyboard.registerListener([this](std::any data) { this->newVirtualKeyboard(std::any_cast<SP<CVirtualKeyboardV1Resource>>(data)); });
m_sListeners.newVirtualMouse =
PROTO::virtualPointer->events.newPointer.registerListener([this](std::any data) { this->newVirtualMouse(std::any_cast<SP<CVirtualPointerV1Resource>>(data)); });
m_sListeners.setCursor = g_pSeatManager->events.setCursor.registerListener([this](std::any d) { this->processMouseRequest(d); });
}
CInputManager::~CInputManager() {
@ -119,8 +124,7 @@ void CInputManager::sendMotionEventsToFocused() {
m_bEmptyFocusCursorSet = false;
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, g_pCompositor->m_pLastFocus, LOCAL.x, LOCAL.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, now.tv_sec * 1000 + now.tv_nsec / 10000000, LOCAL.x, LOCAL.y);
g_pSeatManager->setPointerFocus(g_pCompositor->m_pLastFocus, LOCAL);
}
void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
@ -134,7 +138,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
static auto PRESIZECURSORICON = CConfigValue<Hyprlang::INT>("general:hover_icon_on_border");
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
const auto FOLLOWMOUSE = *PFOLLOWONDND && m_sDrag.drag ? 1 : *PFOLLOWMOUSE;
const auto FOLLOWMOUSE = *PFOLLOWONDND && PROTO::data->dndActive() ? 1 : *PFOLLOWMOUSE;
m_pFoundSurfaceToFocus = nullptr;
m_pFoundLSToFocus.reset();
@ -190,7 +194,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
}
// constraints
if (!g_pCompositor->m_sSeat.mouse.expired() && isConstrained()) {
if (!g_pSeatManager->mouse.expired() && isConstrained()) {
const auto SURF = CWLSurface::surfaceFromWlr(g_pCompositor->m_pLastFocus);
const auto CONSTRAINT = SURF->constraint();
@ -205,7 +209,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (SURF->getWindow() ? SURF->getWindow()->m_fX11SurfaceScaledBy : 1.0);
g_pCompositor->warpCursorTo(CLOSEST, true);
wlr_seat_pointer_send_motion(g_pCompositor->m_sSeat.seat, time, CLOSESTLOCAL.x, CLOSESTLOCAL.y);
g_pSeatManager->sendPointerMotion(time, CLOSESTLOCAL);
PROTO::relativePointer->sendRelativeMotion((uint64_t)time * 1000, {}, {});
}
@ -215,30 +219,32 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF, (uintptr_t)CONSTRAINT.get());
}
// update stuff
updateDragIcon();
// if we are holding a pointer button,
// and we're not dnd-ing, don't refocus. Keep focus on last surface.
if (!PROTO::data->dndActive() && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && g_pSeatManager->state.pointerFocus && !m_bHardInput) {
foundSurface = g_pSeatManager->state.pointerFocus;
if (!m_sDrag.drag && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && m_pLastMouseSurface) {
foundSurface = m_pLastMouseSurface;
pFoundLayerSurface = g_pCompositor->getLayerSurfaceFromSurface(foundSurface);
if (pFoundLayerSurface) {
surfacePos = pFoundLayerSurface->position;
// IME popups aren't desktop-like elements
// TODO: make them.
CInputPopup* foundPopup = m_sIMERelay.popupFromSurface(foundSurface);
if (foundPopup) {
surfacePos = foundPopup->globalBox().pos();
m_bFocusHeldByButtons = true;
m_bRefocusHeldByButtons = refocus;
} else {
CInputPopup* foundPopup = m_sIMERelay.popupFromSurface(foundSurface);
if (foundPopup) {
surfacePos = foundPopup->globalBox().pos();
m_bFocusHeldByButtons = true;
m_bRefocusHeldByButtons = refocus;
} else if (!g_pCompositor->m_pLastWindow.expired()) {
foundSurface = m_pLastMouseSurface;
pFoundWindow = g_pCompositor->m_pLastWindow.lock();
auto HLSurface = CWLSurface::surfaceFromWlr(foundSurface);
surfaceCoords = g_pCompositor->vectorToSurfaceLocal(mouseCoords, pFoundWindow, foundSurface);
m_bFocusHeldByButtons = true;
m_bRefocusHeldByButtons = refocus;
}
if (HLSurface) {
const auto BOX = HLSurface->getSurfaceBoxGlobal();
if (BOX) {
surfacePos = BOX->pos();
pFoundLayerSurface = HLSurface->getLayer();
pFoundWindow = HLSurface->getWindow();
} else // reset foundSurface, find one normally
foundSurface = nullptr;
} else // reset foundSurface, find one normally
foundSurface = nullptr;
}
}
@ -355,6 +361,26 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0)
g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get());
// grabs
if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(foundSurface)) {
if (m_bHardInput || refocus) {
g_pSeatManager->setGrab(nullptr);
return; // setGrab will refocus
} else {
// we need to grab the last surface.
foundSurface = g_pSeatManager->state.pointerFocus;
auto HLSurface = CWLSurface::surfaceFromWlr(foundSurface);
if (HLSurface) {
const auto BOX = HLSurface->getSurfaceBoxGlobal();
if (BOX.has_value())
surfacePos = BOX->pos();
}
}
}
if (!foundSurface) {
if (!m_bEmptyFocusCursorSet) {
if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) {
@ -371,8 +397,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
m_bEmptyFocusCursorSet = true;
}
wlr_seat_pointer_clear_focus(g_pCompositor->m_sSeat.seat);
m_pLastMouseSurface = nullptr;
g_pSeatManager->setPointerFocus(nullptr, {});
if (refocus || g_pCompositor->m_pLastWindow.expired()) // if we are forcing a refocus, and we don't find a surface, clear the kb focus too!
g_pCompositor->focusWindow(nullptr);
@ -386,10 +411,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (pFoundWindow && !pFoundWindow->m_bIsX11 && surfacePos != Vector2D(-1337, -1337)) {
// calc for oversized windows... fucking bullshit.
wlr_box geom;
wlr_xdg_surface_get_geometry(pFoundWindow->m_uSurface.xdg, &geom);
CBox geom = pFoundWindow->m_pXDGSurface->current.geometry;
surfaceLocal = mouseCoords - surfacePos + Vector2D(geom.x, geom.y);
surfaceLocal = mouseCoords - surfacePos + geom.pos();
}
if (pFoundWindow && pFoundWindow->m_bIsX11) // for x11 force scale zero
@ -412,8 +436,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
}
if (currentlyDraggedWindow.lock() && pFoundWindow != currentlyDraggedWindow) {
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y);
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
return;
}
@ -441,20 +464,18 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
// enter if change floating style
if (FOLLOWMOUSE != 3 && allowKeyboardRefocus)
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
m_pLastMouseSurface = foundSurface;
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
} else if (FOLLOWMOUSE == 2 || FOLLOWMOUSE == 3) {
m_pLastMouseSurface = foundSurface;
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
}
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
} else if (FOLLOWMOUSE == 2 || FOLLOWMOUSE == 3)
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
if (pFoundWindow == g_pCompositor->m_pLastWindow) {
m_pLastMouseSurface = foundSurface;
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
}
if (pFoundWindow == g_pCompositor->m_pLastWindow)
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
if (FOLLOWMOUSE != 0 || pFoundWindow == g_pCompositor->m_pLastWindow)
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y);
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
if (g_pSeatManager->state.pointerFocus == foundSurface)
g_pSeatManager->sendPointerMotion(time, surfaceLocal);
m_bLastFocusOnLS = false;
return; // don't enter any new surfaces
@ -489,9 +510,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
m_bLastFocusOnLS = true;
}
m_pLastMouseSurface = foundSurface;
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y);
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
g_pSeatManager->sendPointerMotion(time, surfaceLocal);
}
void CInputManager::onMouseButton(IPointer::SButtonEvent e) {
@ -526,34 +546,33 @@ void CInputManager::onMouseButton(IPointer::SButtonEvent e) {
}
}
void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_event* e) {
void CInputManager::processMouseRequest(std::any E) {
if (!cursorImageUnlocked())
return;
Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e->surface);
auto e = std::any_cast<CSeatManager::SSetCursorEvent>(E);
if (e->seat_client == g_pCompositor->m_sSeat.seat->pointer_state.focused_client) {
Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf);
if (e->surface != m_sCursorSurfaceInfo.wlSurface.wlr()) {
m_sCursorSurfaceInfo.wlSurface.unassign();
if (e.surf != m_sCursorSurfaceInfo.wlSurface.wlr()) {
m_sCursorSurfaceInfo.wlSurface.unassign();
if (e->surface)
m_sCursorSurfaceInfo.wlSurface.assign(e->surface);
}
if (e->surface) {
m_sCursorSurfaceInfo.vHotspot = {e->hotspot_x, e->hotspot_y};
m_sCursorSurfaceInfo.hidden = false;
} else {
m_sCursorSurfaceInfo.vHotspot = {};
m_sCursorSurfaceInfo.hidden = true;
}
m_sCursorSurfaceInfo.name = "";
m_sCursorSurfaceInfo.inUse = true;
g_pHyprRenderer->setCursorSurface(&m_sCursorSurfaceInfo.wlSurface, e->hotspot_x, e->hotspot_y);
if (e.surf)
m_sCursorSurfaceInfo.wlSurface.assign(e.surf);
}
if (e.surf) {
m_sCursorSurfaceInfo.vHotspot = e.hotspot;
m_sCursorSurfaceInfo.hidden = false;
} else {
m_sCursorSurfaceInfo.vHotspot = {};
m_sCursorSurfaceInfo.hidden = true;
}
m_sCursorSurfaceInfo.name = "";
m_sCursorSurfaceInfo.inUse = true;
g_pHyprRenderer->setCursorSurface(&m_sCursorSurfaceInfo.wlSurface, e.hotspot.x, e.hotspot.y);
}
void CInputManager::restoreCursorIconToApp() {
@ -659,7 +678,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) {
if (*PFOLLOWMOUSE == 3) // don't refocus on full loose
break;
if ((g_pCompositor->m_sSeat.mouse.expired() || !isConstrained()) /* No constraints */
if ((g_pSeatManager->mouse.expired() || !isConstrained()) /* No constraints */
&& (w && g_pCompositor->m_pLastWindow.lock() != w) /* window should change */) {
// a bit hacky
// if we only pressed one button, allow us to refocus. m_lCurrentlyHeldButtons.size() > 0 will stick the focus
@ -681,11 +700,16 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) {
}
// notify app if we didnt handle it
if (g_pCompositor->doesSeatAcceptInput(g_pCompositor->m_pLastFocus))
wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, e.timeMs, e.button, e.state);
g_pSeatManager->sendPointerButton(e.timeMs, e.button, e.state);
if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != g_pCompositor->m_pLastMonitor.get() && PMON)
g_pCompositor->setActiveMonitor(PMON);
if (g_pSeatManager->seatGrab && e.state == WL_POINTER_BUTTON_STATE_PRESSED) {
m_bHardInput = true;
simulateMouseMovement();
m_bHardInput = false;
}
}
void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) {
@ -747,14 +771,13 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) {
if (*POFFWINDOWAXIS == 3)
g_pCompositor->warpCursorTo({TEMPCURX, TEMPCURY}, true);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, e.timeMs, TEMPCURX - BOX.x, TEMPCURY - BOX.y);
wlr_seat_pointer_notify_frame(g_pCompositor->m_sSeat.seat);
g_pSeatManager->sendPointerMotion(e.timeMs, Vector2D{TEMPCURX, TEMPCURY} - BOX.pos());
g_pSeatManager->sendPointerFrame();
}
}
}
wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e.timeMs, e.axis, factor * e.delta, std::round(factor * e.deltaDiscrete), e.source,
WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL);
g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, factor * e.delta, std::round(factor * e.deltaDiscrete / 120), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL);
}
Vector2D CInputManager::getMouseCoordsInternal() {
@ -819,6 +842,9 @@ void CInputManager::setupKeyboard(SP<IKeyboard> keeb) {
auto PKEEB = ((IKeyboard*)owner)->self.lock();
const auto LAYOUT = PKEEB->getActiveLayout();
if (PKEEB == g_pSeatManager->keyboard)
g_pSeatManager->updateActiveKeyboardData();
g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", PKEEB->hlName + "," + LAYOUT});
EMIT_HOOK_EVENT("activeLayout", (std::vector<std::any>{PKEEB, LAYOUT}));
},
@ -826,13 +852,9 @@ void CInputManager::setupKeyboard(SP<IKeyboard> keeb) {
disableAllKeyboards(false);
g_pCompositor->m_sSeat.keyboard = keeb;
keeb->active = true;
applyConfigToKeyboard(keeb);
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, keeb->wlr());
g_pSeatManager->setKeyboard(keeb);
}
void CInputManager::setKeyboardLayout() {
@ -1015,7 +1037,7 @@ void CInputManager::setupMouse(SP<IPointer> mauz) {
},
mauz.get());
g_pCompositor->m_sSeat.mouse = mauz;
g_pSeatManager->setMouse(mauz);
m_tmrLastCursorMovement.reset();
}
@ -1185,12 +1207,11 @@ void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; });
if (m_vKeyboards.size() > 0) {
g_pCompositor->m_sSeat.keyboard = m_vKeyboards.back();
g_pCompositor->m_sSeat.keyboard->active = true;
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, g_pCompositor->m_sSeat.keyboard->wlr());
const auto PNEWKEYBOARD = m_vKeyboards.back();
g_pSeatManager->setKeyboard(PNEWKEYBOARD);
PNEWKEYBOARD->active = true;
} else {
g_pCompositor->m_sSeat.keyboard.reset();
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, nullptr);
g_pSeatManager->setKeyboard(nullptr);
}
removeFromHIDs(pKeyboard);
@ -1199,9 +1220,9 @@ void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
void CInputManager::destroyPointer(SP<IPointer> mouse) {
std::erase_if(m_vPointers, [mouse](const auto& other) { return other == mouse; });
g_pCompositor->m_sSeat.mouse = m_vPointers.size() > 0 ? m_vPointers.front() : nullptr;
g_pSeatManager->setMouse(m_vPointers.size() > 0 ? m_vPointers.front() : nullptr);
if (!g_pCompositor->m_sSeat.mouse.expired())
if (!g_pSeatManager->mouse.expired())
unconstrainMouse();
removeFromHIDs(mouse);
@ -1239,6 +1260,26 @@ void CInputManager::destroyTabletPad(SP<CTabletPad> pad) {
removeFromHIDs(pad);
}
void CInputManager::updateKeyboardsLeds(SP<IKeyboard> pKeyboard) {
if (!pKeyboard)
return;
auto keyboard = pKeyboard->wlr();
if (!keyboard || keyboard->xkb_state == nullptr)
return;
uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->led_indexes[i]))
leds |= (1 << i);
}
for (auto& k : m_vKeyboards) {
k->updateLEDs(leds);
}
}
void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
if (!pKeyboard->enabled)
return;
@ -1267,13 +1308,11 @@ void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
IME->setKeyboard(pKeyboard->wlr());
IME->sendKey(e.timeMs, e.keycode, e.state);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, pKeyboard->wlr());
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, e.timeMs, e.keycode, e.state);
g_pSeatManager->setKeyboard(pKeyboard);
g_pSeatManager->sendKeyboardKey(e.timeMs, e.keycode, e.state);
}
for (auto& k : m_vKeyboards) {
k->updateLEDs();
}
updateKeyboardsLeds(pKeyboard);
}
}
@ -1295,13 +1334,11 @@ void CInputManager::onKeyboardMod(SP<IKeyboard> pKeyboard) {
IME->setKeyboard(PWLRKB);
IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, PWLRKB);
wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &MODS);
g_pSeatManager->setKeyboard(pKeyboard);
g_pSeatManager->sendKeyboardMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
}
for (auto& k : m_vKeyboards) {
k->updateLEDs();
}
updateKeyboardsLeds(pKeyboard);
if (PWLRKB->modifiers.group != pKeyboard->activeLayout) {
pKeyboard->activeLayout = PWLRKB->modifiers.group;
@ -1328,24 +1365,8 @@ void CInputManager::refocus() {
mouseMoveUnified(0, true);
}
void CInputManager::updateDragIcon() {
if (!m_sDrag.dragIcon)
return;
switch (m_sDrag.dragIcon->drag->grab_type) {
case WLR_DRAG_GRAB_KEYBOARD: break;
case WLR_DRAG_GRAB_KEYBOARD_POINTER: {
CBox box = {m_sDrag.pos.x - 2, m_sDrag.pos.y - 2, m_sDrag.dragIcon->surface->current.width + 4, m_sDrag.dragIcon->surface->current.height + 4};
g_pHyprRenderer->damageBox(&box);
m_sDrag.pos = getMouseCoordsInternal();
break;
}
default: break;
}
}
void CInputManager::unconstrainMouse() {
if (g_pCompositor->m_sSeat.mouse.expired())
if (g_pSeatManager->mouse.expired())
return;
for (auto& c : m_vConstraints) {
@ -1394,7 +1415,7 @@ void CInputManager::updateCapabilities() {
caps |= WL_SEAT_CAPABILITY_TOUCH;
}
wlr_seat_set_capabilities(g_pCompositor->m_sSeat.seat, caps);
g_pSeatManager->updateCapabilities(caps);
m_uiCapabilities = caps;
}
@ -1406,7 +1427,7 @@ uint32_t CInputManager::accumulateModsFromAllKBs() {
if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb))
continue;
if (!kb->enabled)
if (!kb->enabled || !kb->wlr())
continue;
finalMask |= wlr_keyboard_get_modifiers(kb->wlr());
@ -1637,11 +1658,11 @@ std::string CInputManager::getNameForNewDevice(std::string internalName) {
void CInputManager::releaseAllMouseButtons() {
const auto buttonsCopy = m_lCurrentlyHeldButtons;
if (g_pInputManager->m_sDrag.drag)
if (PROTO::data->dndActive())
return;
for (auto& mb : buttonsCopy) {
wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, 0, mb, WL_POINTER_BUTTON_STATE_RELEASED);
g_pSeatManager->sendPointerButton(0, mb, WL_POINTER_BUTTON_STATE_RELEASED);
}
m_lCurrentlyHeldButtons.clear();

View File

@ -112,12 +112,12 @@ class CInputManager {
void setTouchDeviceConfigs(SP<ITouch> dev = nullptr);
void setTabletConfigs();
void updateDragIcon();
void updateCapabilities();
void updateKeyboardsLeds(SP<IKeyboard>);
void setClickMode(eClickBehaviorMode);
eClickBehaviorMode getClickMode();
void processMouseRequest(wlr_seat_pointer_request_set_cursor_event* e);
void processMouseRequest(std::any e);
void onTouchDown(ITouch::SDownEvent);
void onTouchUp(ITouch::SUpEvent);
@ -142,8 +142,6 @@ class CInputManager {
// for refocus to be forced
PHLWINDOWREF m_pForcedFocus;
SDrag m_sDrag;
std::vector<SP<IKeyboard>> m_vKeyboards;
std::vector<SP<IPointer>> m_vPointers;
std::vector<SP<ITouch>> m_vTouches;
@ -187,16 +185,17 @@ class CInputManager {
void releaseAllMouseButtons();
// for some bugs in follow mouse 0
bool m_bLastFocusOnLS = false;
bool m_bLastFocusOnLS = false;
bool m_bLastFocusOnIMEPopup = false;
// for hard input e.g. clicks
bool m_bHardInput = false;
// for hiding cursor on touch
bool m_bLastInputTouch = false;
// for tracking mouse refocus
PHLWINDOWREF m_pLastMouseFocus;
wlr_surface* m_pLastMouseSurface = nullptr;
//
bool m_bEmptyFocusCursorSet = false;
@ -208,6 +207,7 @@ class CInputManager {
CHyprSignalListener newIdleInhibitor;
CHyprSignalListener newVirtualKeyboard;
CHyprSignalListener newVirtualMouse;
CHyprSignalListener setCursor;
} m_sListeners;
bool m_bCursorImageOverridden = false;

View File

@ -4,6 +4,7 @@
#include "../../protocols/Tablet.hpp"
#include "../../devices/Tablet.hpp"
#include "../../managers/PointerManager.hpp"
#include "../../managers/SeatManager.hpp"
#include "../../protocols/PointerConstraints.hpp"
static void unfocusTool(SP<CTabletTool> tool) {
@ -36,7 +37,7 @@ static void focusTool(SP<CTabletTool> tool, SP<CTablet> tablet, wlr_surface* sur
}
static void refocusTablet(SP<CTablet> tab, SP<CTabletTool> tool, bool motion = false) {
const auto LASTHLSURFACE = CWLSurface::surfaceFromWlr(g_pInputManager->m_pLastMouseSurface);
const auto LASTHLSURFACE = CWLSurface::surfaceFromWlr(g_pSeatManager->state.pointerFocus);
if (!LASTHLSURFACE || !tool->active) {
if (tool->getSurface())
@ -56,7 +57,7 @@ static void refocusTablet(SP<CTablet> tab, SP<CTabletTool> tool, bool motion = f
const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal();
focusTool(tool, tab, g_pInputManager->m_pLastMouseSurface);
focusTool(tool, tab, g_pSeatManager->state.pointerFocus);
if (!motion)
return;

View File

@ -3,6 +3,7 @@
#include "../../config/ConfigValue.hpp"
#include "../../protocols/IdleNotify.hpp"
#include "../../devices/ITouch.hpp"
#include "../SeatManager.hpp"
void CInputManager::onTouchDown(ITouch::SDownEvent e) {
static auto PSWIPETOUCH = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_touch");
@ -76,7 +77,7 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) {
} else
return; // oops, nothing found.
wlr_seat_touch_notify_down(g_pCompositor->m_sSeat.seat, m_sTouchData.touchFocusSurface, e.timeMs, e.touchID, local.x, local.y);
g_pSeatManager->sendTouchDown(m_sTouchData.touchFocusSurface, e.timeMs, e.touchID, local);
PROTO::idle->onActivity();
}
@ -90,9 +91,8 @@ void CInputManager::onTouchUp(ITouch::SUpEvent e) {
return;
}
if (m_sTouchData.touchFocusSurface) {
wlr_seat_touch_notify_up(g_pCompositor->m_sSeat.seat, e.timeMs, e.touchID);
}
if (m_sTouchData.touchFocusSurface)
g_pSeatManager->sendTouchUp(e.timeMs, e.touchID);
}
void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
@ -131,8 +131,7 @@ void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
if (m_sTouchData.touchFocusWindow->m_bIsX11)
local = local * m_sTouchData.touchFocusWindow->m_fX11SurfaceScaledBy;
wlr_seat_touch_notify_motion(g_pCompositor->m_sSeat.seat, e.timeMs, e.touchID, local.x, local.y);
// wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, e->time_msec, local.x, local.y);
g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, local);
} else if (!m_sTouchData.touchFocusLS.expired()) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_sTouchData.touchFocusLS->monitorID);
@ -140,7 +139,6 @@ void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
const auto local = g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchSurfaceOrigin;
wlr_seat_touch_notify_motion(g_pCompositor->m_sSeat.seat, e.timeMs, e.touchID, local.x, local.y);
// wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, e->time_msec, local.x, local.y);
g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, local);
}
}

View File

@ -0,0 +1,326 @@
#include "DataDeviceWlr.hpp"
#include <algorithm>
#include "../managers/SeatManager.hpp"
#include "core/Seat.hpp"
#define LOGM PROTO::dataWlr->protoLog
CWLRDataOffer::CWLRDataOffer(SP<CZwlrDataControlOfferV1> resource_, SP<IDataSource> source_) : source(source_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); });
resource->setOnDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); });
resource->setReceive([this](CZwlrDataControlOfferV1* r, const char* mime, int32_t fd) {
if (!source) {
LOGM(WARN, "Possible bug: Receive on an offer w/o a source");
close(fd);
return;
}
if (dead) {
LOGM(WARN, "Possible bug: Receive on an offer that's dead");
close(fd);
return;
}
LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get());
source->send(mime, fd);
});
}
bool CWLRDataOffer::good() {
return resource->resource();
}
void CWLRDataOffer::sendData() {
if (!source)
return;
for (auto& m : source->mimes()) {
resource->sendOffer(m.c_str());
}
}
CWLRDataSource::CWLRDataSource(SP<CZwlrDataControlSourceV1> resource_, SP<CWLRDataDevice> device_) : device(device_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CZwlrDataControlSourceV1* r) {
events.destroy.emit();
PROTO::dataWlr->destroyResource(this);
});
resource->setOnDestroy([this](CZwlrDataControlSourceV1* r) {
events.destroy.emit();
PROTO::dataWlr->destroyResource(this);
});
resource->setOffer([this](CZwlrDataControlSourceV1* r, const char* mime) { mimeTypes.push_back(mime); });
}
CWLRDataSource::~CWLRDataSource() {
events.destroy.emit();
}
SP<CWLRDataSource> CWLRDataSource::fromResource(wl_resource* res) {
auto data = (CWLRDataSource*)(((CZwlrDataControlSourceV1*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CWLRDataSource::good() {
return resource->resource();
}
std::vector<std::string> CWLRDataSource::mimes() {
return mimeTypes;
}
void CWLRDataSource::send(const std::string& mime, uint32_t fd) {
if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) {
LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAskSend with non-existent mime");
close(fd);
return;
}
resource->sendSend(mime.c_str(), fd);
close(fd);
}
void CWLRDataSource::accepted(const std::string& mime) {
if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end())
LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAccepted with non-existent mime");
// wlr has no accepted
}
void CWLRDataSource::cancelled() {
resource->sendCancelled();
}
void CWLRDataSource::error(uint32_t code, const std::string& msg) {
resource->error(code, msg);
}
CWLRDataDevice::CWLRDataDevice(SP<CZwlrDataControlDeviceV1> resource_) : resource(resource_) {
if (!good())
return;
pClient = resource->client();
resource->setDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); });
resource->setOnDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); });
resource->setSetSelection([this](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) {
auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer<CWLRDataSource>{};
if (!source) {
LOGM(LOG, "wlr reset selection received");
g_pSeatManager->setCurrentSelection(nullptr);
return;
}
if (source && source->used())
LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
source->markUsed();
LOGM(LOG, "wlr manager requests selection to {:x}", (uintptr_t)source.get());
g_pSeatManager->setCurrentSelection(source);
});
resource->setSetPrimarySelection([this](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) {
auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer<CWLRDataSource>{};
if (!source) {
LOGM(LOG, "wlr reset primary selection received");
g_pSeatManager->setCurrentSelection(nullptr);
return;
}
if (source && source->used())
LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
source->markUsed();
LOGM(LOG, "wlr manager requests primary selection to {:x}", (uintptr_t)source.get());
g_pSeatManager->setCurrentPrimarySelection(source);
});
}
bool CWLRDataDevice::good() {
return resource->resource();
}
wl_client* CWLRDataDevice::client() {
return pClient;
}
void CWLRDataDevice::sendInitialSelections() {
PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentSelection.lock(), false);
PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentPrimarySelection.lock(), true);
}
void CWLRDataDevice::sendDataOffer(SP<CWLRDataOffer> offer) {
resource->sendDataOffer(offer->resource.get());
}
void CWLRDataDevice::sendSelection(SP<CWLRDataOffer> selection) {
resource->sendSelection(selection->resource.get());
}
void CWLRDataDevice::sendPrimarySelection(SP<CWLRDataOffer> selection) {
resource->sendPrimarySelection(selection->resource.get());
}
CWLRDataControlManagerResource::CWLRDataControlManagerResource(SP<CZwlrDataControlManagerV1> resource_) : resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); });
resource->setOnDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); });
resource->setGetDataDevice([this](CZwlrDataControlManagerV1* r, uint32_t id, wl_resource* seat) {
const auto RESOURCE = PROTO::dataWlr->m_vDevices.emplace_back(makeShared<CWLRDataDevice>(makeShared<CZwlrDataControlDeviceV1>(r->client(), r->version(), id)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::dataWlr->m_vDevices.pop_back();
return;
}
RESOURCE->self = RESOURCE;
device = RESOURCE;
for (auto& s : sources) {
if (!s)
continue;
s->device = RESOURCE;
}
RESOURCE->sendInitialSelections();
LOGM(LOG, "New wlr data device bound at {:x}", (uintptr_t)RESOURCE.get());
});
resource->setCreateDataSource([this](CZwlrDataControlManagerV1* r, uint32_t id) {
std::erase_if(sources, [](const auto& e) { return e.expired(); });
const auto RESOURCE =
PROTO::dataWlr->m_vSources.emplace_back(makeShared<CWLRDataSource>(makeShared<CZwlrDataControlSourceV1>(r->client(), r->version(), id), device.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::dataWlr->m_vSources.pop_back();
return;
}
if (!device)
LOGM(WARN, "New data source before a device was created");
RESOURCE->self = RESOURCE;
sources.push_back(RESOURCE);
LOGM(LOG, "New wlr data source bound at {:x}", (uintptr_t)RESOURCE.get());
});
}
bool CWLRDataControlManagerResource::good() {
return resource->resource();
}
CDataDeviceWLRProtocol::CDataDeviceWLRProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CDataDeviceWLRProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CWLRDataControlManagerResource>(makeShared<CZwlrDataControlManagerV1>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
LOGM(LOG, "New wlr_data_control_manager at {:x}", (uintptr_t)RESOURCE.get());
}
void CDataDeviceWLRProtocol::destroyResource(CWLRDataControlManagerResource* resource) {
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
}
void CDataDeviceWLRProtocol::destroyResource(CWLRDataSource* resource) {
std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; });
}
void CDataDeviceWLRProtocol::destroyResource(CWLRDataDevice* resource) {
std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; });
}
void CDataDeviceWLRProtocol::destroyResource(CWLRDataOffer* resource) {
std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; });
}
void CDataDeviceWLRProtocol::sendSelectionToDevice(SP<CWLRDataDevice> dev, SP<IDataSource> sel, bool primary) {
if (!sel) {
if (primary)
dev->resource->sendPrimarySelectionRaw(nullptr);
else
dev->resource->sendSelectionRaw(nullptr);
return;
}
const auto OFFER = m_vOffers.emplace_back(makeShared<CWLRDataOffer>(makeShared<CZwlrDataControlOfferV1>(dev->resource->client(), dev->resource->version(), 0), sel));
if (!OFFER->good()) {
dev->resource->noMemory();
m_vOffers.pop_back();
return;
}
OFFER->primary = primary;
LOGM(LOG, "New {}offer {:x} for data source {:x}", primary ? "primary " : " ", (uintptr_t)OFFER.get(), (uintptr_t)sel.get());
dev->sendDataOffer(OFFER);
OFFER->sendData();
if (primary)
dev->sendPrimarySelection(OFFER);
else
dev->sendSelection(OFFER);
}
void CDataDeviceWLRProtocol::setSelection(SP<IDataSource> source, bool primary) {
for (auto& o : m_vOffers) {
if (o->source && o->source->hasDnd())
continue;
if (o->primary != primary)
continue;
o->dead = true;
}
if (!source) {
LOGM(LOG, "resetting {}selection", primary ? "primary " : " ");
for (auto& d : m_vDevices) {
sendSelectionToDevice(d, nullptr, primary);
}
return;
}
LOGM(LOG, "New {}selection for data source {:x}", primary ? "primary" : "", (uintptr_t)source.get());
for (auto& d : m_vDevices) {
sendSelectionToDevice(d, source, primary);
}
}
SP<CWLRDataDevice> CDataDeviceWLRProtocol::dataDeviceForClient(wl_client* c) {
auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; });
if (it == m_vDevices.end())
return nullptr;
return *it;
}

View File

@ -0,0 +1,123 @@
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "wlr-data-control-unstable-v1.hpp"
#include "types/DataDevice.hpp"
class CWLRDataControlManagerResource;
class CWLRDataSource;
class CWLRDataDevice;
class CWLRDataOffer;
class CWLRDataOffer {
public:
CWLRDataOffer(SP<CZwlrDataControlOfferV1> resource_, SP<IDataSource> source);
bool good();
void sendData();
bool dead = false;
bool primary = false;
WP<IDataSource> source;
private:
SP<CZwlrDataControlOfferV1> resource;
friend class CWLRDataDevice;
};
class CWLRDataSource : public IDataSource {
public:
CWLRDataSource(SP<CZwlrDataControlSourceV1> resource_, SP<CWLRDataDevice> device_);
~CWLRDataSource();
static SP<CWLRDataSource> fromResource(wl_resource*);
bool good();
virtual std::vector<std::string> mimes();
virtual void send(const std::string& mime, uint32_t fd);
virtual void accepted(const std::string& mime);
virtual void cancelled();
virtual void error(uint32_t code, const std::string& msg);
std::vector<std::string> mimeTypes;
WP<CWLRDataSource> self;
WP<CWLRDataDevice> device;
private:
SP<CZwlrDataControlSourceV1> resource;
};
class CWLRDataDevice {
public:
CWLRDataDevice(SP<CZwlrDataControlDeviceV1> resource_);
bool good();
wl_client* client();
void sendInitialSelections();
void sendDataOffer(SP<CWLRDataOffer> offer);
void sendSelection(SP<CWLRDataOffer> selection);
void sendPrimarySelection(SP<CWLRDataOffer> selection);
WP<CWLRDataDevice> self;
private:
SP<CZwlrDataControlDeviceV1> resource;
wl_client* pClient = nullptr;
friend class CDataDeviceWLRProtocol;
};
class CWLRDataControlManagerResource {
public:
CWLRDataControlManagerResource(SP<CZwlrDataControlManagerV1> resource_);
bool good();
WP<CWLRDataDevice> device;
std::vector<WP<CWLRDataSource>> sources;
private:
SP<CZwlrDataControlManagerV1> resource;
};
class CDataDeviceWLRProtocol : public IWaylandProtocol {
public:
CDataDeviceWLRProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
private:
void destroyResource(CWLRDataControlManagerResource* resource);
void destroyResource(CWLRDataSource* resource);
void destroyResource(CWLRDataDevice* resource);
void destroyResource(CWLRDataOffer* resource);
//
std::vector<SP<CWLRDataControlManagerResource>> m_vManagers;
std::vector<SP<CWLRDataSource>> m_vSources;
std::vector<SP<CWLRDataDevice>> m_vDevices;
std::vector<SP<CWLRDataOffer>> m_vOffers;
//
void setSelection(SP<IDataSource> source, bool primary);
void sendSelectionToDevice(SP<CWLRDataDevice> dev, SP<IDataSource> sel, bool primary);
//
SP<CWLRDataDevice> dataDeviceForClient(wl_client*);
friend class CSeatManager;
friend class CWLRDataControlManagerResource;
friend class CWLRDataSource;
friend class CWLRDataDevice;
friend class CWLRDataOffer;
};
namespace PROTO {
inline UP<CDataDeviceWLRProtocol> dataWlr;
};

View File

@ -1,124 +1,13 @@
#include "FocusGrab.hpp"
#include "Compositor.hpp"
#include <hyprland-focus-grab-v1.hpp>
#include <managers/input/InputManager.hpp>
#include "../managers/input/InputManager.hpp"
#include "../managers/SeatManager.hpp"
#include <cstdint>
#include <memory>
#include <wayland-server.h>
static void focus_grab_pointer_enter(wlr_seat_pointer_grab* grab, wlr_surface* surface, double sx, double sy) {
if (static_cast<CFocusGrab*>(grab->data)->isSurfaceComitted(surface))
wlr_seat_pointer_enter(grab->seat, surface, sx, sy);
else
wlr_seat_pointer_clear_focus(grab->seat);
}
static void focus_grab_pointer_clear_focus(wlr_seat_pointer_grab* grab) {
wlr_seat_pointer_clear_focus(grab->seat);
}
static void focus_grab_pointer_motion(wlr_seat_pointer_grab* grab, uint32_t time, double sx, double sy) {
wlr_seat_pointer_send_motion(grab->seat, time, sx, sy);
}
static uint32_t focus_grab_pointer_button(wlr_seat_pointer_grab* grab, uint32_t time, uint32_t button, wl_pointer_button_state state) {
uint32_t serial = wlr_seat_pointer_send_button(grab->seat, time, button, state);
if (serial)
return serial;
else {
static_cast<CFocusGrab*>(grab->data)->finish(true);
return 0;
}
}
static void focus_grab_pointer_axis(wlr_seat_pointer_grab* grab, uint32_t time, enum wl_pointer_axis orientation, double value, int32_t value_discrete,
enum wl_pointer_axis_source source, enum wl_pointer_axis_relative_direction relative_direction) {
wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, value_discrete, source, relative_direction);
}
static void focus_grab_pointer_frame(wlr_seat_pointer_grab* grab) {
wlr_seat_pointer_send_frame(grab->seat);
}
static void focus_grab_pointer_cancel(wlr_seat_pointer_grab* grab) {
static_cast<CFocusGrab*>(grab->data)->finish(true);
}
static const wlr_pointer_grab_interface focus_grab_pointer_impl = {
.enter = focus_grab_pointer_enter,
.clear_focus = focus_grab_pointer_clear_focus,
.motion = focus_grab_pointer_motion,
.button = focus_grab_pointer_button,
.axis = focus_grab_pointer_axis,
.frame = focus_grab_pointer_frame,
.cancel = focus_grab_pointer_cancel,
};
static void focus_grab_keyboard_enter(wlr_seat_keyboard_grab* grab, wlr_surface* surface, const uint32_t keycodes[], size_t num_keycodes, const wlr_keyboard_modifiers* modifiers) {
if (static_cast<CFocusGrab*>(grab->data)->isSurfaceComitted(surface))
wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers);
// otherwise the last grabbed window should retain keyboard focus.
}
static void focus_grab_keyboard_clear_focus(wlr_seat_keyboard_grab* grab) {
static_cast<CFocusGrab*>(grab->data)->finish(true);
}
static void focus_grab_keyboard_key(wlr_seat_keyboard_grab* grab, uint32_t time, uint32_t key, uint32_t state) {
wlr_seat_keyboard_send_key(grab->seat, time, key, state);
}
static void focus_grab_keyboard_modifiers(wlr_seat_keyboard_grab* grab, const wlr_keyboard_modifiers* modifiers) {
wlr_seat_keyboard_send_modifiers(grab->seat, modifiers);
}
static void focus_grab_keyboard_cancel(wlr_seat_keyboard_grab* grab) {
static_cast<CFocusGrab*>(grab->data)->finish(true);
}
static const wlr_keyboard_grab_interface focus_grab_keyboard_impl = {
.enter = focus_grab_keyboard_enter,
.clear_focus = focus_grab_keyboard_clear_focus,
.key = focus_grab_keyboard_key,
.modifiers = focus_grab_keyboard_modifiers,
.cancel = focus_grab_keyboard_cancel,
};
static uint32_t focus_grab_touch_down(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {
if (!static_cast<CFocusGrab*>(grab->data)->isSurfaceComitted(point->surface))
return 0;
return wlr_seat_touch_send_down(grab->seat, point->surface, time, point->touch_id, point->sx, point->sy);
}
static void focus_grab_touch_up(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {
wlr_seat_touch_send_up(grab->seat, time, point->touch_id);
}
static void focus_grab_touch_motion(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {
wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, point->sy);
}
static void focus_grab_touch_enter(wlr_seat_touch_grab* grab, uint32_t time, wlr_touch_point* point) {}
static void focus_grab_touch_frame(wlr_seat_touch_grab* grab) {
wlr_seat_touch_send_frame(grab->seat);
}
static void focus_grab_touch_cancel(wlr_seat_touch_grab* grab) {
static_cast<CFocusGrab*>(grab->data)->finish(true);
}
static const wlr_touch_grab_interface focus_grab_touch_impl = {
.down = focus_grab_touch_down,
.up = focus_grab_touch_up,
.motion = focus_grab_touch_motion,
.enter = focus_grab_touch_enter,
.frame = focus_grab_touch_frame,
.cancel = focus_grab_touch_cancel,
};
#define LOGM PROTO::focusGrab->protoLog
CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface) {
hyprListener_surfaceDestroy.initCallback(
@ -133,14 +22,10 @@ CFocusGrab::CFocusGrab(SP<CHyprlandFocusGrabV1> resource_) : resource(resource_)
if (!resource->resource())
return;
m_sPointerGrab.interface = &focus_grab_pointer_impl;
m_sPointerGrab.data = this;
m_sKeyboardGrab.interface = &focus_grab_keyboard_impl;
m_sKeyboardGrab.data = this;
m_sTouchGrab.interface = &focus_grab_touch_impl;
m_sTouchGrab.data = this;
grab = makeShared<CSeatGrab>();
grab->keyboard = true;
grab->pointer = true;
grab->setCallback([this]() { finish(true); });
resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); });
resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); });
@ -167,21 +52,8 @@ bool CFocusGrab::isSurfaceComitted(wlr_surface* surface) {
void CFocusGrab::start() {
if (!m_bGrabActive) {
wlr_seat_pointer_start_grab(g_pCompositor->m_sSeat.seat, &m_sPointerGrab);
wlr_seat_keyboard_start_grab(g_pCompositor->m_sSeat.seat, &m_sKeyboardGrab);
wlr_seat_touch_start_grab(g_pCompositor->m_sSeat.seat, &m_sTouchGrab);
m_bGrabActive = true;
// Ensure the grab ends if another grab begins, including from xdg_popup::grab.
hyprListener_pointerGrabStarted.initCallback(
&g_pCompositor->m_sSeat.seat->events.pointer_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab");
hyprListener_keyboardGrabStarted.initCallback(
&g_pCompositor->m_sSeat.seat->events.keyboard_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab");
hyprListener_touchGrabStarted.initCallback(
&g_pCompositor->m_sSeat.seat->events.touch_grab_begin, [this](void*, void*) { finish(true); }, this, "CFocusGrab");
g_pSeatManager->setGrab(grab);
}
// Ensure new surfaces are focused if under the mouse when comitted.
@ -192,63 +64,43 @@ void CFocusGrab::start() {
void CFocusGrab::finish(bool sendCleared) {
if (m_bGrabActive) {
m_bGrabActive = false;
hyprListener_pointerGrabStarted.removeCallback();
hyprListener_keyboardGrabStarted.removeCallback();
hyprListener_touchGrabStarted.removeCallback();
// Only clear grabs that belong to this focus grab. When superseded by another grab
// or xdg_popup grab we might not own the current grab.
bool hadGrab = false;
if (g_pCompositor->m_sSeat.seat->pointer_state.grab == &m_sPointerGrab) {
wlr_seat_pointer_end_grab(g_pCompositor->m_sSeat.seat);
hadGrab = true;
}
if (g_pCompositor->m_sSeat.seat->keyboard_state.grab == &m_sKeyboardGrab) {
wlr_seat_keyboard_end_grab(g_pCompositor->m_sSeat.seat);
hadGrab = true;
}
if (g_pCompositor->m_sSeat.seat->touch_state.grab == &m_sTouchGrab) {
wlr_seat_touch_end_grab(g_pCompositor->m_sSeat.seat);
hadGrab = true;
if (g_pSeatManager->seatGrab == grab) {
g_pSeatManager->setGrab(nullptr);
}
grab->clear();
m_mSurfaces.clear();
if (sendCleared)
resource->sendCleared();
// Ensure surfaces under the mouse when the grab ends get focus.
if (hadGrab)
g_pInputManager->refocus();
}
}
void CFocusGrab::addSurface(wlr_surface* surface) {
auto iter = m_mSurfaces.find(surface);
if (iter == m_mSurfaces.end())
if (iter == m_mSurfaces.end()) {
m_mSurfaces.emplace(surface, std::make_unique<CFocusGrabSurfaceState>(this, surface));
}
}
void CFocusGrab::removeSurface(wlr_surface* surface) {
auto iter = m_mSurfaces.find(surface);
if (iter != m_mSurfaces.end()) {
if (iter->second->state == CFocusGrabSurfaceState::PendingAddition)
if (iter->second->state == CFocusGrabSurfaceState::PendingAddition) {
m_mSurfaces.erase(iter);
else
} else
iter->second->state = CFocusGrabSurfaceState::PendingRemoval;
}
}
void CFocusGrab::eraseSurface(wlr_surface* surface) {
removeSurface(surface);
commit();
commit(true);
}
void CFocusGrab::refocusKeyboard() {
auto keyboardSurface = g_pCompositor->m_sSeat.seat->keyboard_state.focused_surface;
auto keyboardSurface = g_pSeatManager->state.keyboardFocus;
if (keyboardSurface != nullptr && isSurfaceComitted(keyboardSurface))
return;
@ -263,22 +115,26 @@ void CFocusGrab::refocusKeyboard() {
if (surface)
g_pCompositor->focusSurface(surface);
else
Debug::log(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen.");
LOGM(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen.");
}
void CFocusGrab::commit() {
void CFocusGrab::commit(bool removeOnly) {
auto surfacesChanged = false;
auto anyComitted = false;
for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) {
switch (iter->second->state) {
case CFocusGrabSurfaceState::PendingRemoval:
grab->remove(iter->first);
iter = m_mSurfaces.erase(iter);
surfacesChanged = true;
continue;
case CFocusGrabSurfaceState::PendingAddition:
iter->second->state = CFocusGrabSurfaceState::Comitted;
surfacesChanged = true;
anyComitted = true;
if (!removeOnly) {
iter->second->state = CFocusGrabSurfaceState::Comitted;
grab->add(iter->first);
surfacesChanged = true;
anyComitted = true;
}
break;
case CFocusGrabSurfaceState::Comitted: anyComitted = true; break;
}

View File

@ -8,6 +8,7 @@
#include <vector>
class CFocusGrab;
class CSeatGrab;
class CFocusGrabSurfaceState {
public:
@ -40,13 +41,12 @@ class CFocusGrab {
void removeSurface(wlr_surface* surface);
void eraseSurface(wlr_surface* surface);
void refocusKeyboard();
void commit();
void commit(bool removeOnly = false);
SP<CHyprlandFocusGrabV1> resource;
std::unordered_map<wlr_surface*, UP<CFocusGrabSurfaceState>> m_mSurfaces;
wlr_seat_pointer_grab m_sPointerGrab;
wlr_seat_keyboard_grab m_sKeyboardGrab;
wlr_seat_touch_grab m_sTouchGrab;
SP<CSeatGrab> grab;
bool m_bGrabActive = false;
DYNLISTENER(pointerGrabStarted);

View File

@ -91,7 +91,7 @@ void CForeignToplevelList::onClass(PHLWINDOW pWindow) {
if (!H || H->closed)
return;
H->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str());
H->resource->sendAppId(pWindow->m_szClass.c_str());
}
void CForeignToplevelList::onUnmap(PHLWINDOW pWindow) {

View File

@ -195,7 +195,7 @@ void CForeignToplevelWlrManager::onMap(PHLWINDOW pWindow) {
LOGM(LOG, "Newly mapped window {:016x}", (uintptr_t)pWindow.get());
resource->sendToplevel(NEWHANDLE->resource.get());
NEWHANDLE->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str());
NEWHANDLE->resource->sendAppId(pWindow->m_szClass.c_str());
NEWHANDLE->resource->sendTitle(pWindow->m_szTitle.c_str());
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); PMONITOR)
NEWHANDLE->sendMonitor(PMONITOR);
@ -231,7 +231,7 @@ void CForeignToplevelWlrManager::onClass(PHLWINDOW pWindow) {
if (!H || H->closed)
return;
H->resource->sendAppId(g_pXWaylandManager->getAppIDClass(pWindow).c_str());
H->resource->sendAppId(pWindow->m_szClass.c_str());
H->resource->sendDone();
}

View File

@ -1,5 +1,7 @@
#include "InputMethodV2.hpp"
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "../devices/IKeyboard.hpp"
#include <sys/mman.h>
#define LOGM PROTO::ime->protoLog
@ -11,14 +13,12 @@ CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboar
resource->setRelease([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); });
resource->setOnDestroy([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); });
const auto PKEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat);
if (!PKEYBOARD) {
if (!g_pSeatManager->keyboard) {
LOGM(ERR, "IME called but no active keyboard???");
return;
}
sendKeyboardData(PKEYBOARD);
sendKeyboardData(g_pSeatManager->keyboard->wlr());
}
CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
@ -53,19 +53,20 @@ void CInputMethodKeyboardGrabV2::sendKeyboardData(wlr_keyboard* keyboard) {
close(keymapFD);
sendMods(0, 0, 0, 0);
const auto MODS = keyboard->modifiers;
sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
resource->sendRepeatInfo(keyboard->repeat_info.rate, keyboard->repeat_info.delay);
}
void CInputMethodKeyboardGrabV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) {
const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, resource->client()));
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client()));
resource->sendKey(SERIAL, time, key, (uint32_t)state);
}
void CInputMethodKeyboardGrabV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, resource->client()));
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client()));
resource->sendModifiers(SERIAL, depressed, latched, locked, group);
}

View File

@ -1,5 +1,6 @@
#include "LayerShell.hpp"
#include "../Compositor.hpp"
#include "XDGShell.hpp"
#define LOGM PROTO::layerShell->protoLog
@ -118,14 +119,14 @@ CLayerShellResource::CLayerShellResource(SP<CZwlrLayerSurfaceV1> resource_, wlr_
});
resource->setGetPopup([this](CZwlrLayerSurfaceV1* r, wl_resource* popup_) {
auto popup = wlr_xdg_popup_from_resource(popup_);
auto popup = CXDGPopupResource::fromResource(popup_);
if (popup->parent) {
if (popup->taken) {
r->error(-1, "Parent already exists!");
return;
}
popup->parent = surface;
popup->taken = true;
events.newPopup.emit(popup);
});

View File

@ -2,6 +2,7 @@
#include "../desktop/WLSurface.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include "../managers/SeatManager.hpp"
#define LOGM PROTO::constraints->protoLog
@ -125,10 +126,10 @@ void CPointerConstraint::activate() {
return;
// TODO: hack, probably not a super duper great idea
if (g_pCompositor->m_sSeat.seat->pointer_state.focused_surface != pHLSurface->wlr()) {
if (g_pSeatManager->state.pointerFocus != pHLSurface->wlr()) {
const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal();
const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, pHLSurface->wlr(), LOCAL.x, LOCAL.y);
g_pSeatManager->setPointerFocus(pHLSurface->wlr(), LOCAL);
}
if (locked)

View File

@ -1,5 +1,7 @@
#include "PointerGestures.hpp"
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "core/Seat.hpp"
#define LOGM PROTO::pointerGestures->protoLog
@ -103,26 +105,26 @@ void CPointerGesturesProtocol::onGetHoldGesture(CZwpPointerGesturesV1* pMgr, uin
}
void CPointerGesturesProtocol::swipeBegin(uint32_t timeMs, uint32_t fingers) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client);
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock());
for (auto& sw : m_vSwipes) {
if (sw->resource->client() != FOCUSEDCLIENT)
continue;
sw->resource->sendBegin(SERIAL, timeMs, g_pCompositor->m_sSeat.seat->pointer_state.focused_surface->resource, fingers);
sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers);
}
}
void CPointerGesturesProtocol::swipeUpdate(uint32_t timeMs, const Vector2D& delta) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
for (auto& sw : m_vSwipes) {
if (sw->resource->client() != FOCUSEDCLIENT)
@ -133,12 +135,12 @@ void CPointerGesturesProtocol::swipeUpdate(uint32_t timeMs, const Vector2D& delt
}
void CPointerGesturesProtocol::swipeEnd(uint32_t timeMs, bool cancelled) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client);
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock());
for (auto& sw : m_vSwipes) {
if (sw->resource->client() != FOCUSEDCLIENT)
@ -149,26 +151,26 @@ void CPointerGesturesProtocol::swipeEnd(uint32_t timeMs, bool cancelled) {
}
void CPointerGesturesProtocol::pinchBegin(uint32_t timeMs, uint32_t fingers) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client);
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock());
for (auto& sw : m_vPinches) {
if (sw->resource->client() != FOCUSEDCLIENT)
continue;
sw->resource->sendBegin(SERIAL, timeMs, g_pCompositor->m_sSeat.seat->pointer_state.focused_surface->resource, fingers);
sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers);
}
}
void CPointerGesturesProtocol::pinchUpdate(uint32_t timeMs, const Vector2D& delta, double scale, double rotation) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
for (auto& sw : m_vPinches) {
if (sw->resource->client() != FOCUSEDCLIENT)
@ -179,12 +181,12 @@ void CPointerGesturesProtocol::pinchUpdate(uint32_t timeMs, const Vector2D& delt
}
void CPointerGesturesProtocol::pinchEnd(uint32_t timeMs, bool cancelled) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client);
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock());
for (auto& sw : m_vPinches) {
if (sw->resource->client() != FOCUSEDCLIENT)
@ -195,28 +197,28 @@ void CPointerGesturesProtocol::pinchEnd(uint32_t timeMs, bool cancelled) {
}
void CPointerGesturesProtocol::holdBegin(uint32_t timeMs, uint32_t fingers) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client);
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock());
for (auto& sw : m_vHolds) {
if (sw->resource->client() != FOCUSEDCLIENT)
continue;
sw->resource->sendBegin(SERIAL, timeMs, g_pCompositor->m_sSeat.seat->pointer_state.focused_surface->resource, fingers);
sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers);
}
}
void CPointerGesturesProtocol::holdEnd(uint32_t timeMs, bool cancelled) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSEDCLIENT = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client();
const auto SERIAL = wlr_seat_client_next_serial(g_pCompositor->m_sSeat.seat->pointer_state.focused_client);
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock());
for (auto& sw : m_vHolds) {
if (sw->resource->client() != FOCUSEDCLIENT)

View File

@ -0,0 +1,338 @@
#include "PrimarySelection.hpp"
#include <algorithm>
#include "../managers/SeatManager.hpp"
#include "core/Seat.hpp"
#include "../config/ConfigValue.hpp"
#define LOGM PROTO::primarySelection->protoLog
CPrimarySelectionOffer::CPrimarySelectionOffer(SP<CZwpPrimarySelectionOfferV1> resource_, SP<IDataSource> source_) : source(source_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); });
resource->setOnDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); });
resource->setReceive([this](CZwpPrimarySelectionOfferV1* r, const char* mime, int32_t fd) {
if (!source) {
LOGM(WARN, "Possible bug: Receive on an offer w/o a source");
close(fd);
return;
}
if (dead) {
LOGM(WARN, "Possible bug: Receive on an offer that's dead");
close(fd);
return;
}
LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get());
source->send(mime, fd);
});
}
bool CPrimarySelectionOffer::good() {
return resource->resource();
}
void CPrimarySelectionOffer::sendData() {
if (!source)
return;
for (auto& m : source->mimes()) {
resource->sendOffer(m.c_str());
}
}
CPrimarySelectionSource::CPrimarySelectionSource(SP<CZwpPrimarySelectionSourceV1> resource_, SP<CPrimarySelectionDevice> device_) : device(device_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CZwpPrimarySelectionSourceV1* r) {
events.destroy.emit();
PROTO::primarySelection->destroyResource(this);
});
resource->setOnDestroy([this](CZwpPrimarySelectionSourceV1* r) {
events.destroy.emit();
PROTO::primarySelection->destroyResource(this);
});
resource->setOffer([this](CZwpPrimarySelectionSourceV1* r, const char* mime) { mimeTypes.push_back(mime); });
}
CPrimarySelectionSource::~CPrimarySelectionSource() {
events.destroy.emit();
}
SP<CPrimarySelectionSource> CPrimarySelectionSource::fromResource(wl_resource* res) {
auto data = (CPrimarySelectionSource*)(((CZwpPrimarySelectionSourceV1*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CPrimarySelectionSource::good() {
return resource->resource();
}
std::vector<std::string> CPrimarySelectionSource::mimes() {
return mimeTypes;
}
void CPrimarySelectionSource::send(const std::string& mime, uint32_t fd) {
if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) {
LOGM(ERR, "Compositor/App bug: CPrimarySelectionSource::sendAskSend with non-existent mime");
close(fd);
return;
}
resource->sendSend(mime.c_str(), fd);
close(fd);
}
void CPrimarySelectionSource::accepted(const std::string& mime) {
if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end())
LOGM(ERR, "Compositor/App bug: CPrimarySelectionSource::sendAccepted with non-existent mime");
// primary sel has no accepted
}
void CPrimarySelectionSource::cancelled() {
resource->sendCancelled();
}
void CPrimarySelectionSource::error(uint32_t code, const std::string& msg) {
resource->error(code, msg);
}
CPrimarySelectionDevice::CPrimarySelectionDevice(SP<CZwpPrimarySelectionDeviceV1> resource_) : resource(resource_) {
if (!good())
return;
pClient = resource->client();
resource->setDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); });
resource->setOnDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); });
resource->setSetSelection([this](CZwpPrimarySelectionDeviceV1* r, wl_resource* sourceR, uint32_t serial) {
static auto PPRIMARYSEL = CConfigValue<Hyprlang::INT>("misc:middle_click_paste");
if (!*PPRIMARYSEL) {
LOGM(LOG, "Ignoring primary selection: disabled in config");
g_pSeatManager->setCurrentPrimarySelection(nullptr);
return;
}
auto source = sourceR ? CPrimarySelectionSource::fromResource(sourceR) : CSharedPointer<CPrimarySelectionSource>{};
if (!source) {
LOGM(LOG, "wlr reset selection received");
g_pSeatManager->setCurrentPrimarySelection(nullptr);
return;
}
if (source && source->used())
LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
source->markUsed();
LOGM(LOG, "wlr manager requests selection to {:x}", (uintptr_t)source.get());
g_pSeatManager->setCurrentPrimarySelection(source);
});
}
bool CPrimarySelectionDevice::good() {
return resource->resource();
}
wl_client* CPrimarySelectionDevice::client() {
return pClient;
}
void CPrimarySelectionDevice::sendDataOffer(SP<CPrimarySelectionOffer> offer) {
resource->sendDataOffer(offer->resource.get());
}
void CPrimarySelectionDevice::sendSelection(SP<CPrimarySelectionOffer> selection) {
if (!selection)
resource->sendSelectionRaw(nullptr);
else
resource->sendSelection(selection->resource.get());
}
CPrimarySelectionManager::CPrimarySelectionManager(SP<CZwpPrimarySelectionDeviceManagerV1> resource_) : resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CZwpPrimarySelectionDeviceManagerV1* r) { PROTO::primarySelection->destroyResource(this); });
resource->setGetDevice([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id, wl_resource* seat) {
const auto RESOURCE =
PROTO::primarySelection->m_vDevices.emplace_back(makeShared<CPrimarySelectionDevice>(makeShared<CZwpPrimarySelectionDeviceV1>(r->client(), r->version(), id)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::primarySelection->m_vDevices.pop_back();
return;
}
RESOURCE->self = RESOURCE;
device = RESOURCE;
for (auto& s : sources) {
if (!s)
continue;
s->device = RESOURCE;
}
LOGM(LOG, "New primary selection data device bound at {:x}", (uintptr_t)RESOURCE.get());
});
resource->setCreateSource([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id) {
std::erase_if(sources, [](const auto& e) { return e.expired(); });
const auto RESOURCE = PROTO::primarySelection->m_vSources.emplace_back(
makeShared<CPrimarySelectionSource>(makeShared<CZwpPrimarySelectionSourceV1>(r->client(), r->version(), id), device.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::primarySelection->m_vSources.pop_back();
return;
}
if (!device)
LOGM(WARN, "New data source before a device was created");
RESOURCE->self = RESOURCE;
sources.push_back(RESOURCE);
LOGM(LOG, "New primary selection data source bound at {:x}", (uintptr_t)RESOURCE.get());
});
}
bool CPrimarySelectionManager::good() {
return resource->resource();
}
CPrimarySelectionProtocol::CPrimarySelectionProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CPrimarySelectionProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CPrimarySelectionManager>(makeShared<CZwpPrimarySelectionDeviceManagerV1>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
LOGM(LOG, "New primary_seletion_manager at {:x}", (uintptr_t)RESOURCE.get());
// we need to do it here because protocols come before seatMgr
if (!listeners.onPointerFocusChange)
listeners.onPointerFocusChange = g_pSeatManager->events.pointerFocusChange.registerListener([this](std::any d) { this->onPointerFocus(); });
}
void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionManager* resource) {
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
}
void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionSource* resource) {
std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; });
}
void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionDevice* resource) {
std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; });
}
void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionOffer* resource) {
std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; });
}
void CPrimarySelectionProtocol::sendSelectionToDevice(SP<CPrimarySelectionDevice> dev, SP<IDataSource> sel) {
if (!sel) {
dev->sendSelection(nullptr);
return;
}
const auto OFFER =
m_vOffers.emplace_back(makeShared<CPrimarySelectionOffer>(makeShared<CZwpPrimarySelectionOfferV1>(dev->resource->client(), dev->resource->version(), 0), sel));
if (!OFFER->good()) {
dev->resource->noMemory();
m_vOffers.pop_back();
return;
}
LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get());
dev->sendDataOffer(OFFER);
OFFER->sendData();
dev->sendSelection(OFFER);
}
void CPrimarySelectionProtocol::setSelection(SP<IDataSource> source) {
for (auto& o : m_vOffers) {
if (o->source && o->source->hasDnd())
continue;
o->dead = true;
}
if (!source) {
LOGM(LOG, "resetting selection");
if (!g_pSeatManager->state.pointerFocusResource)
return;
auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client());
if (DESTDEVICE)
sendSelectionToDevice(DESTDEVICE, nullptr);
return;
}
LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get());
if (!g_pSeatManager->state.pointerFocusResource)
return;
auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client());
if (!DESTDEVICE) {
LOGM(LOG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device");
return;
}
sendSelectionToDevice(DESTDEVICE, source);
}
void CPrimarySelectionProtocol::updateSelection() {
if (!g_pSeatManager->state.pointerFocusResource)
return;
auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client());
if (!DESTDEVICE) {
LOGM(LOG, "CPrimarySelectionProtocol::updateSelection: cannot send selection to a client without a data_device");
return;
}
sendSelectionToDevice(DESTDEVICE, g_pSeatManager->selection.currentPrimarySelection.lock());
}
void CPrimarySelectionProtocol::onPointerFocus() {
for (auto& o : m_vOffers) {
o->dead = true;
}
updateSelection();
}
SP<CPrimarySelectionDevice> CPrimarySelectionProtocol::dataDeviceForClient(wl_client* c) {
auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; });
if (it == m_vDevices.end())
return nullptr;
return *it;
}

View File

@ -0,0 +1,127 @@
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
#include "WaylandProtocol.hpp"
#include "primary-selection-unstable-v1.hpp"
#include "types/DataDevice.hpp"
class CPrimarySelectionOffer;
class CPrimarySelectionSource;
class CPrimarySelectionDevice;
class CPrimarySelectionManager;
class CPrimarySelectionOffer {
public:
CPrimarySelectionOffer(SP<CZwpPrimarySelectionOfferV1> resource_, SP<IDataSource> source_);
bool good();
void sendData();
bool dead = false;
WP<IDataSource> source;
private:
SP<CZwpPrimarySelectionOfferV1> resource;
friend class CPrimarySelectionDevice;
};
class CPrimarySelectionSource : public IDataSource {
public:
CPrimarySelectionSource(SP<CZwpPrimarySelectionSourceV1> resource_, SP<CPrimarySelectionDevice> device_);
~CPrimarySelectionSource();
static SP<CPrimarySelectionSource> fromResource(wl_resource*);
bool good();
virtual std::vector<std::string> mimes();
virtual void send(const std::string& mime, uint32_t fd);
virtual void accepted(const std::string& mime);
virtual void cancelled();
virtual void error(uint32_t code, const std::string& msg);
std::vector<std::string> mimeTypes;
WP<CPrimarySelectionSource> self;
WP<CPrimarySelectionDevice> device;
private:
SP<CZwpPrimarySelectionSourceV1> resource;
};
class CPrimarySelectionDevice {
public:
CPrimarySelectionDevice(SP<CZwpPrimarySelectionDeviceV1> resource_);
bool good();
wl_client* client();
void sendDataOffer(SP<CPrimarySelectionOffer> offer);
void sendSelection(SP<CPrimarySelectionOffer> selection);
WP<CPrimarySelectionDevice> self;
private:
SP<CZwpPrimarySelectionDeviceV1> resource;
wl_client* pClient = nullptr;
friend class CPrimarySelectionProtocol;
};
class CPrimarySelectionManager {
public:
CPrimarySelectionManager(SP<CZwpPrimarySelectionDeviceManagerV1> resource_);
bool good();
WP<CPrimarySelectionDevice> device;
std::vector<WP<CPrimarySelectionSource>> sources;
private:
SP<CZwpPrimarySelectionDeviceManagerV1> resource;
};
class CPrimarySelectionProtocol : public IWaylandProtocol {
public:
CPrimarySelectionProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
private:
void destroyResource(CPrimarySelectionManager* resource);
void destroyResource(CPrimarySelectionDevice* resource);
void destroyResource(CPrimarySelectionSource* resource);
void destroyResource(CPrimarySelectionOffer* resource);
//
std::vector<SP<CPrimarySelectionManager>> m_vManagers;
std::vector<SP<CPrimarySelectionDevice>> m_vDevices;
std::vector<SP<CPrimarySelectionSource>> m_vSources;
std::vector<SP<CPrimarySelectionOffer>> m_vOffers;
//
void setSelection(SP<IDataSource> source);
void sendSelectionToDevice(SP<CPrimarySelectionDevice> dev, SP<IDataSource> sel);
void updateSelection();
void onPointerFocus();
//
SP<CPrimarySelectionDevice> dataDeviceForClient(wl_client*);
friend class CPrimarySelectionManager;
friend class CPrimarySelectionDevice;
friend class CPrimarySelectionSource;
friend class CPrimarySelectionOffer;
friend class CSeatManager;
struct {
CHyprSignalListener onPointerFocusChange;
} listeners;
};
namespace PROTO {
inline UP<CPrimarySelectionProtocol> primarySelection;
};

View File

@ -1,5 +1,7 @@
#include "RelativePointer.hpp"
#include "Compositor.hpp"
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "core/Seat.hpp"
#include <algorithm>
CRelativePointer::CRelativePointer(SP<CZwpRelativePointerV1> resource_) : resource(resource_) {
@ -58,10 +60,10 @@ void CRelativePointerProtocol::onGetRelativePointer(CZwpRelativePointerManagerV1
void CRelativePointerProtocol::sendRelativeMotion(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) {
if (!g_pCompositor->m_sSeat.seat->pointer_state.focused_client)
if (!g_pSeatManager->state.pointerFocusResource)
return;
const auto FOCUSED = g_pCompositor->m_sSeat.seat->pointer_state.focused_client->client;
const auto FOCUSED = g_pSeatManager->state.pointerFocusResource->client();
for (auto& rp : m_vRelativePointers) {
if (FOCUSED != rp->client())

View File

@ -1,5 +1,6 @@
#include "SessionLock.hpp"
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "FractionalScale.hpp"
#define LOGM PROTO::sessionLock->protoLog
@ -76,7 +77,7 @@ CSessionLockSurface::~CSessionLockSurface() {
}
void CSessionLockSurface::sendConfigure() {
const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, resource->client()));
const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client()));
resource->sendConfigure(SERIAL, pMonitor->vecSize.x, pMonitor->vecSize.y);
}

View File

@ -1,6 +1,8 @@
#include "Tablet.hpp"
#include "../devices/Tablet.hpp"
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "core/Seat.hpp"
#include <algorithm>
#define LOGM PROTO::tablet->protoLog
@ -159,12 +161,10 @@ CTabletToolV2Resource::CTabletToolV2Resource(SP<CZwpTabletToolV2> resource_, SP<
resource->setOnDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); });
resource->setSetCursor([this](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) {
wlr_seat_pointer_request_set_cursor_event e;
e.hotspot_x = hot_x;
e.hotspot_y = hot_y;
e.surface = surf ? wlr_surface_from_resource(surf) : nullptr;
e.serial = serial;
g_pInputManager->processMouseRequest(&e);
if (!g_pSeatManager->state.pointerFocusResource || g_pSeatManager->state.pointerFocusResource->client() != r->client())
return;
g_pInputManager->processMouseRequest(CSeatManager::SSetCursorEvent{surf ? wlr_surface_from_resource(surf) : nullptr, {hot_x, hot_y}});
});
}
@ -539,7 +539,7 @@ void CTabletV2Protocol::down(SP<CTabletTool> tool) {
if (t->tool != tool || !t->current)
continue;
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client()));
t->resource->sendDown(serial);
t->queueFrame();
}
@ -586,7 +586,7 @@ void CTabletV2Protocol::proximityIn(SP<CTabletTool> tool, SP<CTablet> tablet, wl
toolResource->current = true;
toolResource->lastSurf = surf;
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, toolResource->resource->client()));
auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(toolResource->resource->client()));
toolResource->resource->sendProximityIn(serial, tabletResource->resource.get(), surf->resource);
toolResource->queueFrame();
@ -610,7 +610,7 @@ void CTabletV2Protocol::buttonTool(SP<CTabletTool> tool, uint32_t button, uint32
if (t->tool != tool || !t->current)
continue;
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client()));
t->resource->sendButton(serial, button, (zwpTabletToolV2ButtonState)state);
t->queueFrame();
}
@ -634,7 +634,7 @@ void CTabletV2Protocol::mode(SP<CTabletPad> pad, uint32_t group, uint32_t mode,
LOGM(ERR, "BUG THIS: group >= t->groups.size()");
return;
}
auto serial = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, t->resource->client()));
auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client()));
t->groups.at(group)->resource->sendModeSwitch(timeMs, serial, mode);
}
}

View File

@ -87,8 +87,8 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP<CZwpVirtualKeyboardV1>
}
CVirtualKeyboardV1Resource::~CVirtualKeyboardV1Resource() {
wlr_keyboard_finish(&keyboard);
events.destroy.emit();
wlr_keyboard_finish(&keyboard);
}
bool CVirtualKeyboardV1Resource::good() {

756
src/protocols/XDGShell.cpp Normal file
View File

@ -0,0 +1,756 @@
#include "XDGShell.hpp"
#include <algorithm>
#include "../Compositor.hpp"
#include "../managers/SeatManager.hpp"
#include "core/Seat.hpp"
#define LOGM PROTO::xdgShell->protoLog
CXDGPopupResource::CXDGPopupResource(SP<CXdgPopup> resource_, SP<CXDGSurfaceResource> owner_, SP<CXDGSurfaceResource> surface_, SP<CXDGPositionerResource> positioner) :
surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgPopup* r) {
if (surface && surface->mapped)
surface->events.unmap.emit();
PROTO::xdgShell->onPopupDestroy(self);
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setOnDestroy([this](CXdgPopup* r) {
if (surface && surface->mapped)
surface->events.unmap.emit();
PROTO::xdgShell->onPopupDestroy(self);
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setReposition([this](CXdgPopup* r, wl_resource* positionerRes, uint32_t token) {
LOGM(LOG, "Popup {:x} asks for reposition", (uintptr_t)this);
lastRepositionToken = token;
auto pos = CXDGPositionerResource::fromResource(positionerRes);
if (!pos)
return;
positionerRules = CXDGPositionerRules{pos};
events.reposition.emit();
});
resource->setGrab([this](CXdgPopup* r, wl_resource* seat, uint32_t serial) {
LOGM(LOG, "xdg_popup {:x} requests grab", (uintptr_t)this);
PROTO::xdgShell->addOrStartGrab(self.lock());
});
if (parent)
taken = true;
}
CXDGPopupResource::~CXDGPopupResource() {
PROTO::xdgShell->onPopupDestroy(self);
events.destroy.emit();
}
void CXDGPopupResource::applyPositioning(const CBox& box, const Vector2D& t1coord) {
CBox constraint = box.copy().translate(surface->pending.geometry.pos());
geometry = positionerRules.getPosition(constraint, accumulateParentOffset() + t1coord);
LOGM(LOG, "Popup {:x} gets unconstrained to {} {}", (uintptr_t)this, geometry.pos(), geometry.size());
configure(geometry);
if (lastRepositionToken)
repositioned();
}
Vector2D CXDGPopupResource::accumulateParentOffset() {
SP<CXDGSurfaceResource> current = parent.lock();
Vector2D off;
while (current) {
off += current->current.geometry.pos();
if (current->popup) {
off += current->popup->geometry.pos();
current = current->popup->parent.lock();
} else
break;
}
return off;
}
SP<CXDGPopupResource> CXDGPopupResource::fromResource(wl_resource* res) {
auto data = (CXDGPopupResource*)(((CXdgPopup*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CXDGPopupResource::good() {
return resource->resource();
}
void CXDGPopupResource::configure(const CBox& box) {
resource->sendConfigure(box.x, box.y, box.w, box.h);
if (surface)
surface->scheduleConfigure();
}
void CXDGPopupResource::done() {
events.dismissed.emit();
resource->sendPopupDone();
}
void CXDGPopupResource::repositioned() {
if (!lastRepositionToken)
return;
LOGM(LOG, "repositioned: sending reposition token {}", lastRepositionToken);
resource->sendRepositioned(lastRepositionToken);
lastRepositionToken = 0;
}
CXDGToplevelResource::CXDGToplevelResource(SP<CXdgToplevel> resource_, SP<CXDGSurfaceResource> owner_) : owner(owner_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgToplevel* r) {
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setOnDestroy([this](CXdgToplevel* r) {
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
if (resource->version() >= 5) {
wl_array arr;
wl_array_init(&arr);
auto p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t));
*p = XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN;
p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t));
*p = XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE;
resource->sendWmCapabilities(&arr);
wl_array_release(&arr);
}
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM);
resource->setSetTitle([this](CXdgToplevel* r, const char* t) {
state.title = t;
events.metadataChanged.emit();
});
resource->setSetAppId([this](CXdgToplevel* r, const char* id) {
state.appid = id;
events.metadataChanged.emit();
});
resource->setSetMaxSize([this](CXdgToplevel* r, int32_t x, int32_t y) {
pending.maxSize = {x, y};
events.sizeLimitsChanged.emit();
});
resource->setSetMinSize([this](CXdgToplevel* r, int32_t x, int32_t y) {
pending.minSize = {x, y};
events.sizeLimitsChanged.emit();
});
resource->setSetMaximized([this](CXdgToplevel* r) {
state.requestsMaximize = true;
events.stateChanged.emit();
state.requestsMaximize.reset();
});
resource->setUnsetMaximized([this](CXdgToplevel* r) {
state.requestsMaximize = false;
events.stateChanged.emit();
state.requestsMaximize.reset();
});
resource->setSetFullscreen([this](CXdgToplevel* r, wl_resource* output) {
state.requestsFullscreen = true;
events.stateChanged.emit();
state.requestsFullscreen.reset();
});
resource->setUnsetFullscreen([this](CXdgToplevel* r) {
state.requestsFullscreen = false;
events.stateChanged.emit();
state.requestsFullscreen.reset();
});
resource->setSetMinimized([this](CXdgToplevel* r) {
state.requestsMinimize = true;
events.stateChanged.emit();
state.requestsFullscreen.reset();
});
resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) {
auto newp = parentR ? CXDGToplevelResource::fromResource(parentR) : nullptr;
parent = newp;
LOGM(LOG, "Toplevel {:x} sets parent to {:x}", (uintptr_t)this, (uintptr_t)newp.get());
});
}
CXDGToplevelResource::~CXDGToplevelResource() {
events.destroy.emit();
}
SP<CXDGToplevelResource> CXDGToplevelResource::fromResource(wl_resource* res) {
auto data = (CXDGToplevelResource*)(((CXdgToplevel*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CXDGToplevelResource::good() {
return resource->resource();
}
uint32_t CXDGToplevelResource::setSize(const Vector2D& size) {
pendingApply.size = size;
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setMaximized(bool maximized) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_MAXIMIZED) != pendingApply.states.end();
if (maximized == set)
return owner->scheduledSerial;
if (maximized && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED);
else if (!maximized && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED);
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setFullscreen(bool fullscreen) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_FULLSCREEN) != pendingApply.states.end();
if (fullscreen == set)
return owner->scheduledSerial;
if (fullscreen && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN);
else if (!fullscreen && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN);
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setActive(bool active) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_ACTIVATED) != pendingApply.states.end();
if (active == set)
return owner->scheduledSerial;
if (active && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED);
else if (!active && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED);
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_SUSPENDED) != pendingApply.states.end();
if (sus == set)
return owner->scheduledSerial;
if (sus && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED);
else if (!sus && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED);
applyState();
return owner->scheduleConfigure();
}
void CXDGToplevelResource::applyState() {
wl_array arr;
wl_array_init(&arr);
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr);
wl_array_release(&arr);
}
void CXDGToplevelResource::close() {
resource->sendClose();
}
CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBase> owner_, wlr_surface* surface_) : owner(owner_), surface(surface_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgSurface* r) {
if (mapped)
events.unmap.emit();
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setOnDestroy([this](CXdgSurface* r) {
if (mapped)
events.unmap.emit();
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
hyprListener_surfaceDestroy.initCallback(
&surface->events.destroy,
[this](void* owner, void* data) {
LOGM(WARN, "wl_surface destroyed before its xdg_surface role object");
hyprListener_surfaceDestroy.removeCallback();
hyprListener_surfaceCommit.removeCallback();
if (mapped)
events.unmap.emit();
mapped = false;
surface = nullptr;
events.destroy.emit();
},
nullptr, "CXDGSurfaceResource");
hyprListener_surfaceCommit.initCallback(
&surface->events.commit,
[this](void* owner, void* data) {
current = pending;
if (toplevel)
toplevel->current = toplevel->pending;
if (initialCommit && surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0) {
resource->error(-1, "Buffer attached before initial commit");
return;
}
if (surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0 && !mapped) {
// this forces apps to not draw CSD.
if (toplevel)
toplevel->setMaximized(true);
mapped = true;
wlr_surface_map(surface);
events.map.emit();
return;
}
if (surface->pending.buffer_width <= 0 && surface->pending.buffer_height <= 0 && mapped) {
mapped = false;
wlr_surface_unmap(surface);
events.unmap.emit();
return;
}
events.commit.emit();
initialCommit = false;
},
nullptr, "CXDGSurfaceResource");
resource->setGetToplevel([this](CXdgSurface* r, uint32_t id) {
const auto RESOURCE = PROTO::xdgShell->m_vToplevels.emplace_back(makeShared<CXDGToplevelResource>(makeShared<CXdgToplevel>(r->client(), r->version(), id), self.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vToplevels.pop_back();
return;
}
toplevel = RESOURCE;
toplevel->self = RESOURCE;
LOGM(LOG, "xdg_surface {:x} gets a toplevel {:x}", (uintptr_t)owner.get(), (uintptr_t)RESOURCE.get());
g_pCompositor->m_vWindows.emplace_back(CWindow::create(self.lock()));
for (auto& p : popups) {
if (!p)
continue;
events.newPopup.emit(p);
}
});
resource->setGetPopup([this](CXdgSurface* r, uint32_t id, wl_resource* parentXDG, wl_resource* positionerRes) {
auto parent = parentXDG ? CXDGSurfaceResource::fromResource(parentXDG) : nullptr;
auto positioner = CXDGPositionerResource::fromResource(positionerRes);
const auto RESOURCE =
PROTO::xdgShell->m_vPopups.emplace_back(makeShared<CXDGPopupResource>(makeShared<CXdgPopup>(r->client(), r->version(), id), parent, self.lock(), positioner));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vPopups.pop_back();
return;
}
popup = RESOURCE;
RESOURCE->self = RESOURCE;
LOGM(LOG, "xdg_surface {:x} gets a popup {:x} owner {:x}", (uintptr_t)self.get(), (uintptr_t)RESOURCE.get(), (uintptr_t)parent.get());
if (!parent)
return;
parent->popups.emplace_back(RESOURCE);
if (parent->mapped)
parent->events.newPopup.emit(RESOURCE);
});
resource->setAckConfigure([this](CXdgSurface* r, uint32_t serial) {
events.ack.emit(serial);
; // TODO: verify it
});
resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
LOGM(LOG, "xdg_surface {:x} requests geometry {}x{} {}x{}", (uintptr_t)this, x, y, w, h);
pending.geometry = {x, y, w, h};
});
}
CXDGSurfaceResource::~CXDGSurfaceResource() {
events.destroy.emit();
if (configureSource)
wl_event_source_remove(configureSource);
}
bool CXDGSurfaceResource::good() {
return resource->resource();
}
SP<CXDGSurfaceResource> CXDGSurfaceResource::fromResource(wl_resource* res) {
auto data = (CXDGSurfaceResource*)(((CXdgSurface*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
static void onConfigure(void* data) {
((CXDGSurfaceResource*)data)->configure();
}
uint32_t CXDGSurfaceResource::scheduleConfigure() {
if (configureSource)
return scheduledSerial;
configureSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, onConfigure, this);
scheduledSerial = wl_display_next_serial(g_pCompositor->m_sWLDisplay);
return scheduledSerial;
}
void CXDGSurfaceResource::configure() {
configureSource = nullptr;
resource->sendConfigure(scheduledSerial);
}
CXDGPositionerResource::CXDGPositionerResource(SP<CXdgPositioner> resource_, SP<CXDGWMBase> owner_) : owner(owner_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); });
resource->setOnDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); });
resource->setSetSize([this](CXdgPositioner* r, int32_t x, int32_t y) {
if (x <= 0 || y <= 0) {
r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size");
return;
}
state.requestedSize = {x, y};
});
resource->setSetAnchorRect([this](CXdgPositioner* r, int32_t x, int32_t y, int32_t w, int32_t h) {
if (w <= 0 || h <= 0) {
r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid box");
return;
}
state.anchorRect = {x, y, w, h};
});
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->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.gravity = g; });
resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { state.constraintAdjustment = (uint32_t)a; });
// TODO: support this shit better. The current impl _works_, but is lacking and could be wrong in a few cases.
// doesn't matter _that_ much for now, though.
}
SP<CXDGPositionerResource> CXDGPositionerResource::fromResource(wl_resource* res) {
auto data = (CXDGPositionerResource*)(((CXdgPositioner*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CXDGPositionerResource::good() {
return resource->resource();
}
CXDGPositionerRules::CXDGPositionerRules(SP<CXDGPositionerResource> positioner) {
state = positioner->state;
}
static Vector2D pointForAnchor(const CBox& box, xdgPositionerAnchor anchor) {
switch (anchor) {
case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.F, 0};
case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.F, box.size().y};
case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0, box.size().y / 2.F};
case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F};
case XDG_POSITIONER_ANCHOR_TOP_LEFT: return box.pos();
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0, box.size().y};
case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return box.pos() + Vector2D{box.size().x, 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) {
Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord);
CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.anchor) + state.offset, state.requestedSize};
bool success = predictedBox.inside(constraint);
if (success)
return predictedBox.translate(-parentCoord - constraint.pos());
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;
CBox test = predictedBox;
success = true;
if (flipX && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0}).expand(-1).inside(constraint))
test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0});
else if (flipY && test.copy().translate(Vector2D{0, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint))
test.translate(Vector2D{0, -predictedBox.h - state.anchorRect.h});
else if (flipX && flipY && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint))
test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h});
else
success = false;
if (success)
return test.translate(-parentCoord - constraint.pos());
}
// if flips fail, we will slide and remember.
// if the positioner is allowed to resize, then resize the slid thing.
CBox test = predictedBox;
// for slide and resize, defines the padding around the edge for the positioned
// surface.
constexpr int EDGE_PADDING = 4;
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)) {
// attempt to slide
const bool slideX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X;
const bool slideY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
//const bool gravityLeft = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT;
//const bool gravityTop = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_TOP || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_RIGHT;
const bool leftEdgeOut = predictedBox.x < constraint.x;
const bool topEdgeOut = predictedBox.y < constraint.y;
const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w;
const bool bottomEdgeOut = predictedBox.y + predictedBox.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 = constraint.x + constraint.w - predictedBox.w - EDGE_PADDING;
if (topEdgeOut && slideY)
test.y = constraint.y + EDGE_PADDING;
if (bottomEdgeOut && slideY)
test.y = constraint.y + constraint.h - predictedBox.y - EDGE_PADDING;
success = test.copy().expand(-1).inside(constraint);
if (success)
return test.translate(-parentCoord - constraint.pos());
}
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)) {
const bool resizeX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X;
const bool resizeY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y;
const bool leftEdgeOut = predictedBox.x < constraint.x;
const bool topEdgeOut = predictedBox.y < constraint.y;
const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w;
const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h;
// TODO: this isn't truly conformant.
if (leftEdgeOut && resizeX) {
test.w = test.x + test.w - constraint.x - EDGE_PADDING;
test.x = constraint.x + EDGE_PADDING;
}
if (rightEdgeOut && resizeX)
test.w = -(constraint.w + constraint.x - test.w - test.x + 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 + constraint.y - test.h - test.y + EDGE_PADDING);
success = test.copy().expand(-1).inside(constraint);
if (success)
return test.translate(parentCoord - constraint.pos());
}
LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place");
return test.translate(-parentCoord - constraint.pos());
}
CXDGWMBase::CXDGWMBase(SP<CXdgWmBase> resource_) : resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); });
resource->setOnDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); });
pClient = resource->client();
resource->setCreatePositioner([this](CXdgWmBase* r, uint32_t id) {
const auto RESOURCE =
PROTO::xdgShell->m_vPositioners.emplace_back(makeShared<CXDGPositionerResource>(makeShared<CXdgPositioner>(r->client(), r->version(), id), self.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vPositioners.pop_back();
return;
}
RESOURCE->self = RESOURCE;
positioners.emplace_back(RESOURCE);
LOGM(LOG, "New xdg_positioner at {:x}", (uintptr_t)RESOURCE.get());
});
resource->setGetXdgSurface([this](CXdgWmBase* r, uint32_t id, wl_resource* surf) {
const auto RESOURCE = PROTO::xdgShell->m_vSurfaces.emplace_back(
makeShared<CXDGSurfaceResource>(makeShared<CXdgSurface>(r->client(), r->version(), id), self.lock(), wlr_surface_from_resource(surf)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vSurfaces.pop_back();
return;
}
RESOURCE->self = RESOURCE;
surfaces.emplace_back(RESOURCE);
LOGM(LOG, "New xdg_surface at {:x}", (uintptr_t)RESOURCE.get());
});
}
bool CXDGWMBase::good() {
return resource->resource();
}
wl_client* CXDGWMBase::client() {
return pClient;
}
CXDGShellProtocol::CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
grab = makeShared<CSeatGrab>();
grab->keyboard = true;
grab->pointer = true;
grab->setCallback([this]() {
for (auto& g : grabbed) {
g->done();
}
grabbed.clear();
});
}
void CXDGShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vWMBases.emplace_back(makeShared<CXDGWMBase>(makeShared<CXdgWmBase>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vWMBases.pop_back();
return;
}
RESOURCE->self = RESOURCE;
LOGM(LOG, "New xdg_wm_base at {:x}", (uintptr_t)RESOURCE.get());
}
void CXDGShellProtocol::destroyResource(CXDGWMBase* resource) {
std::erase_if(m_vWMBases, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGPositionerResource* resource) {
std::erase_if(m_vPositioners, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGSurfaceResource* resource) {
std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGToplevelResource* resource) {
std::erase_if(m_vToplevels, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGPopupResource* resource) {
std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::addOrStartGrab(SP<CXDGPopupResource> popup) {
if (!grabOwner) {
grabOwner = popup;
grabbed.clear();
grab->clear();
grab->add(popup->surface->surface);
if (popup->parent)
grab->add(popup->parent->surface);
g_pSeatManager->setGrab(grab);
grabbed.emplace_back(popup);
return;
}
grabbed.emplace_back(popup);
grab->add(popup->surface->surface);
if (popup->parent)
grab->add(popup->parent->surface);
}
void CXDGShellProtocol::onPopupDestroy(WP<CXDGPopupResource> popup) {
if (popup == grabOwner) {
g_pSeatManager->setGrab(nullptr);
for (auto& g : grabbed) {
g->done();
}
grabbed.clear();
return;
}
std::erase(grabbed, popup);
if (popup->surface)
grab->remove(popup->surface->surface);
}

265
src/protocols/XDGShell.hpp Normal file
View File

@ -0,0 +1,265 @@
#pragma once
#include <memory>
#include <vector>
#include <cstdint>
#include <optional>
#include "WaylandProtocol.hpp"
#include "xdg-shell.hpp"
#include "../helpers/Vector2D.hpp"
#include "../helpers/Box.hpp"
#include "../helpers/signal/Signal.hpp"
class CXDGWMBase;
class CXDGPositionerResource;
class CXDGSurfaceResource;
class CXDGToplevelResource;
class CXDGPopupResource;
class CSeatGrab;
struct SXDGPositionerState {
Vector2D requestedSize;
CBox anchorRect;
xdgPositionerAnchor anchor = XDG_POSITIONER_ANCHOR_NONE;
xdgPositionerGravity gravity = XDG_POSITIONER_GRAVITY_NONE;
uint32_t constraintAdjustment = 0;
Vector2D offset;
bool reactive = false;
Vector2D parentSize;
};
class CXDGPositionerRules {
public:
CXDGPositionerRules(SP<CXDGPositionerResource> positioner);
CBox getPosition(const CBox& constraint, const Vector2D& parentPos);
private:
SXDGPositionerState state;
};
class CXDGPopupResource {
public:
CXDGPopupResource(SP<CXdgPopup> resource_, SP<CXDGSurfaceResource> parent_, SP<CXDGSurfaceResource> surface_, SP<CXDGPositionerResource> positioner_);
~CXDGPopupResource();
static SP<CXDGPopupResource> fromResource(wl_resource*);
bool good();
void applyPositioning(const CBox& availableBox, const Vector2D& t1coord /* relative to box */);
WP<CXDGSurfaceResource> surface;
WP<CXDGSurfaceResource> parent;
WP<CXDGPopupResource> self;
bool taken = false;
CBox geometry;
struct {
CSignal reposition;
CSignal dismissed;
CSignal destroy; // only the role
} events;
// schedules a configure event
void configure(const CBox& box);
void done();
void repositioned();
private:
SP<CXdgPopup> resource;
uint32_t lastRepositionToken = 0;
Vector2D accumulateParentOffset();
CXDGPositionerRules positionerRules;
};
class CXDGToplevelResource {
public:
CXDGToplevelResource(SP<CXdgToplevel> resource_, SP<CXDGSurfaceResource> owner_);
~CXDGToplevelResource();
static SP<CXDGToplevelResource> fromResource(wl_resource*);
WP<CXDGSurfaceResource> owner;
WP<CXDGToplevelResource> self;
PHLWINDOWREF window;
bool good();
// schedule a configure event
uint32_t setSize(const Vector2D& size);
uint32_t setMaximized(bool maximized);
uint32_t setFullscreen(bool fullscreen);
uint32_t setActive(bool active);
uint32_t setSuspeneded(bool sus);
void close();
struct {
CSignal sizeLimitsChanged;
CSignal stateChanged; // maximized, fs, minimized, etc.
CSignal metadataChanged; // title, appid
CSignal destroy; // only the role
} events;
struct {
std::string title;
std::string appid;
// volatile state: is reset after the stateChanged signal fires
std::optional<bool> requestsMaximize;
std::optional<bool> requestsFullscreen;
std::optional<bool> requestsMinimize;
} state;
struct {
Vector2D size;
std::vector<xdgToplevelState> states;
} pendingApply;
struct {
Vector2D minSize = {1, 1};
Vector2D maxSize = {1337420, 694200};
} pending, current;
WP<CXDGToplevelResource> parent;
private:
SP<CXdgToplevel> resource;
void applyState();
};
class CXDGSurfaceResource {
public:
CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBase> owner_, wlr_surface* surface_);
~CXDGSurfaceResource();
static SP<CXDGSurfaceResource> fromResource(wl_resource*);
bool good();
WP<CXDGWMBase> owner;
wlr_surface* surface = nullptr;
WP<CXDGToplevelResource> toplevel;
WP<CXDGPopupResource> popup;
WP<CXDGSurfaceResource> self;
struct {
CBox geometry;
} pending, current;
struct {
CSignal ack;
CSignal commit;
CSignal map;
CSignal unmap;
CSignal destroy;
CSignal newPopup; // SP<CXDGPopupResource>
} events;
bool initialCommit = true;
bool mapped = false;
uint32_t scheduleConfigure();
// do not call directly
void configure();
private:
SP<CXdgSurface> resource;
uint32_t lastConfigureSerial = 0;
uint32_t scheduledSerial = 0;
wl_event_source* configureSource = nullptr;
//
std::vector<WP<CXDGPopupResource>> popups;
DYNLISTENER(surfaceDestroy);
DYNLISTENER(surfaceCommit);
friend class CXDGPopupResource;
friend class CXDGToplevelResource;
};
class CXDGPositionerResource {
public:
CXDGPositionerResource(SP<CXdgPositioner> resource_, SP<CXDGWMBase> owner_);
static SP<CXDGPositionerResource> fromResource(wl_resource*);
bool good();
SXDGPositionerState state;
WP<CXDGWMBase> owner;
WP<CXDGPositionerResource> self;
private:
SP<CXdgPositioner> resource;
};
class CXDGWMBase {
public:
CXDGWMBase(SP<CXdgWmBase> resource_);
bool good();
wl_client* client();
std::vector<WP<CXDGPositionerResource>> positioners;
std::vector<WP<CXDGSurfaceResource>> surfaces;
WP<CXDGWMBase> self;
private:
SP<CXdgWmBase> resource;
wl_client* pClient = nullptr;
};
class CXDGShellProtocol : public IWaylandProtocol {
public:
CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
private:
void destroyResource(CXDGWMBase* resource);
void destroyResource(CXDGPositionerResource* resource);
void destroyResource(CXDGSurfaceResource* resource);
void destroyResource(CXDGToplevelResource* resource);
void destroyResource(CXDGPopupResource* resource);
//
std::vector<SP<CXDGWMBase>> m_vWMBases;
std::vector<SP<CXDGPositionerResource>> m_vPositioners;
std::vector<SP<CXDGSurfaceResource>> m_vSurfaces;
std::vector<SP<CXDGToplevelResource>> m_vToplevels;
std::vector<SP<CXDGPopupResource>> m_vPopups;
// current popup grab
WP<CXDGPopupResource> grabOwner;
SP<CSeatGrab> grab;
std::vector<WP<CXDGPopupResource>> grabbed;
void addOrStartGrab(SP<CXDGPopupResource> popup);
void onPopupDestroy(WP<CXDGPopupResource> popup);
friend class CXDGWMBase;
friend class CXDGPositionerResource;
friend class CXDGSurfaceResource;
friend class CXDGToplevelResource;
friend class CXDGPopupResource;
};
namespace PROTO {
inline UP<CXDGShellProtocol> xdgShell;
};

View File

@ -0,0 +1,659 @@
#include "DataDevice.hpp"
#include <algorithm>
#include "../../managers/SeatManager.hpp"
#include "../../managers/PointerManager.hpp"
#include "../../Compositor.hpp"
#include "Seat.hpp"
#define LOGM PROTO::data->protoLog
CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataSource> source_) : source(source_), resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CWlDataOffer* r) {
if (!dead)
PROTO::data->completeDrag();
PROTO::data->destroyResource(this);
});
resource->setOnDestroy([this](CWlDataOffer* r) {
if (!dead)
PROTO::data->completeDrag();
PROTO::data->destroyResource(this);
});
resource->setAccept([this](CWlDataOffer* r, uint32_t serial, const char* mime) {
if (!source) {
LOGM(WARN, "Possible bug: Accept on an offer w/o a source");
return;
}
if (dead) {
LOGM(WARN, "Possible bug: Accept on an offer that's dead");
return;
}
LOGM(LOG, "Offer {:x} accepts data from source {:x} with mime {}", (uintptr_t)this, (uintptr_t)source.get(), mime ? mime : "null");
source->accepted(mime ? mime : "");
accepted = mime;
});
resource->setReceive([this](CWlDataOffer* r, const char* mime, uint32_t fd) {
if (!source) {
LOGM(WARN, "Possible bug: Receive on an offer w/o a source");
close(fd);
return;
}
if (dead) {
LOGM(WARN, "Possible bug: Receive on an offer that's dead");
close(fd);
return;
}
LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get());
if (!accepted) {
LOGM(WARN, "Offer was never accepted, sending accept first");
source->accepted(mime ? mime : "");
}
source->send(mime ? mime : "", fd);
recvd = true;
// if (source->hasDnd())
// PROTO::data->completeDrag();
});
resource->setFinish([this](CWlDataOffer* r) {
dead = true;
if (!source || !recvd || !accepted)
PROTO::data->abortDrag();
else
PROTO::data->completeDrag();
});
}
bool CWLDataOfferResource::good() {
return resource->resource();
}
void CWLDataOfferResource::sendData() {
if (!source)
return;
resource->sendSourceActions(7);
resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
for (auto& m : source->mimes()) {
LOGM(LOG, " | offer {:x} supports mime {}", (uintptr_t)this, m);
resource->sendOffer(m.c_str());
}
}
CWLDataSourceResource::CWLDataSourceResource(SP<CWlDataSource> resource_, SP<CWLDataDeviceResource> device_) : device(device_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CWlDataSource* r) {
events.destroy.emit();
PROTO::data->onDestroyDataSource(self);
PROTO::data->destroyResource(this);
});
resource->setOnDestroy([this](CWlDataSource* r) {
events.destroy.emit();
PROTO::data->onDestroyDataSource(self);
PROTO::data->destroyResource(this);
});
resource->setOffer([this](CWlDataSource* r, const char* mime) { mimeTypes.push_back(mime); });
resource->setSetActions([this](CWlDataSource* r, uint32_t a) {
LOGM(LOG, "DataSource {:x} actions {}", (uintptr_t)this, a);
actions = (wl_data_device_manager_dnd_action)a;
});
}
CWLDataSourceResource::~CWLDataSourceResource() {
events.destroy.emit();
PROTO::data->onDestroyDataSource(self);
}
SP<CWLDataSourceResource> CWLDataSourceResource::fromResource(wl_resource* res) {
auto data = (CWLDataSourceResource*)(((CWlDataSource*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CWLDataSourceResource::good() {
return resource->resource();
}
void CWLDataSourceResource::accepted(const std::string& mime) {
if (mime.empty()) {
resource->sendTarget(nullptr);
return;
}
if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) {
LOGM(ERR, "Compositor/App bug: CWLDataSourceResource::sendAccepted with non-existent mime");
return;
}
resource->sendTarget(mime.c_str());
}
std::vector<std::string> CWLDataSourceResource::mimes() {
return mimeTypes;
}
void CWLDataSourceResource::send(const std::string& mime, uint32_t fd) {
if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) {
LOGM(ERR, "Compositor/App bug: CWLDataSourceResource::sendAskSend with non-existent mime");
close(fd);
return;
}
resource->sendSend(mime.c_str(), fd);
close(fd);
}
void CWLDataSourceResource::cancelled() {
resource->sendCancelled();
}
bool CWLDataSourceResource::hasDnd() {
return dnd;
}
bool CWLDataSourceResource::dndDone() {
return dndSuccess;
}
void CWLDataSourceResource::error(uint32_t code, const std::string& msg) {
resource->error(code, msg);
}
void CWLDataSourceResource::sendDndDropPerformed() {
if (resource->version() < 3)
return;
resource->sendDndDropPerformed();
}
void CWLDataSourceResource::sendDndFinished() {
if (resource->version() < 3)
return;
resource->sendDndFinished();
}
void CWLDataSourceResource::sendDndAction(wl_data_device_manager_dnd_action a) {
if (resource->version() < 3)
return;
resource->sendAction(a);
}
CWLDataDeviceResource::CWLDataDeviceResource(SP<CWlDataDevice> resource_) : resource(resource_) {
if (!good())
return;
resource->setRelease([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); });
resource->setOnDestroy([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); });
pClient = resource->client();
resource->setSetSelection([this](CWlDataDevice* r, wl_resource* sourceR, uint32_t serial) {
auto source = sourceR ? CWLDataSourceResource::fromResource(sourceR) : CSharedPointer<CWLDataSourceResource>{};
if (!source) {
LOGM(LOG, "Reset selection received");
g_pSeatManager->setCurrentSelection(nullptr);
return;
}
if (source && source->used)
LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
source->markUsed();
g_pSeatManager->setCurrentSelection(source);
});
resource->setStartDrag([this](CWlDataDevice* r, wl_resource* sourceR, wl_resource* origin, wl_resource* icon, uint32_t serial) {
auto source = CWLDataSourceResource::fromResource(sourceR);
if (!source) {
LOGM(ERR, "No source in drag");
return;
}
if (source && source->used)
LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
source->markUsed();
source->dnd = true;
PROTO::data->initiateDrag(source, icon ? wlr_surface_from_resource(icon) : nullptr, wlr_surface_from_resource(origin));
});
}
bool CWLDataDeviceResource::good() {
return resource->resource();
}
wl_client* CWLDataDeviceResource::client() {
return pClient;
}
void CWLDataDeviceResource::sendDataOffer(SP<CWLDataOfferResource> offer) {
if (offer)
resource->sendDataOffer(offer->resource.get());
else
resource->sendDataOfferRaw(nullptr);
}
void CWLDataDeviceResource::sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP<CWLDataOfferResource> offer) {
resource->sendEnterRaw(serial, surf->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), offer->resource->resource());
}
void CWLDataDeviceResource::sendLeave() {
resource->sendLeave();
}
void CWLDataDeviceResource::sendMotion(uint32_t timeMs, const Vector2D& local) {
resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLDataDeviceResource::sendDrop() {
resource->sendDrop();
}
void CWLDataDeviceResource::sendSelection(SP<CWLDataOfferResource> offer) {
if (!offer)
resource->sendSelectionRaw(nullptr);
else
resource->sendSelection(offer->resource.get());
}
CWLDataDeviceManagerResource::CWLDataDeviceManagerResource(SP<CWlDataDeviceManager> resource_) : resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CWlDataDeviceManager* r) { PROTO::data->destroyResource(this); });
resource->setCreateDataSource([this](CWlDataDeviceManager* r, uint32_t id) {
std::erase_if(sources, [](const auto& e) { return e.expired(); });
const auto RESOURCE = PROTO::data->m_vSources.emplace_back(makeShared<CWLDataSourceResource>(makeShared<CWlDataSource>(r->client(), r->version(), id), device.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::data->m_vSources.pop_back();
return;
}
if (!device)
LOGM(WARN, "New data source before a device was created");
RESOURCE->self = RESOURCE;
sources.push_back(RESOURCE);
LOGM(LOG, "New data source bound at {:x}", (uintptr_t)RESOURCE.get());
});
resource->setGetDataDevice([this](CWlDataDeviceManager* r, uint32_t id, wl_resource* seat) {
const auto RESOURCE = PROTO::data->m_vDevices.emplace_back(makeShared<CWLDataDeviceResource>(makeShared<CWlDataDevice>(r->client(), r->version(), id)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::data->m_vDevices.pop_back();
return;
}
RESOURCE->self = RESOURCE;
for (auto& s : sources) {
if (!s)
continue;
s->device = RESOURCE;
}
LOGM(LOG, "New data device bound at {:x}", (uintptr_t)RESOURCE.get());
});
}
bool CWLDataDeviceManagerResource::good() {
return resource->resource();
}
CWLDataDeviceProtocol::CWLDataDeviceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CWLDataDeviceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CWLDataDeviceManagerResource>(makeShared<CWlDataDeviceManager>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
LOGM(LOG, "New datamgr resource bound at {:x}", (uintptr_t)RESOURCE.get());
// we need to do it here because protocols come before seatMgr
if (!listeners.onKeyboardFocusChange)
listeners.onKeyboardFocusChange = g_pSeatManager->events.keyboardFocusChange.registerListener([this](std::any d) { this->onKeyboardFocus(); });
}
void CWLDataDeviceProtocol::destroyResource(CWLDataDeviceManagerResource* seat) {
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == seat; });
}
void CWLDataDeviceProtocol::destroyResource(CWLDataDeviceResource* resource) {
std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; });
}
void CWLDataDeviceProtocol::destroyResource(CWLDataSourceResource* resource) {
std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; });
}
void CWLDataDeviceProtocol::destroyResource(CWLDataOfferResource* resource) {
std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; });
}
SP<CWLDataDeviceResource> CWLDataDeviceProtocol::dataDeviceForClient(wl_client* c) {
auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; });
if (it == m_vDevices.end())
return nullptr;
return *it;
}
void CWLDataDeviceProtocol::sendSelectionToDevice(SP<CWLDataDeviceResource> dev, SP<IDataSource> sel) {
if (!sel) {
dev->sendSelection(nullptr);
return;
}
const auto OFFER = m_vOffers.emplace_back(makeShared<CWLDataOfferResource>(makeShared<CWlDataOffer>(dev->resource->client(), dev->resource->version(), 0), sel));
if (!OFFER->good()) {
dev->resource->noMemory();
m_vOffers.pop_back();
return;
}
LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get());
dev->sendDataOffer(OFFER);
OFFER->sendData();
dev->sendSelection(OFFER);
}
void CWLDataDeviceProtocol::onDestroyDataSource(WP<CWLDataSourceResource> source) {
if (dnd.currentSource == source)
abortDrag();
}
void CWLDataDeviceProtocol::setSelection(SP<IDataSource> source) {
for (auto& o : m_vOffers) {
if (o->source && o->source->hasDnd())
continue;
o->dead = true;
}
if (!source) {
LOGM(LOG, "resetting selection");
if (!g_pSeatManager->state.keyboardFocusResource)
return;
auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client());
if (DESTDEVICE)
sendSelectionToDevice(DESTDEVICE, nullptr);
return;
}
LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get());
if (!g_pSeatManager->state.keyboardFocusResource)
return;
auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client());
if (!DESTDEVICE) {
LOGM(LOG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device");
return;
}
sendSelectionToDevice(DESTDEVICE, source);
}
void CWLDataDeviceProtocol::updateSelection() {
if (!g_pSeatManager->state.keyboardFocusResource)
return;
auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client());
if (!DESTDEVICE) {
LOGM(LOG, "CWLDataDeviceProtocol::onKeyboardFocus: cannot send selection to a client without a data_device");
return;
}
sendSelectionToDevice(DESTDEVICE, g_pSeatManager->selection.currentSelection.lock());
}
void CWLDataDeviceProtocol::onKeyboardFocus() {
for (auto& o : m_vOffers) {
o->dead = true;
}
updateSelection();
updateDrag();
}
void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource, wlr_surface* dragSurface, wlr_surface* origin) {
if (dnd.currentSource) {
LOGM(WARN, "New drag started while old drag still active??");
abortDrag();
}
g_pInputManager->setCursorImageUntilUnset("grabbing");
dnd.overriddenCursor = true;
LOGM(LOG, "initiateDrag: source {:x}, surface: {:x}, origin: {:x}", (uintptr_t)currentSource.get(), (uintptr_t)dragSurface, (uintptr_t)origin);
currentSource->used = true;
dnd.currentSource = currentSource;
dnd.originSurface = origin;
dnd.dndSurface = dragSurface;
if (dragSurface) {
dnd.hyprListener_dndSurfaceDestroy.initCallback(
&dragSurface->events.destroy, [this](void* owner, void* data) { abortDrag(); }, nullptr, "CWLDataDeviceProtocol::drag");
dnd.hyprListener_dndSurfaceCommit.initCallback(
&dragSurface->events.commit,
[this](void* owner, void* data) {
if (dnd.dndSurface->pending.buffer_width > 0 && dnd.dndSurface->pending.buffer_height > 0 && !dnd.dndSurface->mapped) {
wlr_surface_map(dnd.dndSurface);
return;
}
if (dnd.dndSurface->pending.buffer_width <= 0 && dnd.dndSurface->pending.buffer_height <= 0 && dnd.dndSurface->mapped) {
wlr_surface_unmap(dnd.dndSurface);
return;
}
},
nullptr, "CWLDataDeviceProtocol::drag");
}
dnd.mouseButton = g_pHookSystem->hookDynamic("mouseButton", [this](void* self, SCallbackInfo& info, std::any e) {
auto E = std::any_cast<IPointer::SButtonEvent>(e);
if (E.state == WL_POINTER_BUTTON_STATE_RELEASED) {
LOGM(LOG, "Dropping drag on mouseUp");
dropDrag();
}
});
dnd.touchUp = g_pHookSystem->hookDynamic("touchUp", [this](void* self, SCallbackInfo& info, std::any e) {
LOGM(LOG, "Dropping drag on touchUp");
dropDrag();
});
dnd.mouseMove = g_pHookSystem->hookDynamic("mouseMove", [this](void* self, SCallbackInfo& info, std::any e) {
auto V = std::any_cast<const Vector2D>(e);
if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) {
auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus);
if (!surf)
return;
const auto box = surf->getSurfaceBoxGlobal();
if (!box.has_value())
return;
dnd.focusedDevice->sendMotion(0 /* this is a hack */, V - box->pos());
LOGM(LOG, "Drag motion {}", V - box->pos());
}
});
dnd.touchMove = g_pHookSystem->hookDynamic("touchMove", [this](void* self, SCallbackInfo& info, std::any e) {
auto E = std::any_cast<ITouch::SMotionEvent>(e);
if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) {
auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus);
if (!surf)
return;
const auto box = surf->getSurfaceBoxGlobal();
if (!box.has_value())
return;
dnd.focusedDevice->sendMotion(E.timeMs, E.pos);
LOGM(LOG, "Drag motion {}", E.pos);
}
});
// make a new offer, etc
updateDrag();
}
void CWLDataDeviceProtocol::updateDrag() {
if (!dnd.currentSource)
return;
if (dnd.focusedDevice)
dnd.focusedDevice->sendLeave();
if (!g_pSeatManager->state.keyboardFocusResource)
return;
dnd.focusedDevice = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client());
if (!dnd.focusedDevice)
return;
// make a new offer
const auto OFFER = m_vOffers.emplace_back(
makeShared<CWLDataOfferResource>(makeShared<CWlDataOffer>(dnd.focusedDevice->resource->client(), dnd.focusedDevice->resource->version(), 0), dnd.currentSource.lock()));
if (!OFFER->good()) {
dnd.currentSource->resource->noMemory();
m_vOffers.pop_back();
return;
}
LOGM(LOG, "New dnd offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)dnd.currentSource.get());
dnd.focusedDevice->sendDataOffer(OFFER);
OFFER->sendData();
dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.keyboardFocus,
Vector2D{g_pSeatManager->state.keyboardFocus->current.width, g_pSeatManager->state.keyboardFocus->current.height} / 2.F, OFFER);
}
void CWLDataDeviceProtocol::resetDndState() {
dnd.dndSurface = nullptr;
dnd.hyprListener_dndSurfaceDestroy.removeCallback();
dnd.hyprListener_dndSurfaceCommit.removeCallback();
dnd.mouseButton.reset();
dnd.mouseMove.reset();
dnd.touchUp.reset();
dnd.touchMove.reset();
}
void CWLDataDeviceProtocol::dropDrag() {
if (!dnd.focusedDevice || !dnd.currentSource) {
if (dnd.currentSource)
abortDrag();
return;
}
dnd.currentSource->sendDndDropPerformed();
dnd.focusedDevice->sendDrop();
dnd.focusedDevice->sendLeave();
resetDndState();
if (dnd.overriddenCursor)
g_pInputManager->unsetCursorImage();
dnd.overriddenCursor = false;
}
void CWLDataDeviceProtocol::completeDrag() {
resetDndState();
if (!dnd.focusedDevice || !dnd.currentSource)
return;
dnd.currentSource->sendDndFinished();
dnd.focusedDevice.reset();
dnd.currentSource.reset();
g_pSeatManager->resendEnterEvents();
}
void CWLDataDeviceProtocol::abortDrag() {
resetDndState();
if (dnd.overriddenCursor)
g_pInputManager->unsetCursorImage();
dnd.overriddenCursor = false;
if (!dnd.focusedDevice || !dnd.currentSource)
return;
dnd.focusedDevice->sendLeave();
dnd.currentSource->cancelled();
dnd.focusedDevice.reset();
dnd.currentSource.reset();
g_pSeatManager->resendEnterEvents();
}
void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
if (!dnd.dndSurface || !wlr_surface_get_texture(dnd.dndSurface))
return;
const auto POS = g_pInputManager->getMouseCoordsInternal();
CBox box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}}
.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F)
.scale(pMonitor->scale);
g_pHyprOpenGL->renderTexture(wlr_surface_get_texture(dnd.dndSurface), &box, 1.F);
box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}}.translate(g_pPointerManager->cursorSizeLogical() / 2.F);
g_pHyprRenderer->damageBox(&box);
wlr_surface_send_frame_done(dnd.dndSurface, when);
}
bool CWLDataDeviceProtocol::dndActive() {
return dnd.currentSource;
}

View File

@ -0,0 +1,194 @@
#pragma once
/*
Implementations for:
- wl_data_offer
- wl_data_source
- wl_data_device
- wl_data_device_manager
*/
#include <memory>
#include <vector>
#include <cstdint>
#include "../WaylandProtocol.hpp"
#include <wayland-server-protocol.h>
#include "wayland.hpp"
#include "../../helpers/signal/Signal.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../types/DataDevice.hpp"
class CWLDataDeviceResource;
class CWLDataDeviceManagerResource;
class CWLDataSourceResource;
class CWLDataOfferResource;
class CMonitor;
class CWLDataOfferResource {
public:
CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataSource> source_);
bool good();
void sendData();
WP<IDataSource> source;
bool dead = false;
bool accepted = false;
bool recvd = false;
uint32_t actions = 0;
private:
SP<CWlDataOffer> resource;
wl_client* pClient = nullptr;
friend class CWLDataDeviceResource;
};
class CWLDataSourceResource : public IDataSource {
public:
CWLDataSourceResource(SP<CWlDataSource> resource_, SP<CWLDataDeviceResource> device_);
~CWLDataSourceResource();
static SP<CWLDataSourceResource> fromResource(wl_resource*);
bool good();
virtual std::vector<std::string> mimes();
virtual void send(const std::string& mime, uint32_t fd);
virtual void accepted(const std::string& mime);
virtual void cancelled();
virtual bool hasDnd();
virtual bool dndDone();
virtual void error(uint32_t code, const std::string& msg);
void sendDndDropPerformed();
void sendDndFinished();
void sendDndAction(wl_data_device_manager_dnd_action a);
bool used = false;
bool dnd = false;
bool dndSuccess = false;
WP<CWLDataDeviceResource> device;
WP<CWLDataSourceResource> self;
std::vector<std::string> mimeTypes;
uint32_t actions = 0;
private:
SP<CWlDataSource> resource;
wl_client* pClient = nullptr;
friend class CWLDataDeviceProtocol;
};
class CWLDataDeviceResource {
public:
CWLDataDeviceResource(SP<CWlDataDevice> resource_);
bool good();
wl_client* client();
void sendDataOffer(SP<CWLDataOfferResource> offer);
void sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP<CWLDataOfferResource> offer);
void sendLeave();
void sendMotion(uint32_t timeMs, const Vector2D& local);
void sendDrop();
void sendSelection(SP<CWLDataOfferResource> offer);
WP<CWLDataDeviceResource> self;
private:
SP<CWlDataDevice> resource;
wl_client* pClient = nullptr;
friend class CWLDataDeviceProtocol;
};
class CWLDataDeviceManagerResource {
public:
CWLDataDeviceManagerResource(SP<CWlDataDeviceManager> resource_);
bool good();
WP<CWLDataDeviceResource> device;
std::vector<WP<CWLDataSourceResource>> sources;
private:
SP<CWlDataDeviceManager> resource;
};
class CWLDataDeviceProtocol : public IWaylandProtocol {
public:
CWLDataDeviceProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
// renders and damages the dnd icon, if present
void renderDND(CMonitor* pMonitor, timespec* when);
// for inputmgr to force refocus
// TODO: move handling to seatmgr
bool dndActive();
private:
void destroyResource(CWLDataDeviceManagerResource* resource);
void destroyResource(CWLDataDeviceResource* resource);
void destroyResource(CWLDataSourceResource* resource);
void destroyResource(CWLDataOfferResource* resource);
//
std::vector<SP<CWLDataDeviceManagerResource>> m_vManagers;
std::vector<SP<CWLDataDeviceResource>> m_vDevices;
std::vector<SP<CWLDataSourceResource>> m_vSources;
std::vector<SP<CWLDataOfferResource>> m_vOffers;
//
void onDestroyDataSource(WP<CWLDataSourceResource> source);
void setSelection(SP<IDataSource> source);
void sendSelectionToDevice(SP<CWLDataDeviceResource> dev, SP<IDataSource> sel);
void updateSelection();
void onKeyboardFocus();
struct {
WP<CWLDataDeviceResource> focusedDevice;
WP<CWLDataSourceResource> currentSource;
wlr_surface* dndSurface = nullptr;
wlr_surface* originSurface = nullptr; // READ-ONLY
bool overriddenCursor = false;
DYNLISTENER(dndSurfaceDestroy);
DYNLISTENER(dndSurfaceCommit);
// for ending a dnd
SP<HOOK_CALLBACK_FN> mouseMove;
SP<HOOK_CALLBACK_FN> mouseButton;
SP<HOOK_CALLBACK_FN> touchUp;
SP<HOOK_CALLBACK_FN> touchMove;
} dnd;
void abortDrag();
void initiateDrag(WP<CWLDataSourceResource> currentSource, wlr_surface* dragSurface, wlr_surface* origin);
void updateDrag();
void dropDrag();
void completeDrag();
void resetDndState();
//
SP<CWLDataDeviceResource> dataDeviceForClient(wl_client*);
friend class CSeatManager;
friend class CWLDataDeviceManagerResource;
friend class CWLDataDeviceResource;
friend class CWLDataSourceResource;
friend class CWLDataOfferResource;
struct {
CHyprSignalListener onKeyboardFocusChange;
} listeners;
};
namespace PROTO {
inline UP<CWLDataDeviceProtocol> data;
};

437
src/protocols/core/Seat.cpp Normal file
View File

@ -0,0 +1,437 @@
#include "Seat.hpp"
#include "../../devices/IKeyboard.hpp"
#include "../../managers/SeatManager.hpp"
#include "../../config/ConfigValue.hpp"
#include <algorithm>
#include <fcntl.h>
#define LOGM PROTO::seat->protoLog
CWLTouchResource::CWLTouchResource(SP<CWlTouch> resource_, SP<CWLSeatResource> owner_) : owner(owner_), resource(resource_) {
if (!good())
return;
resource->setRelease([this](CWlTouch* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([this](CWlTouch* r) { PROTO::seat->destroyResource(this); });
}
bool CWLTouchResource::good() {
return resource->resource();
}
void CWLTouchResource::sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner)
return;
if (currentSurface) {
LOGM(WARN, "requested CWLTouchResource::sendDown without sendUp first.");
sendUp(timeMs, id);
}
ASSERT(wl_resource_get_client(surface->resource) == owner->client());
currentSurface = surface;
hyprListener_surfaceDestroy.initCallback(
&surface->events.destroy, [this, id, timeMs](void* owner, void* data) { sendUp(timeMs + 10 /* hack */, id); }, this, "CWLTouchResource");
// FIXME:
// fix this once we get our own wlr_surface, this is horrible
resource->sendDownRaw(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->resource, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) {
if (!owner || !currentSurface)
return;
resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id);
currentSurface = nullptr;
hyprListener_surfaceDestroy.removeCallback();
}
void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {
if (!owner || !currentSurface)
return;
resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLTouchResource::sendFrame() {
if (!owner || !currentSurface)
return;
resource->sendFrame();
}
void CWLTouchResource::sendCancel() {
if (!owner || !currentSurface)
return;
resource->sendCancel();
}
void CWLTouchResource::sendShape(int32_t id, const Vector2D& shape) {
if (!owner || !currentSurface || resource->version() < 6)
return;
resource->sendShape(id, wl_fixed_from_double(shape.x), wl_fixed_from_double(shape.y));
}
void CWLTouchResource::sendOrientation(int32_t id, double angle) {
if (!owner || !currentSurface || resource->version() < 6)
return;
resource->sendOrientation(id, wl_fixed_from_double(angle));
}
CWLPointerResource::CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResource> owner_) : owner(owner_), resource(resource_) {
if (!good())
return;
resource->setRelease([this](CWlPointer* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([this](CWlPointer* r) { PROTO::seat->destroyResource(this); });
resource->setSetCursor([this](CWlPointer* r, uint32_t serial, wl_resource* surf, int32_t hotX, int32_t hotY) {
if (!owner) {
LOGM(ERR, "Client bug: setCursor when seatClient is already dead");
return;
}
g_pSeatManager->onSetCursor(owner.lock(), serial, surf ? wlr_surface_from_resource(surf) : nullptr, {hotX, hotY});
});
}
bool CWLPointerResource::good() {
return resource->resource();
}
void CWLPointerResource::sendEnter(wlr_surface* surface, const Vector2D& local) {
if (!owner || currentSurface == surface)
return;
if (currentSurface) {
LOGM(WARN, "requested CWLPointerResource::sendEnter without sendLeave first.");
sendLeave();
}
ASSERT(wl_resource_get_client(surface->resource) == owner->client());
currentSurface = surface;
hyprListener_surfaceDestroy.initCallback(
&surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLPointerResource");
resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLPointerResource::sendLeave() {
if (!owner || !currentSurface)
return;
resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource);
currentSurface = nullptr;
hyprListener_surfaceDestroy.removeCallback();
}
void CWLPointerResource::sendMotion(uint32_t timeMs, const Vector2D& local) {
if (!owner || !currentSurface)
return;
resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
}
void CWLPointerResource::sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state) {
if (!owner || !currentSurface)
return;
resource->sendButton(g_pSeatManager->nextSerial(owner.lock()), timeMs, button, state);
}
void CWLPointerResource::sendAxis(uint32_t timeMs, wl_pointer_axis axis, double value) {
if (!owner || !currentSurface)
return;
resource->sendAxis(timeMs, axis, wl_fixed_from_double(value));
}
void CWLPointerResource::sendFrame() {
if (!owner || resource->version() < 5)
return;
resource->sendFrame();
}
void CWLPointerResource::sendAxisSource(wl_pointer_axis_source source) {
if (!owner || !currentSurface || resource->version() < 5)
return;
resource->sendAxisSource(source);
}
void CWLPointerResource::sendAxisStop(uint32_t timeMs, wl_pointer_axis axis) {
if (!owner || !currentSurface || resource->version() < 5)
return;
resource->sendAxisStop(timeMs, axis);
}
void CWLPointerResource::sendAxisDiscrete(wl_pointer_axis axis, int32_t discrete) {
if (!owner || !currentSurface || resource->version() < 5)
return;
resource->sendAxisDiscrete(axis, discrete);
}
void CWLPointerResource::sendAxisValue120(wl_pointer_axis axis, int32_t value120) {
if (!owner || !currentSurface || resource->version() < 8)
return;
resource->sendAxisValue120(axis, value120);
}
void CWLPointerResource::sendAxisRelativeDirection(wl_pointer_axis axis, wl_pointer_axis_relative_direction direction) {
if (!owner || !currentSurface || resource->version() < 9)
return;
resource->sendAxisRelativeDirection(axis, direction);
}
CWLKeyboardResource::CWLKeyboardResource(SP<CWlKeyboard> resource_, SP<CWLSeatResource> owner_) : owner(owner_), resource(resource_) {
if (!good())
return;
resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); });
resource->setOnDestroy([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); });
static auto REPEAT = CConfigValue<Hyprlang::INT>("input:repeat_rate");
static auto DELAY = CConfigValue<Hyprlang::INT>("input:repeat_delay");
sendKeymap(g_pSeatManager->keyboard.lock());
repeatInfo(*REPEAT, *DELAY);
}
bool CWLKeyboardResource::good() {
return resource->resource();
}
void CWLKeyboardResource::sendKeymap(SP<IKeyboard> keyboard) {
wl_keyboard_keymap_format format = keyboard ? WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 : WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP;
int fd;
uint32_t size;
if (keyboard) {
fd = keyboard->wlr()->keymap_fd;
size = keyboard->wlr()->keymap_size;
} else {
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
LOGM(ERR, "Failed to open /dev/null");
return;
}
size = 0;
}
resource->sendKeymap(format, fd, size);
if (!keyboard)
close(fd);
}
void CWLKeyboardResource::sendEnter(wlr_surface* surface) {
if (!owner || currentSurface == surface)
return;
if (currentSurface) {
LOGM(WARN, "requested CWLKeyboardResource::sendEnter without sendLeave first.");
sendLeave();
}
ASSERT(wl_resource_get_client(surface->resource) == owner->client());
currentSurface = surface;
hyprListener_surfaceDestroy.initCallback(
&surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLKeyboardResource");
wl_array arr;
wl_array_init(&arr);
resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, &arr);
wl_array_release(&arr);
}
void CWLKeyboardResource::sendLeave() {
if (!owner || !currentSurface)
return;
resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource);
currentSurface = nullptr;
hyprListener_surfaceDestroy.removeCallback();
}
void CWLKeyboardResource::sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state) {
if (!owner || !currentSurface)
return;
resource->sendKey(g_pSeatManager->nextSerial(owner.lock()), timeMs, key, state);
}
void CWLKeyboardResource::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
if (!owner || !currentSurface)
return;
resource->sendModifiers(g_pSeatManager->nextSerial(owner.lock()), depressed, latched, locked, group);
}
void CWLKeyboardResource::repeatInfo(uint32_t rate, uint32_t delayMs) {
if (!owner || resource->version() < 4)
return;
resource->sendRepeatInfo(rate, delayMs);
}
CWLSeatResource::CWLSeatResource(SP<CWlSeat> resource_) : resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CWlSeat* r) {
events.destroy.emit();
PROTO::seat->destroyResource(this);
});
resource->setRelease([this](CWlSeat* r) {
events.destroy.emit();
PROTO::seat->destroyResource(this);
});
pClient = resource->client();
resource->setGetKeyboard([this](CWlSeat* r, uint32_t id) {
const auto RESOURCE = PROTO::seat->m_vKeyboards.emplace_back(makeShared<CWLKeyboardResource>(makeShared<CWlKeyboard>(r->client(), r->version(), id), self.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::seat->m_vKeyboards.pop_back();
return;
}
keyboards.push_back(RESOURCE);
});
resource->setGetPointer([this](CWlSeat* r, uint32_t id) {
const auto RESOURCE = PROTO::seat->m_vPointers.emplace_back(makeShared<CWLPointerResource>(makeShared<CWlPointer>(r->client(), r->version(), id), self.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::seat->m_vPointers.pop_back();
return;
}
pointers.push_back(RESOURCE);
});
resource->setGetTouch([this](CWlSeat* r, uint32_t id) {
const auto RESOURCE = PROTO::seat->m_vTouches.emplace_back(makeShared<CWLTouchResource>(makeShared<CWlTouch>(r->client(), r->version(), id), self.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::seat->m_vTouches.pop_back();
return;
}
touches.push_back(RESOURCE);
});
if (resource->version() >= 2)
resource->sendName(HL_SEAT_NAME);
sendCapabilities(PROTO::seat->currentCaps);
}
CWLSeatResource::~CWLSeatResource() {
events.destroy.emit();
}
void CWLSeatResource::sendCapabilities(uint32_t caps) {
uint32_t wlCaps = 0;
if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)
wlCaps |= WL_SEAT_CAPABILITY_KEYBOARD;
if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)
wlCaps |= WL_SEAT_CAPABILITY_POINTER;
if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)
wlCaps |= WL_SEAT_CAPABILITY_TOUCH;
resource->sendCapabilities((wl_seat_capability)wlCaps);
}
bool CWLSeatResource::good() {
return resource->resource();
}
wl_client* CWLSeatResource::client() {
return pClient;
}
CWLSeatProtocol::CWLSeatProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
void CWLSeatProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vSeatResources.emplace_back(makeShared<CWLSeatResource>(makeShared<CWlSeat>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vSeatResources.pop_back();
return;
}
RESOURCE->self = RESOURCE;
LOGM(LOG, "New seat resource bound at {:x}", (uintptr_t)RESOURCE.get());
events.newSeatResource.emit(RESOURCE);
}
void CWLSeatProtocol::destroyResource(CWLSeatResource* seat) {
std::erase_if(m_vSeatResources, [&](const auto& other) { return other.get() == seat; });
}
void CWLSeatProtocol::destroyResource(CWLKeyboardResource* resource) {
std::erase_if(m_vKeyboards, [&](const auto& other) { return other.get() == resource; });
}
void CWLSeatProtocol::destroyResource(CWLPointerResource* resource) {
std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == resource; });
}
void CWLSeatProtocol::destroyResource(CWLTouchResource* resource) {
std::erase_if(m_vTouches, [&](const auto& other) { return other.get() == resource; });
}
void CWLSeatProtocol::updateCapabilities(uint32_t caps) {
if (caps == currentCaps)
return;
currentCaps = caps;
for (auto& s : m_vSeatResources) {
s->sendCapabilities(caps);
}
}
void CWLSeatProtocol::updateKeymap() {
for (auto& k : m_vKeyboards) {
k->sendKeymap(g_pSeatManager->keyboard.lock());
}
}
void CWLSeatProtocol::updateRepeatInfo(uint32_t rate, uint32_t delayMs) {
for (auto& k : m_vKeyboards) {
k->repeatInfo(rate, delayMs);
}
}
SP<CWLSeatResource> CWLSeatProtocol::seatResourceForClient(wl_client* client) {
for (auto& r : m_vSeatResources) {
if (r->client() == client)
return r;
}
return nullptr;
}

163
src/protocols/core/Seat.hpp Normal file
View File

@ -0,0 +1,163 @@
#pragma once
/*
Implementations for:
- wl_seat
- wl_keyboard
- wl_pointer
- wl_touch
*/
#include <memory>
#include <vector>
#include <cstdint>
#include "../WaylandProtocol.hpp"
#include <wayland-server-protocol.h>
#include "wayland.hpp"
#include "../../helpers/signal/Signal.hpp"
#include "../../helpers/Vector2D.hpp"
constexpr const char* HL_SEAT_NAME = "Hyprland";
class IKeyboard;
class CWLPointerResource;
class CWLKeyboardResource;
class CWLTouchResource;
class CWLSeatResource;
class CWLTouchResource {
public:
CWLTouchResource(SP<CWlTouch> resource_, SP<CWLSeatResource> owner_);
bool good();
void sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local);
void sendUp(uint32_t timeMs, int32_t id);
void sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local);
void sendFrame();
void sendCancel();
void sendShape(int32_t id, const Vector2D& shape);
void sendOrientation(int32_t id, double angle);
WP<CWLSeatResource> owner;
private:
SP<CWlTouch> resource;
wlr_surface* currentSurface = nullptr;
DYNLISTENER(surfaceDestroy);
};
class CWLPointerResource {
public:
CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResource> owner_);
bool good();
void sendEnter(wlr_surface* surface, const Vector2D& local);
void sendLeave();
void sendMotion(uint32_t timeMs, const Vector2D& local);
void sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state);
void sendAxis(uint32_t timeMs, wl_pointer_axis axis, double value);
void sendFrame();
void sendAxisSource(wl_pointer_axis_source source);
void sendAxisStop(uint32_t timeMs, wl_pointer_axis axis);
void sendAxisDiscrete(wl_pointer_axis axis, int32_t discrete);
void sendAxisValue120(wl_pointer_axis axis, int32_t value120);
void sendAxisRelativeDirection(wl_pointer_axis axis, wl_pointer_axis_relative_direction direction);
WP<CWLSeatResource> owner;
private:
SP<CWlPointer> resource;
wlr_surface* currentSurface = nullptr;
DYNLISTENER(surfaceDestroy);
};
class CWLKeyboardResource {
public:
CWLKeyboardResource(SP<CWlKeyboard> resource_, SP<CWLSeatResource> owner_);
bool good();
void sendKeymap(SP<IKeyboard> keeb);
void sendEnter(wlr_surface* surface);
void sendLeave();
void sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state);
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
void repeatInfo(uint32_t rate, uint32_t delayMs);
WP<CWLSeatResource> owner;
private:
SP<CWlKeyboard> resource;
wlr_surface* currentSurface = nullptr;
DYNLISTENER(surfaceDestroy);
};
class CWLSeatResource {
public:
CWLSeatResource(SP<CWlSeat> resource_);
~CWLSeatResource();
void sendCapabilities(uint32_t caps); // uses IHID capabilities
bool good();
wl_client* client();
std::vector<WP<CWLPointerResource>> pointers;
std::vector<WP<CWLKeyboardResource>> keyboards;
std::vector<WP<CWLTouchResource>> touches;
WP<CWLSeatResource> self;
struct {
CSignal destroy;
} events;
private:
SP<CWlSeat> resource;
wl_client* pClient = nullptr;
};
class CWLSeatProtocol : public IWaylandProtocol {
public:
CWLSeatProtocol(const wl_interface* iface, const int& ver, const std::string& name);
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
struct {
CSignal newSeatResource; // SP<CWLSeatResource>
} events;
private:
void updateCapabilities(uint32_t caps); // in IHID caps
void updateRepeatInfo(uint32_t rate, uint32_t delayMs);
void updateKeymap();
void destroyResource(CWLSeatResource* resource);
void destroyResource(CWLKeyboardResource* resource);
void destroyResource(CWLTouchResource* resource);
void destroyResource(CWLPointerResource* resource);
//
std::vector<SP<CWLSeatResource>> m_vSeatResources;
std::vector<SP<CWLKeyboardResource>> m_vKeyboards;
std::vector<SP<CWLTouchResource>> m_vTouches;
std::vector<SP<CWLPointerResource>> m_vPointers;
SP<CWLSeatResource> seatResourceForClient(wl_client* client);
//
uint32_t currentCaps = 0;
friend class CWLSeatResource;
friend class CWLKeyboardResource;
friend class CWLTouchResource;
friend class CWLPointerResource;
friend class CSeatManager;
};
namespace PROTO {
inline UP<CWLSeatProtocol> seat;
};

View File

@ -0,0 +1,17 @@
#include "DataDevice.hpp"
bool IDataSource::hasDnd() {
return false;
}
bool IDataSource::dndDone() {
return false;
}
bool IDataSource::used() {
return wasUsed;
}
void IDataSource::markUsed() {
wasUsed = true;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <string>
#include <vector>
#include <cstdint>
#include "../../helpers/signal/Signal.hpp"
class IDataSource {
public:
IDataSource() {}
virtual ~IDataSource() {}
virtual std::vector<std::string> mimes() = 0;
virtual void send(const std::string& mime, uint32_t fd) = 0;
virtual void accepted(const std::string& mime) = 0;
virtual void cancelled() = 0;
virtual bool hasDnd();
virtual bool dndDone();
virtual bool used();
virtual void markUsed();
virtual void error(uint32_t code, const std::string& msg) = 0;
struct {
CSignal destroy;
} events;
private:
bool wasUsed = false;
};

View File

@ -1,9 +1,9 @@
#include <random>
#include <pango/pangocairo.h>
#include "Shaders.hpp"
#include "OpenGL.hpp"
#include "../Compositor.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "Shaders.hpp"
#include <random>
#include "../config/ConfigValue.hpp"
#include "../desktop/LayerSurface.hpp"
#include "../protocols/LayerShell.hpp"
@ -2097,25 +2097,36 @@ void CHyprOpenGLImpl::renderMirrored() {
}
void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) {
static auto PSPLASHCOLOR = CConfigValue<Hyprlang::INT>("misc:col.splash");
static auto PSPLASHCOLOR = CConfigValue<Hyprlang::INT>("misc:col.splash");
static auto PSPLASHFONT = CConfigValue<std::string>("misc:splash_font_family");
static auto FALLBACKFONT = CConfigValue<std::string>("misc:font_family");
static auto PSPLASHFONT = CConfigValue<std::string>("misc:splash_font_family");
const auto FONTFAMILY = *PSPLASHFONT != STRVAL_EMPTY ? *PSPLASHFONT : *FALLBACKFONT;
const auto FONTSIZE = (int)(size.y / 76);
const auto COLOR = CColor(*PSPLASHCOLOR);
cairo_select_font_face(CAIRO, (*PSPLASHFONT).c_str(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
PangoLayout* layoutText = pango_cairo_create_layout(CAIRO);
PangoFontDescription* pangoFD = pango_font_description_new();
const auto FONTSIZE = (int)(size.y / 76);
cairo_set_font_size(CAIRO, FONTSIZE);
const auto COLOR = CColor(*PSPLASHCOLOR);
pango_font_description_set_family_static(pangoFD, FONTFAMILY.c_str());
pango_font_description_set_absolute_size(pangoFD, FONTSIZE * PANGO_SCALE);
pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL);
pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL);
pango_layout_set_font_description(layoutText, pangoFD);
cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a);
cairo_text_extents_t textExtents;
cairo_text_extents(CAIRO, g_pCompositor->m_szCurrentSplash.c_str(), &textExtents);
int textW = 0, textH = 0;
pango_layout_set_text(layoutText, g_pCompositor->m_szCurrentSplash.c_str(), -1);
pango_layout_get_size(layoutText, &textW, &textH);
textW /= PANGO_SCALE;
textH /= PANGO_SCALE;
cairo_move_to(CAIRO, (size.x - textExtents.width) / 2.0, size.y - textExtents.height + offsetY);
cairo_move_to(CAIRO, (size.x - textW) / 2.0, size.y - textH * 2 + offsetY);
pango_cairo_show_layout(CAIRO, layoutText);
cairo_show_text(CAIRO, g_pCompositor->m_szCurrentSplash.c_str());
pango_font_description_free(pangoFD);
g_object_unref(layoutText);
cairo_surface_flush(CAIROSURFACE);
}

View File

@ -10,7 +10,9 @@
#include "../desktop/LayerSurface.hpp"
#include "../protocols/SessionLock.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/XDGShell.hpp"
#include "../protocols/PresentationTime.hpp"
#include "../protocols/core/DataDevice.hpp"
extern "C" {
#include <xf86drm.h>
@ -615,9 +617,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec
if (mode == RENDER_PASS_ALL || mode == RENDER_PASS_POPUP) {
if (!pWindow->m_bIsX11) {
CBox geom;
wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr());
geom.applyFromWlr();
CBox geom = pWindow->m_pXDGSurface->current.geometry;
renderdata.x -= geom.x;
renderdata.y -= geom.y;
@ -643,7 +643,20 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec
if (pWindow->m_sAdditionalConfigData.nearestNeighbor.toUnderlying())
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true;
wlr_xdg_surface_for_each_popup_surface(pWindow->m_uSurface.xdg, renderSurface, &renderdata);
pWindow->m_pPopupHead->breadthfirst(
[](CPopup* popup, void* data) {
if (!popup->m_sWLSurface.wlr())
return;
auto pos = popup->coordsRelativeToParent();
auto rd = (SRenderData*)data;
Vector2D oldPos = {rd->x, rd->y};
rd->x += pos.x;
rd->y += pos.y;
wlr_surface_for_each_surface(popup->m_sWLSurface.wlr(), renderSurface, rd);
rd->x = oldPos.x;
rd->y = oldPos.y;
},
&renderdata);
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
@ -1000,9 +1013,7 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurfa
if (!main || !pWindow)
return;
CBox geom;
wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr());
geom.applyFromWlr();
CBox geom = pWindow->m_pXDGSurface->current.geometry;
// ignore X and Y, adjust uv
if (geom.x != 0 || geom.y != 0 || geom.width > pWindow->m_vRealSize.value().x || geom.height > pWindow->m_vRealSize.value().y) {
@ -1796,19 +1807,7 @@ void CHyprRenderer::damageMirrorsWith(CMonitor* pMonitor, const CRegion& pRegion
}
void CHyprRenderer::renderDragIcon(CMonitor* pMonitor, timespec* time) {
if (!(g_pInputManager->m_sDrag.dragIcon && g_pInputManager->m_sDrag.iconMapped && g_pInputManager->m_sDrag.dragIcon->surface))
return;
SRenderData renderdata = {pMonitor, time, g_pInputManager->m_sDrag.pos.x, g_pInputManager->m_sDrag.pos.y};
renderdata.surface = g_pInputManager->m_sDrag.dragIcon->surface;
renderdata.w = g_pInputManager->m_sDrag.dragIcon->surface->current.width;
renderdata.h = g_pInputManager->m_sDrag.dragIcon->surface->current.height;
wlr_surface_for_each_surface(g_pInputManager->m_sDrag.dragIcon->surface, renderSurface, &renderdata);
CBox box = {g_pInputManager->m_sDrag.pos.x - 2, g_pInputManager->m_sDrag.pos.y - 2, g_pInputManager->m_sDrag.dragIcon->surface->current.width + 4,
g_pInputManager->m_sDrag.dragIcon->surface->current.height + 4};
g_pHyprRenderer->damageBox(&box);
PROTO::data->renderDND(pMonitor, time);
}
DAMAGETRACKINGMODES CHyprRenderer::damageTrackingModeFromStr(const std::string& mode) {
@ -2255,9 +2254,6 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR
void CHyprRenderer::setCursorSurface(CWLSurface* surf, int hotspotX, int hotspotY, bool force) {
m_bCursorHasSurface = surf;
if (surf == m_sLastCursorData.surf && hotspotX == m_sLastCursorData.hotspotX && hotspotY == m_sLastCursorData.hotspotY && !force)
return;
m_sLastCursorData.name = "";
m_sLastCursorData.surf = surf;
m_sLastCursorData.hotspotX = hotspotX;
@ -2493,8 +2489,8 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) {
const auto PWORKSPACE = pMonitor->activeWorkspace;
if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || g_pInputManager->m_sDrag.drag || g_pCompositor->m_sSeat.exclusiveClient || pMonitor->activeSpecialWorkspace ||
PWORKSPACE->m_fAlpha.value() != 1.f || PWORKSPACE->m_vRenderOffset.value() != Vector2D{})
if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || PROTO::data->dndActive() || pMonitor->activeSpecialWorkspace || PWORKSPACE->m_fAlpha.value() != 1.f ||
PWORKSPACE->m_vRenderOffset.value() != Vector2D{})
return;
const auto PCANDIDATE = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
@ -2533,8 +2529,7 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) {
if (PCANDIDATE->m_bIsX11) {
surfaceCount = 1;
} else {
wlr_xdg_surface_for_each_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount);
wlr_xdg_surface_for_each_popup_surface(PCANDIDATE->m_uSurface.xdg, countSubsurfacesIter, &surfaceCount);
surfaceCount = PCANDIDATE->popupsCount() + PCANDIDATE->surfacesCount();
}
if (surfaceCount > 1)

View File

@ -1,6 +1,7 @@
#include "CHyprGroupBarDecoration.hpp"
#include "../../Compositor.hpp"
#include "../../config/ConfigValue.hpp"
#include "managers/LayoutManager.hpp"
#include <ranges>
#include <pango/pangocairo.h>
@ -12,6 +13,7 @@ static CTexture m_tGradientLockedInactive;
constexpr int BAR_INDICATOR_HEIGHT = 3;
constexpr int BAR_PADDING_OUTER_VERT = 2;
constexpr int BAR_PADDING_OUTER_HORZ = 2;
constexpr int BAR_TEXT_PAD = 2;
constexpr int BAR_HORIZONTAL_PADDING = 2;
@ -32,6 +34,7 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
static auto PRENDERTITLES = CConfigValue<Hyprlang::INT>("group:groupbar:render_titles");
static auto PGRADIENTS = CConfigValue<Hyprlang::INT>("group:groupbar:gradients");
static auto PPRIORITY = CConfigValue<Hyprlang::INT>("group:groupbar:priority");
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
SDecorationPositioningInfo info;
info.policy = DECORATION_POSITION_STICKY;
@ -39,16 +42,20 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
info.priority = *PPRIORITY;
info.reserved = true;
if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate)
info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2}, {0, 0}};
else
if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate) {
if (*PSTACKED) {
const auto ONEBARHEIGHT = BAR_PADDING_OUTER_VERT + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0);
info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + BAR_PADDING_OUTER_VERT}, {0, 0}};
} else
info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2}, {0, 0}};
} else
info.desiredExtents = {{0, 0}, {0, 0}};
return info;
}
void CHyprGroupBarDecoration::onPositioningReply(const SDecorationPositioningReply& reply) {
m_bAssignedBox = reply.assignedGeometry;
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pWindow.lock());
}
eDecorationType CHyprGroupBarDecoration::getDecorationType() {
@ -96,24 +103,31 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) {
static auto PTITLEFONTSIZE = CConfigValue<Hyprlang::INT>("group:groupbar:font_size");
static auto PHEIGHT = CConfigValue<Hyprlang::INT>("group:groupbar:height");
static auto PGRADIENTS = CConfigValue<Hyprlang::INT>("group:groupbar:gradients");
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
if (!*PENABLED || !m_pWindow->m_sSpecialRenderData.decorate)
return;
const auto ASSIGNEDBOX = assignedBoxGlobal();
m_fBarWidth = (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw;
const auto ONEBARHEIGHT = BAR_PADDING_OUTER_VERT + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0);
m_fBarWidth = *PSTACKED ? ASSIGNEDBOX.w : (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw;
m_fBarHeight = *PSTACKED ? ((ASSIGNEDBOX.h - 2 - BAR_PADDING_OUTER_VERT) - BAR_PADDING_OUTER_VERT * (barsToDraw)) / barsToDraw : ASSIGNEDBOX.h - BAR_PADDING_OUTER_VERT;
const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0) + 2;
if (DESIREDHEIGHT != ASSIGNEDBOX.h)
const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + BAR_PADDING_OUTER_VERT : BAR_PADDING_OUTER_VERT * 2 + ONEBARHEIGHT;
if (DESIREDHEIGHT != ASSIGNEDBOX.h) {
g_pDecorationPositioner->repositionDeco(this);
}
int xoff = 0;
int yoff = 0;
for (int i = 0; i < barsToDraw; ++i) {
CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y + ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
BAR_INDICATOR_HEIGHT};
const auto WINDOWINDEX = *PSTACKED ? m_dwGroupMembers.size() - i - 1 : i;
CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y + ASSIGNEDBOX.h - yoff - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
BAR_INDICATOR_HEIGHT};
if (rect.width <= 0 || rect.height <= 0)
break;
@ -133,38 +147,43 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) {
const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE;
const auto* const PCOLINACTIVE = GROUPLOCKED ? GROUPCOLINACTIVELOCKED : GROUPCOLINACTIVE;
CColor color = m_dwGroupMembers[i].lock() == g_pCompositor->m_pLastWindow.lock() ? PCOLACTIVE->m_vColors[0] : PCOLINACTIVE->m_vColors[0];
CColor color = m_dwGroupMembers[WINDOWINDEX].lock() == g_pCompositor->m_pLastWindow.lock() ? PCOLACTIVE->m_vColors[0] : PCOLINACTIVE->m_vColors[0];
color.a *= a;
g_pHyprOpenGL->renderRect(&rect, color);
rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x,
ASSIGNEDBOX.y - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth,
ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2};
ASSIGNEDBOX.y + ASSIGNEDBOX.h - yoff - ONEBARHEIGHT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
(*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)};
rect.scale(pMonitor->scale);
if (*PGRADIENTS) {
const auto& GRADIENTTEX = (m_dwGroupMembers[i].lock() == g_pCompositor->m_pLastWindow.lock() ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) :
(GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive));
const auto& GRADIENTTEX =
(m_dwGroupMembers[WINDOWINDEX].lock() == g_pCompositor->m_pLastWindow.lock() ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) :
(GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive));
if (GRADIENTTEX.m_iTexID != 0)
g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0);
}
if (*PRENDERTITLES) {
CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle);
CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[WINDOWINDEX]->m_szTitle);
if (!pTitleTex)
pTitleTex = m_sTitleTexs.titleTexs
.emplace_back(std::make_unique<CTitleTex>(
m_dwGroupMembers[i].lock(), Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale))
.get();
pTitleTex =
m_sTitleTexs.titleTexs
.emplace_back(std::make_unique<CTitleTex>(m_dwGroupMembers[WINDOWINDEX].lock(),
Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale))
.get();
rect.y += (ASSIGNEDBOX.h / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale;
rect.y += (*PHEIGHT / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale;
rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale;
g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f);
}
xoff += BAR_HORIZONTAL_PADDING + m_fBarWidth;
if (*PSTACKED)
yoff += ONEBARHEIGHT;
else
xoff += BAR_HORIZONTAL_PADDING + m_fBarWidth;
}
if (*PRENDERTITLES)
@ -190,11 +209,13 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
static auto FALLBACKFONT = CConfigValue<std::string>("misc:font_family");
static auto PTITLEFONTFAMILY = CConfigValue<std::string>("group:groupbar:font_family");
static auto PTITLEFONTSIZE = CConfigValue<Hyprlang::INT>("group:groupbar:font_size");
static auto PTEXTCOLOR = CConfigValue<Hyprlang::INT>("group:groupbar:text_color");
const CColor COLOR = CColor(*PTEXTCOLOR);
const CColor COLOR = CColor(*PTEXTCOLOR);
const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT;
// clear the pixmap
cairo_save(CAIRO);
@ -206,7 +227,8 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float
PangoLayout* layout = pango_cairo_create_layout(CAIRO);
pango_layout_set_text(layout, szContent.c_str(), -1);
PangoFontDescription* fontDesc = pango_font_description_from_string((*PTITLEFONTFAMILY).c_str());
PangoFontDescription* fontDesc = pango_font_description_new();
pango_font_description_set_family_static(fontDesc, FONTFAMILY.c_str());
pango_font_description_set_size(fontDesc, *PTITLEFONTSIZE * PANGO_SCALE * monitorScale);
pango_layout_set_font_description(layout, fontDesc);
pango_font_description_free(fontDesc);
@ -335,13 +357,18 @@ void refreshGroupBarGradients() {
}
bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) {
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
if (m_pWindow.lock() == m_pWindow->m_sGroupData.pNextWindow.lock())
return false;
const float BARRELATIVEX = pos.x - assignedBoxGlobal().x;
const int WINDOWINDEX = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
const float BARRELATIVEY = pos.y - assignedBoxGlobal().y;
const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_fBarHeight + BAR_PADDING_OUTER_VERT)) : (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
if (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth)
if (!*PSTACKED && (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth))
return false;
if (*PSTACKED && (BARRELATIVEY - (m_fBarHeight + BAR_PADDING_OUTER_VERT) * WINDOWINDEX < BAR_PADDING_OUTER_VERT))
return false;
PHLWINDOW pWindow = m_pWindow->getGroupWindowByIndex(WINDOWINDEX);
@ -364,11 +391,13 @@ bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) {
}
bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWINDOW pDraggedWindow) {
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
if (!pDraggedWindow->canBeGroupedInto(m_pWindow.lock()))
return false;
const float BARRELATIVEX = pos.x - assignedBoxGlobal().x - m_fBarWidth / 2;
const int WINDOWINDEX = BARRELATIVEX < 0 ? -1 : (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
const float BARRELATIVE = *PSTACKED ? pos.y - assignedBoxGlobal().y - (m_fBarHeight + BAR_PADDING_OUTER_VERT) / 2 : pos.x - assignedBoxGlobal().x - m_fBarWidth / 2;
const float BARSIZE = *PSTACKED ? m_fBarHeight + BAR_PADDING_OUTER_VERT : m_fBarWidth + BAR_HORIZONTAL_PADDING;
const int WINDOWINDEX = BARRELATIVE < 0 ? -1 : BARRELATIVE / BARSIZE;
PHLWINDOW pWindowInsertAfter = m_pWindow->getGroupWindowByIndex(WINDOWINDEX);
PHLWINDOW pWindowInsertEnd = pWindowInsertAfter->m_sGroupData.pNextWindow.lock();
@ -423,11 +452,13 @@ bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWIND
}
bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPointer::SButtonEvent& e) {
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
if (m_pWindow->m_bIsFullscreen && m_pWindow->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL)
return true;
const float BARRELATIVEX = pos.x - assignedBoxGlobal().x;
const int WINDOWINDEX = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
const float BARRELATIVEY = pos.y - assignedBoxGlobal().y;
const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_fBarHeight + BAR_PADDING_OUTER_VERT)) : (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
// close window on middle click
@ -446,7 +477,9 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo
return true;
// click on padding
if (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth) {
const auto TABPAD = !*PSTACKED && (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth);
const auto STACKPAD = *PSTACKED && (BARRELATIVEY - (m_fBarHeight + BAR_PADDING_OUTER_VERT) * WINDOWINDEX < BAR_PADDING_OUTER_VERT);
if (TABPAD || STACKPAD) {
if (!g_pCompositor->isWindowActive(m_pWindow.lock()))
g_pCompositor->focusWindow(m_pWindow.lock());
return true;

View File

@ -54,6 +54,7 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration {
std::deque<PHLWINDOWREF> m_dwGroupMembers;
float m_fBarWidth;
float m_fBarHeight;
CTitleTex* textureFromTitle(const std::string&);
void invalidateTextures();

@ -1 +1 @@
Subproject commit 5c1d51c5a2793480f5b6c4341ad0797052aec2ea
Subproject commit a336b9b1fb415433e849de002df68c45034d0419