mirror of https://github.com/hyprwm/Hyprland
Merge branch 'hyprwm:main' into fix/cursor-min-padding
This commit is contained in:
commit
4e753b25a9
|
@ -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)
|
||||
|
|
10
Makefile
10
Makefile
|
@ -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
|
||||
|
||||
|
|
30
flake.lock
30
flake.lock
|
@ -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": {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.");
|
||||
|
|
21
meson.build
21
meson.build
|
@ -5,30 +5,21 @@ 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
|
||||
'debug=false',
|
||||
'cpp_std=c++23',
|
||||
])
|
||||
|
||||
# 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
|
||||
|
||||
add_project_arguments(
|
||||
[
|
||||
'-Wno-unused-parameter',
|
||||
'-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'],
|
||||
)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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=";
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
|
||||
// calc for oversized windows... fucking bullshit, again.
|
||||
CBox geom;
|
||||
wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, geom.pWlr());
|
||||
geom.applyFromWlr();
|
||||
CBox geom = pWindow->m_pXDGSurface->current.geometry;
|
||||
|
||||
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);
|
||||
// try popups first
|
||||
const auto PPOPUP = pWindow->m_pPopupHead->at(pos);
|
||||
|
||||
if (PFOUND) {
|
||||
wlr_surface* found = PPOPUP ? PPOPUP->m_sWLSurface.wlr() : nullptr;
|
||||
|
||||
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;
|
||||
|
@ -1420,6 +1387,7 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
|
|||
|
||||
// 0 -> history, 1 -> shared length
|
||||
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);
|
||||
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -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;
|
||||
|
@ -108,20 +105,46 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
|||
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) {
|
||||
|
|
|
@ -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() {
|
||||
|
@ -82,25 +71,24 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
|
|||
float maxWidth = 0;
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
// first rect (bg, col)
|
||||
const float FIRSTRECTANIMP =
|
||||
(notif->started.getMillis() > (ANIM_DURATION_MS - ANIM_LAG_MS) ?
|
||||
|
@ -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,25 +158,26 @@ 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);
|
||||
}
|
||||
g_object_unref(layout);
|
||||
|
||||
// cleanup notifs
|
||||
std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
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;
|
||||
|
||||
if (!m_pResource)
|
||||
return {};
|
||||
|
||||
CPopup* current = this;
|
||||
offset -= current->m_pResource->surface->current.geometry.pos();
|
||||
|
||||
offset -= {m_pWLR->base->current.geometry.x, m_pWLR->base->current.geometry.y};
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
@ -47,7 +49,7 @@ class CPopup {
|
|||
// T2 owners
|
||||
CPopup* m_pParent = nullptr;
|
||||
|
||||
wlr_xdg_popup* m_pWLR = nullptr;
|
||||
WP<CXDGPopupResource> m_pResource;
|
||||
|
||||
Vector2D m_vLastSize = {};
|
||||
Vector2D m_vLastPos = {};
|
||||
|
@ -60,16 +62,14 @@ class CPopup {
|
|||
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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,7 +87,7 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
|
|||
const int BORDERSIZE = getRealBorderSize();
|
||||
|
||||
if (m_sAdditionalConfigData.dimAround) {
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
|
||||
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)}};
|
||||
}
|
||||
|
@ -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,7 +147,7 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
|
|||
|
||||
CBox CWindow::getFullWindowBoundingBox() {
|
||||
if (m_sAdditionalConfigData.dimAround) {
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
|
||||
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR)
|
||||
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
|
||||
}
|
||||
|
||||
|
@ -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; });
|
||||
}
|
|
@ -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, "]");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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,6 +45,7 @@ void CWorkspace::init(PHLWORKSPACE self) {
|
|||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
|
||||
m_bPersistent = WORKSPACERULE.isPersistent;
|
||||
|
||||
if (self->m_bWasCreatedEmtpy)
|
||||
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
|
||||
g_pKeybindManager->spawn(*cmd);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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!!");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -146,9 +144,7 @@ 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);
|
||||
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,33 +916,6 @@ 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 (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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
|
@ -1004,7 +923,6 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
|
|||
g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
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")) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Systemd {
|
||||
int SdBooted(void) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <pango/pangocairo.h>
|
||||
#include "HyprError.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../config/ConfigValue.hpp"
|
||||
|
@ -60,6 +61,9 @@ void CHyprError::createQueued() {
|
|||
|
||||
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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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,7 +389,6 @@ 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);
|
||||
foundInPressedKeys = true;
|
||||
suppressEvent = !it->sent;
|
||||
|
@ -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,6 +720,9 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint3
|
|||
if (k.handler == "global" || k.transparent)
|
||||
continue; // can't be shadowed
|
||||
|
||||
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);
|
||||
|
||||
|
@ -698,6 +745,7 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint3
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
k.shadowed = shadow;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1173,6 +1221,7 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
|
|||
|
||||
void CKeybindManager::moveFocusTo(std::string args) {
|
||||
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)) {
|
||||
|
@ -1182,7 +1231,9 @@ void CKeybindManager::moveFocusTo(std::string args) {
|
|||
|
||||
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock();
|
||||
if (!PLASTWINDOW) {
|
||||
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 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) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "../defines.hpp"
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include "../Compositor.hpp"
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
@ -14,9 +15,11 @@ class IKeyboard;
|
|||
|
||||
struct SKeybind {
|
||||
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;
|
||||
|
@ -27,6 +30,7 @@ struct SKeybind {
|
|||
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ class CPointerManager {
|
|||
|
||||
//
|
||||
Vector2D position();
|
||||
Vector2D cursorSizeLogical();
|
||||
|
||||
private:
|
||||
void recheckPointerPosition();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
m_bFocusHeldByButtons = true;
|
||||
m_bRefocusHeldByButtons = refocus;
|
||||
} else {
|
||||
// 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 if (!g_pCompositor->m_pLastWindow.expired()) {
|
||||
foundSurface = m_pLastMouseSurface;
|
||||
pFoundWindow = g_pCompositor->m_pLastWindow.lock();
|
||||
} else {
|
||||
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,23 +546,23 @@ 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()) {
|
||||
if (e.surf != m_sCursorSurfaceInfo.wlSurface.wlr()) {
|
||||
m_sCursorSurfaceInfo.wlSurface.unassign();
|
||||
|
||||
if (e->surface)
|
||||
m_sCursorSurfaceInfo.wlSurface.assign(e->surface);
|
||||
if (e.surf)
|
||||
m_sCursorSurfaceInfo.wlSurface.assign(e.surf);
|
||||
}
|
||||
|
||||
if (e->surface) {
|
||||
m_sCursorSurfaceInfo.vHotspot = {e->hotspot_x, e->hotspot_y};
|
||||
if (e.surf) {
|
||||
m_sCursorSurfaceInfo.vHotspot = e.hotspot;
|
||||
m_sCursorSurfaceInfo.hidden = false;
|
||||
} else {
|
||||
m_sCursorSurfaceInfo.vHotspot = {};
|
||||
|
@ -552,8 +572,7 @@ void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_even
|
|||
m_sCursorSurfaceInfo.name = "";
|
||||
|
||||
m_sCursorSurfaceInfo.inUse = true;
|
||||
g_pHyprRenderer->setCursorSurface(&m_sCursorSurfaceInfo.wlSurface, e->hotspot_x, e->hotspot_y);
|
||||
}
|
||||
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();
|
||||
|
|
|
@ -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;
|
||||
|
@ -188,15 +186,16 @@ class CInputManager {
|
|||
|
||||
// for some bugs in follow mouse 0
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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:
|
||||
if (!removeOnly) {
|
||||
iter->second->state = CFocusGrabSurfaceState::Comitted;
|
||||
grab->add(iter->first);
|
||||
surfacesChanged = true;
|
||||
anyComitted = true;
|
||||
}
|
||||
break;
|
||||
case CFocusGrabSurfaceState::Comitted: anyComitted = true; break;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,8 +87,8 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP<CZwpVirtualKeyboardV1>
|
|||
}
|
||||
|
||||
CVirtualKeyboardV1Resource::~CVirtualKeyboardV1Resource() {
|
||||
wlr_keyboard_finish(&keyboard);
|
||||
events.destroy.emit();
|
||||
wlr_keyboard_finish(&keyboard);
|
||||
}
|
||||
|
||||
bool CVirtualKeyboardV1Resource::good() {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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"
|
||||
|
@ -2098,24 +2098,35 @@ 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 PSPLASHFONT = CConfigValue<std::string>("misc:splash_font_family");
|
||||
static auto FALLBACKFONT = CConfigValue<std::string>("misc:font_family");
|
||||
|
||||
cairo_select_font_face(CAIRO, (*PSPLASHFONT).c_str(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
||||
|
||||
const auto FONTFAMILY = *PSPLASHFONT != STRVAL_EMPTY ? *PSPLASHFONT : *FALLBACKFONT;
|
||||
const auto FONTSIZE = (int)(size.y / 76);
|
||||
cairo_set_font_size(CAIRO, FONTSIZE);
|
||||
|
||||
const auto COLOR = CColor(*PSPLASHCOLOR);
|
||||
|
||||
PangoLayout* layoutText = pango_cairo_create_layout(CAIRO);
|
||||
PangoFontDescription* pangoFD = pango_font_description_new();
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
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
|
||||
} 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,23 +103,30 @@ 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) {
|
||||
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 - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth,
|
||||
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)
|
||||
|
@ -133,37 +147,42 @@ 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) :
|
||||
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))
|
||||
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);
|
||||
}
|
||||
|
||||
if (*PSTACKED)
|
||||
yoff += ONEBARHEIGHT;
|
||||
else
|
||||
xoff += BAR_HORIZONTAL_PADDING + m_fBarWidth;
|
||||
}
|
||||
|
||||
|
@ -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 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;
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue