Merge branch 'main' into main

This commit is contained in:
Jasson 2024-07-21 12:43:59 -04:00 committed by GitHub
commit b1d48be242
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
139 changed files with 5170 additions and 3703 deletions

View file

@ -34,6 +34,7 @@ runs:
libglvnd \
libinput \
libliftoff \
libxcursor \
libxcvt \
libxfont2 \
libxkbcommon \
@ -73,6 +74,11 @@ runs:
run: |
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
- name: Get aquamarine-git
shell: bash
run: |
git clone https://github.com/hyprwm/aquamarine && cd aquamarine && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target aquamarine && cmake --install build
- name: Get Xorg pacman pkgs
shell: bash
if: inputs.INSTALL_XORG_PKGS == 'true'

8
.gitignore vendored
View file

@ -7,6 +7,9 @@ cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
CPackConfig.cmake
CPackSourceConfig.cmake
hyprland.pc
_deps
build/
@ -15,6 +18,9 @@ result*
/.idea/
.envrc
.cache
.direnv
/.cmake/
/.worktree/
*.o
protocols/*.c*
@ -31,5 +37,3 @@ gmon.out
PKGBUILD
src/version.h
.direnv

4
.gitmodules vendored
View file

@ -7,7 +7,3 @@
[submodule "subprojects/tracy"]
path = subprojects/tracy
url = https://github.com/wolfpld/tracy
[submodule "subprojects/wlroots-hyprland"]
path = subprojects/wlroots-hyprland
url = https://github.com/hyprwm/wlroots-hyprland
ignore = dirty

View file

@ -1,17 +1,16 @@
cmake_minimum_required(VERSION 3.27)
include(CheckIncludeFile)
include(ExternalProject)
include(GNUInstallDirs)
# Get version
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/props.json PROPS)
string(JSON VER GET ${PROPS} version)
file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
string(STRIP ${VER_RAW} VER)
project(Hyprland
DESCRIPTION "A Modern C++ Wayland Compositor"
VERSION ${VER}
)
project(
Hyprland
DESCRIPTION "A Modern C++ Wayland Compositor"
VERSION ${VER})
set(HYPRLAND_VERSION ${VER})
set(PREFIX ${CMAKE_INSTALL_PREFIX})
@ -22,49 +21,32 @@ set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
message(STATUS "Gathering git info")
# Get git info
# hash and branch
execute_process(
COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
# Get git info hash and branch
execute_process(COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
# udis
add_subdirectory("subprojects/udis86")
# wlroots
message(STATUS "Setting up wlroots")
if(CMAKE_BUILD_TYPE)
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER)
if(BUILDTYPE_LOWER STREQUAL "release")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "debug")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "relwithdebinfo")
set(BUILDTYPE_LOWER "debugoptimized")
elseif(BUILDTYPE_LOWER STREQUAL "minsizerel")
set(BUILDTYPE_LOWER "minsize")
elseif(BUILDTYPE_LOWER STREQUAL "none")
set(BUILDTYPE_LOWER "plain")
else()
set(BUILDTYPE_LOWER "release")
endif()
else()
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER)
if(BUILDTYPE_LOWER STREQUAL "release")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "debug")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "relwithdebinfo")
set(BUILDTYPE_LOWER "debugoptimized")
elseif(BUILDTYPE_LOWER STREQUAL "minsizerel")
set(BUILDTYPE_LOWER "minsize")
elseif(BUILDTYPE_LOWER STREQUAL "none")
set(BUILDTYPE_LOWER "plain")
else()
set(BUILDTYPE_LOWER "release")
endif()
else()
set(BUILDTYPE_LOWER "release")
endif()
ExternalProject_Add(
wlroots-hyprland
PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland
SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland
CONFIGURE_COMMAND meson setup --reconfigure --clearcache build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dxwayland=$<IF:$<BOOL:${NO_XWAYLAND}>,disabled,enabled> -Dexamples=false -Drenderers=gles2 -Dbackends=drm,libinput $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none>
BUILD_COMMAND ninja -C build
BUILD_ALWAYS true
BUILD_IN_SOURCE true
BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland/build/libwlroots.a
INSTALL_COMMAND echo "wlroots-hyprland: install not needed"
)
find_package(PkgConfig REQUIRED)
pkg_get_variable(WaylandScanner wayland-scanner wayland_scanner)
@ -74,25 +56,24 @@ 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")
add_compile_definitions(HYPRLAND_DEBUG)
message(STATUS "Configuring Hyprland in Debug with CMake")
add_compile_definitions(HYPRLAND_DEBUG)
else()
add_compile_options(-O3)
message(STATUS "Configuring Hyprland in Release with CMake")
add_compile_options(-O3)
message(STATUS "Configuring Hyprland in Release with CMake")
endif()
include_directories(
.
"src/"
"subprojects/wlroots-hyprland/include/"
"subprojects/wlroots-hyprland/build/include/"
"subprojects/udis86/"
"protocols/")
include_directories(. "src/" "subprojects/udis86/" "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
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
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)
@ -102,19 +83,39 @@ message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
if(LEGACY_RENDERER)
set(GLES_VERSION "GLES2")
set(GLES_VERSION "GLES2")
else()
set(GLES_VERSION "GLES3")
set(GLES_VERSION "GLES3")
endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(deps REQUIRED IMPORTED_TARGET
xkbcommon uuid
wayland-server wayland-client wayland-cursor wayland-protocols
cairo pango pangocairo pixman-1
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm
hyprlang>=0.3.2 hyprcursor>=0.1.7 hyprutils>=0.2.0
)
pkg_check_modules(
deps
REQUIRED
IMPORTED_TARGET
aquamarine
xkbcommon
uuid
wayland-server
wayland-client
wayland-cursor
wayland-protocols
cairo
pango
pangocairo
pixman-1
xcursor
libdrm
libinput
hwdata
libseat
libdisplay-info
libliftoff
libudev
gbm
hyprlang>=0.3.2
hyprcursor>=0.1.7
hyprutils>=0.2.0)
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
@ -122,85 +123,97 @@ file(GLOB_RECURSE SRCFILES "src/*.cpp")
set(TRACY_CPP_FILES "")
if(USE_TRACY)
set(TRACY_CPP_FILES "subprojects/tracy/public/TracyClient.cpp")
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
set(TRACY_CPP_FILES "subprojects/tracy/public/TracyClient.cpp")
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
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")
message(STATUS "Setting debug flags")
if (WITH_ASAN)
message(STATUS "Enabling ASan")
if(WITH_ASAN)
message(STATUS "Enabling ASan")
target_link_libraries(Hyprland asan)
target_compile_options(Hyprland PUBLIC -fsanitize=address)
target_link_libraries(Hyprland asan)
target_compile_options(Hyprland PUBLIC -fsanitize=address)
endif()
if(USE_TRACY)
message(STATUS "Tracy is turned on")
option(TRACY_ENABLE "" ON)
option(TRACY_ON_DEMAND "" ON)
add_subdirectory(subprojects/tracy)
target_link_libraries(Hyprland Tracy::TracyClient)
if(USE_TRACY_GPU)
message(STATUS "Tracy GPU Profiling is turned on")
add_compile_definitions(USE_TRACY_GPU)
endif()
endif()
if(USE_TRACY)
message(STATUS "Tracy is turned on")
option( TRACY_ENABLE "" ON)
option( TRACY_ON_DEMAND "" ON)
add_subdirectory (subprojects/tracy)
target_link_libraries(Hyprland Tracy::TracyClient)
if(USE_TRACY_GPU)
message(STATUS "Tracy GPU Profiling is turned on")
add_compile_definitions(USE_TRACY_GPU)
endif()
endif()
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()
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)
if(EXECINFOH)
message(STATUS "Configuration supports execinfo")
add_compile_definitions(HAS_EXECINFO)
message(STATUS "Configuration supports execinfo")
add_compile_definitions(HAS_EXECINFO)
endif()
include(CheckLibraryExists)
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
if(HAVE_LIBEXECINFO)
target_link_libraries(Hyprland execinfo)
target_link_libraries(Hyprland execinfo)
endif()
check_include_file("sys/timerfd.h" HAS_TIMERFD)
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
if(NOT HAS_TIMERFD AND epoll_FOUND)
target_link_libraries(Hyprland PkgConfig::epoll)
target_link_libraries(Hyprland PkgConfig::epoll)
endif()
if(LEGACY_RENDERER)
message(STATUS "Using the legacy GLES2 renderer!")
add_compile_definitions(LEGACY_RENDERER)
message(STATUS "Using the legacy GLES2 renderer!")
add_compile_definitions(LEGACY_RENDERER)
endif()
if(NO_XWAYLAND)
message(STATUS "Using the NO_XWAYLAND flag, disabling XWayland!")
add_compile_definitions(NO_XWAYLAND)
message(STATUS "Using the NO_XWAYLAND flag, disabling XWayland!")
add_compile_definitions(NO_XWAYLAND)
else()
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
pkg_check_modules(xdeps REQUIRED IMPORTED_TARGET xcb xwayland xcb-util xcb-render xcb-xfixes xcb-icccm xcb-composite xcb-res xcb-ewmh xcb-errors)
target_link_libraries(Hyprland PkgConfig::xdeps)
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
pkg_check_modules(
xdeps
REQUIRED
IMPORTED_TARGET
xcb
xwayland
xcb-util
xcb-render
xcb-xfixes
xcb-icccm
xcb-composite
xcb-res
xcb-ewmh
xcb-errors)
target_link_libraries(Hyprland PkgConfig::xdeps)
endif()
if(NO_SYSTEMD)
message(STATUS "SYSTEMD support is disabled...")
message(STATUS "SYSTEMD support is disabled...")
else()
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined)...")
add_compile_definitions(USES_SYSTEMD)
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined)...")
add_compile_definitions(USES_SYSTEMD)
endif()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
@ -209,7 +222,8 @@ include(CPack)
message(STATUS "Setting precompiled headers")
target_precompile_headers(Hyprland PRIVATE $<$<COMPILE_LANGUAGE:CXX>:src/pch/pch.hpp>)
target_precompile_headers(Hyprland PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:src/pch/pch.hpp>)
message(STATUS "Setting link libraries")
@ -219,103 +233,115 @@ target_link_libraries(Hyprland rt PkgConfig::deps)
add_custom_target(generate-protocol-headers)
function(protocol protoPath protoName external)
if (external)
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
else()
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif()
if(external)
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
else()
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif()
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
COMMAND ${WaylandScanner} server-header ${path} protocols/${protoName}-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c
COMMAND ${WaylandScanner} private-code ${path} protocols/${protoName}-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
target_sources(Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c)
target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
COMMAND ${WaylandScanner} server-header ${path}
protocols/${protoName}-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c
COMMAND ${WaylandScanner} private-code ${path}
protocols/${protoName}-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(
Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c)
target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h)
endfunction()
function(protocolNew protoPath protoName external)
if (external)
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
else()
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif()
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
COMMAND hyprwayland-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp protocols/${protoName}.hpp)
target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp)
function(protocolnew protoPath protoName external)
if(external)
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
else()
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif()
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
COMMAND hyprwayland-scanner ${path}/${protoName}.xml
${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp
protocols/${protoName}.hpp)
target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp)
endfunction()
function(protocolWayland)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
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 protocols/wayland.hpp)
target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
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 protocols/wayland.hpp)
target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
endfunction()
target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland/build/libwlroots.a
OpenGL::EGL
OpenGL::GL
Threads::Threads
libudis86
uuid
)
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads
libudis86 uuid)
protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true)
protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false)
protocol("protocols/wlr-screencopy-unstable-v1.xml"
"wlr-screencopy-unstable-v1" true)
protocol(
"subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml"
"hyprland-global-shortcuts-v1" true)
protocol(
"subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml"
"hyprland-toplevel-export-v1" true)
protocol("unstable/text-input/text-input-unstable-v1.xml"
"text-input-unstable-v1" false)
protocolNew("protocols" "wlr-gamma-control-unstable-v1" true)
protocolNew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true)
protocolNew("protocols" "wlr-output-power-management-unstable-v1" true)
protocolNew("protocols" "virtual-keyboard-unstable-v1" true)
protocolNew("protocols" "wlr-virtual-pointer-unstable-v1" true)
protocolNew("protocols" "input-method-unstable-v2" true)
protocolNew("protocols" "wlr-output-management-unstable-v1" true)
protocolNew("protocols" "kde-server-decoration" true)
protocolNew("protocols" "wlr-data-control-unstable-v1" true)
protocolNew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1" true)
protocolNew("protocols" "wlr-layer-shell-unstable-v1" true)
protocolNew("protocols" "wayland-drm" true)
protocolNew("staging/tearing-control" "tearing-control-v1" false)
protocolNew("staging/fractional-scale" "fractional-scale-v1" false)
protocolNew("unstable/xdg-output" "xdg-output-unstable-v1" false)
protocolNew("staging/cursor-shape" "cursor-shape-v1" false)
protocolNew("unstable/idle-inhibit" "idle-inhibit-unstable-v1" false)
protocolNew("unstable/relative-pointer" "relative-pointer-unstable-v1" false)
protocolNew("unstable/xdg-decoration" "xdg-decoration-unstable-v1" false)
protocolNew("staging/alpha-modifier" "alpha-modifier-v1" false)
protocolNew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1" false)
protocolNew("unstable/pointer-gestures" "pointer-gestures-unstable-v1" false)
protocolNew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false)
protocolNew("unstable/text-input" "text-input-unstable-v3" false)
protocolNew("unstable/pointer-constraints" "pointer-constraints-unstable-v1" false)
protocolNew("staging/xdg-activation" "xdg-activation-v1" false)
protocolNew("staging/ext-idle-notify" "ext-idle-notify-v1" false)
protocolNew("staging/ext-session-lock" "ext-session-lock-v1" false)
protocolNew("stable/tablet" "tablet-v2" false)
protocolNew("stable/presentation-time" "presentation-time" false)
protocolNew("stable/xdg-shell" "xdg-shell" false)
protocolNew("unstable/primary-selection" "primary-selection-unstable-v1" false)
protocolNew("staging/xwayland-shell" "xwayland-shell-v1" false)
protocolNew("stable/viewporter" "viewporter" false)
protocolNew("stable/linux-dmabuf" "linux-dmabuf-v1" false)
protocolnew("protocols" "wlr-gamma-control-unstable-v1" true)
protocolnew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true)
protocolnew("protocols" "wlr-output-power-management-unstable-v1" true)
protocolnew("protocols" "virtual-keyboard-unstable-v1" true)
protocolnew("protocols" "wlr-virtual-pointer-unstable-v1" true)
protocolnew("protocols" "input-method-unstable-v2" true)
protocolnew("protocols" "wlr-output-management-unstable-v1" true)
protocolnew("protocols" "kde-server-decoration" true)
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolnew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1"
true)
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
protocolnew("protocols" "wayland-drm" true)
protocolnew("staging/tearing-control" "tearing-control-v1" false)
protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
protocolnew("unstable/xdg-output" "xdg-output-unstable-v1" false)
protocolnew("staging/cursor-shape" "cursor-shape-v1" false)
protocolnew("unstable/idle-inhibit" "idle-inhibit-unstable-v1" false)
protocolnew("unstable/relative-pointer" "relative-pointer-unstable-v1" false)
protocolnew("unstable/xdg-decoration" "xdg-decoration-unstable-v1" false)
protocolnew("staging/alpha-modifier" "alpha-modifier-v1" false)
protocolnew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1"
false)
protocolnew("unstable/pointer-gestures" "pointer-gestures-unstable-v1" false)
protocolnew("unstable/keyboard-shortcuts-inhibit"
"keyboard-shortcuts-inhibit-unstable-v1" false)
protocolnew("unstable/text-input" "text-input-unstable-v3" false)
protocolnew("unstable/pointer-constraints" "pointer-constraints-unstable-v1"
false)
protocolnew("staging/xdg-activation" "xdg-activation-v1" false)
protocolnew("staging/ext-idle-notify" "ext-idle-notify-v1" false)
protocolnew("staging/ext-session-lock" "ext-session-lock-v1" false)
protocolnew("stable/tablet" "tablet-v2" false)
protocolnew("stable/presentation-time" "presentation-time" false)
protocolnew("stable/xdg-shell" "xdg-shell" false)
protocolnew("unstable/primary-selection" "primary-selection-unstable-v1" false)
protocolnew("staging/xwayland-shell" "xwayland-shell-v1" false)
protocolnew("stable/viewporter" "viewporter" false)
protocolnew("stable/linux-dmabuf" "linux-dmabuf-v1" false)
protocolnew("staging/drm-lease" "drm-lease-v1" false)
protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
protocolWayland()
protocolwayland()
# tools
add_subdirectory(hyprctl)
@ -324,12 +350,12 @@ add_subdirectory(hyprpm)
# binary and symlink
install(TARGETS Hyprland)
install(CODE "execute_process( \
install(
CODE "execute_process( \
COMMAND ${CMAKE_COMMAND} -E create_symlink \
${CMAKE_INSTALL_BINDIR}/Hyprland \
${CMAKE_INSTALL_BINDIR}/hyprland
)"
)
${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \
${CMAKE_INSTALL_FULL_BINDIR}/hyprland
)")
# session file
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
@ -337,8 +363,7 @@ install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
# wallpapers
file(GLOB_RECURSE WALLPAPERS "assets/wall*")
install(FILES ${WALLPAPERS}
DESTINATION ${CMAKE_INSTALL_DATADIR}/hyprland)
install(FILES ${WALLPAPERS} DESTINATION ${CMAKE_INSTALL_DATADIR}/hyprland)
# default config
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.conf
@ -350,34 +375,24 @@ install(FILES ${CMAKE_SOURCE_DIR}/assets/hyprland-portals.conf
# man pages
file(GLOB_RECURSE MANPAGES "docs/*.1")
install(FILES ${MANPAGES}
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
install(FILES ${MANPAGES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
# pkgconfig entry
install(FILES ${CMAKE_BINARY_DIR}/hyprland.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
# wlroots headers
set(HEADERS_WLR "${CMAKE_CURRENT_SOURCE_DIR}/subprojects/wlroots-hyprland/include/wlr")
install(DIRECTORY ${HEADERS_WLR}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING PATTERN "*.h")
# config.h and version.h
set(HEADERS_WLR_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/subprojects/wlroots-hyprland/build/include/wlr")
install(DIRECTORY ${HEADERS_WLR_ROOT}/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland/wlr
FILES_MATCHING PATTERN "*.h")
# protocol headers
set(HEADERS_PROTO "${CMAKE_CURRENT_SOURCE_DIR}/protocols")
install(DIRECTORY ${HEADERS_PROTO}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING PATTERN "*.h*")
install(
DIRECTORY ${HEADERS_PROTO}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING
PATTERN "*.h*")
# hyprland headers
set(HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src")
install(DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING PATTERN "*.h*")
install(
DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING
PATTERN "*.h*")

View file

@ -27,7 +27,6 @@ nopch:
clear:
rm -rf build
rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp
rm -rf ./subprojects/wlroots-hyprland/build
all:
$(MAKE) clear
@ -50,14 +49,11 @@ installheaders:
rm -fr ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols
mkdir -p ${PREFIX}/include/hyprland/wlr
mkdir -p ${PREFIX}/share/pkgconfig
cmake --build ./build --config Release --target generate-protocol-headers
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
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
@ -88,7 +84,7 @@ asan:
@pidof Hyprland > /dev/null && exit 1 || echo ""
rm -rf ./wayland
git reset --hard
#git reset --hard
@echo -en "If you want to apply a patch, input its path (leave empty for none):\n"
@read patchvar

View file

@ -13,10 +13,10 @@
<br>
Hyprland is a dynamic tiling Wayland compositor based on wlroots that doesn't sacrifice on its looks.
Hyprland is a 100% independent, dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
It provides the latest Wayland features, is highly customizable, has all the eyecandy, the most powerful plugins,
easy IPC, much more QoL stuff than other wlr-based compositors and more...
easy IPC, much more QoL stuff than other compositors and more...
<br>
<br>
@ -37,7 +37,7 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
- All of the eyecandy: gradient borders, blur, animations, shadows and much more
- A lot of customization
- Much more QoL stuff than other wlr-based compositors
- 100% independent, no wlroots, no libweston, no kwin, no mutter.
- Custom bezier curves for the best animations
- Powerful plugin support
- Built-in plugin manager
@ -48,7 +48,6 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
- Config reloaded instantly upon saving
- Fully dynamic workspaces
- Two built-in layouts and more available as plugins
- Uses forked wlroots with QoL patches
- Global keybinds passed to your apps of choice
- Tiling/pseudotiling/floating/fullscreen windows
- Special workspaces (scratchpads)
@ -86,7 +85,7 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
<br>
**[wlroots]** - *For their amazing library*
**[wlroots]** - *For powering Hyprland in the past*
**[tinywl]** - *For showing how 2 do stuff*

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.41.2

View file

@ -32,6 +32,12 @@ Show command usage.
.TP
\f[B]-c\f[R], \f[B]--config\f[R]
Specify config file to use.
.TP
\f[B]--socket\f[R]
Sets the Wayland socket name (for Wayland socket handover)
.TP
\f[B]--wayland-fd\f[R]
Sets the Wayland socket file descriptor (for Wayland socket handover)
.SH BUGS
.TP
Submit bug reports and request features online at:

View file

@ -41,6 +41,12 @@ OPTIONS
**-c**, **--config**
Specify config file to use.
**--socket**
Sets the Wayland socket name (for Wayland socket handover)
**--wayland-fd**
Sets the Wayland socket file descriptor (for Wayland socket handover)
BUGS
====

View file

@ -1,5 +1,34 @@
{
"nodes": {
"aquamarine": {
"inputs": {
"hyprutils": [
"hyprutils"
],
"hyprwayland-scanner": [
"hyprwayland-scanner"
],
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1721571743,
"narHash": "sha256-hat7wggtDISBJD8kTo5MTrT+IsY/Ha2MwgjmqqijoCA=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "601f6cf95cbe4fef02dc7faf34bba58566c914e9",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "aquamarine",
"type": "github"
}
},
"hyprcursor": {
"inputs": {
"hyprlang": [
@ -13,11 +42,11 @@
]
},
"locked": {
"lastModified": 1720108799,
"narHash": "sha256-AxRkTJlbB8r7aG6gvc7IaLhc2T9TO4/8uqanKRxukBQ=",
"lastModified": 1721330371,
"narHash": "sha256-aYlHTWylczLt6ERJyg6E66Y/XSCbVL7leVcRuJmVbpI=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "a5c0d57325c5f0814c39110a70ca19c070ae9486",
"rev": "4493a972b48f9c3014befbbf381ed5fff91a65dc",
"type": "github"
},
"original": {
@ -64,11 +93,11 @@
]
},
"locked": {
"lastModified": 1720381373,
"narHash": "sha256-lyC/EZdHULsaAKVryK11lgHY9u6pXr7qR4irnxNWC7k=",
"lastModified": 1721324361,
"narHash": "sha256-BiJKO0IIdnSwHQBSrEJlKlFr753urkLE48wtt0UhNG4=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "5df0174fd09de4ac5475233d65ffc703e89b82eb",
"rev": "adbefbf49664a6c2c8bf36b6487fd31e3eb68086",
"type": "github"
},
"original": {
@ -87,11 +116,11 @@
]
},
"locked": {
"lastModified": 1721071737,
"narHash": "sha256-qmC9jGfbE4+EIBbbSAkrfR/p49wShjpv4/KztgE/P54=",
"lastModified": 1721324102,
"narHash": "sha256-WAZ0X6yJW1hFG6otkHBfyJDKRpNP5stsRqdEuHrFRpk=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "eb1ceff2b87f6820789249f63faa8e9dcb54d05f",
"rev": "962582a090bc233c4de9d9897f46794280288989",
"type": "github"
},
"original": {
@ -110,11 +139,11 @@
]
},
"locked": {
"lastModified": 1720215857,
"narHash": "sha256-JPdL+Qul+jEueAn8CARfcWP83eJgwkhMejQYfDvrgvU=",
"lastModified": 1721324119,
"narHash": "sha256-SOOqIT27/X792+vsLSeFdrNTF+OSRp5qXv6Te+fb2Qg=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "d5fa094ca27e0039be5e94c0a80ae433145af8bb",
"rev": "a048a6cb015340bd82f97c1f40a4b595ca85cc30",
"type": "github"
},
"original": {
@ -125,11 +154,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1720957393,
"narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=",
"lastModified": 1721379653,
"narHash": "sha256-8MUgifkJ7lkZs3u99UDZMB4kbOxvMEXQZ31FO3SopZ0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "693bc46d169f5af9c992095736e82c3488bf7dbb",
"rev": "1d9c2c9b3e71b9ee663d11c5d298727dace8d374",
"type": "github"
},
"original": {
@ -141,6 +170,7 @@
},
"root": {
"inputs": {
"aquamarine": "aquamarine",
"hyprcursor": "hyprcursor",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",

View file

@ -7,6 +7,14 @@
# <https://github.com/nix-systems/nix-systems>
systems.url = "github:nix-systems/default-linux";
aquamarine = {
url = "github:hyprwm/aquamarine";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprutils.follows = "hyprutils";
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
};
hyprcursor = {
url = "github:hyprwm/hyprcursor";
inputs.nixpkgs.follows = "nixpkgs";

View file

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

View file

@ -172,6 +172,9 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not check out revision " << rev << ". shell returned:\n" << ret << "\n";
return false;
}
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "git submodule update --init returned: " << ret << "\n";
}
progress.m_iSteps = 1;
@ -226,6 +229,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "git submodule update --init returned: " << ret << "\n";
break;
}
}
@ -356,7 +365,7 @@ eHeadersErrors CPluginManager::headersValid() {
else
headers = "";
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots-hyprland"))
if (PATH.ends_with("protocols"))
continue;
verHeader = trim(PATH.substr(2)) + "/hyprland/src/version.h";
@ -493,11 +502,6 @@ bool CPluginManager::updateHeaders(bool force) {
return false;
}
// le hack. Wlroots has to generate its build/include
ret = execAndGet("cd " + WORKINGDIR + "/subprojects/wlroots-hyprland && meson setup -Drenderers=gles2 -Dexamples=false build");
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "meson returned: " + ret);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland");
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources";

View file

@ -1,5 +1,5 @@
project('Hyprland', 'cpp', 'c',
version : run_command('jq', '-r', '.version', join_paths(meson.source_root(), 'props.json'), check: true).stdout().strip(),
version : run_command('cat', join_paths(meson.source_root(), 'VERSION'), check: true).stdout().strip(),
default_options : [
'warning_level=2',
'default_library=static',
@ -24,8 +24,6 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
wlroots = subproject('wlroots-hyprland', default_options: ['examples=false', 'renderers=gles2'])
have_xwlr = wlroots.get_variable('features').get('xwayland')
xcb_dep = dependency('xcb', required: get_option('xwayland'))
xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland'))
xcb_errors_dep = dependency('xcb-errors', required: get_option('xwayland'))
@ -38,12 +36,7 @@ cmake = import('cmake')
udis = cmake.subproject('udis86')
udis86 = udis.dependency('libudis86')
if get_option('xwayland').enabled() and not have_xwlr
error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support')
endif
have_xwayland = xcb_dep.found() and have_xwlr
if not have_xwayland
if not xcb_dep.found()
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif
@ -86,5 +79,5 @@ import('pkgconfig').generate(
url: 'https://github.com/hyprwm/Hyprland',
description: 'Hyprland header files',
install_dir: pkg_install_dir,
subdirs: ['', 'hyprland/protocols', 'hyprland', 'hyprland/wlr'],
subdirs: ['', 'hyprland/protocols', 'hyprland'],
)

View file

@ -6,6 +6,7 @@
makeWrapper,
cmake,
ninja,
aquamarine,
binutils,
cairo,
expat,
@ -104,6 +105,7 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
buildInputs = lib.concatLists [
[
aquamarine
cairo
expat
fribidi
@ -136,6 +138,7 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
(lib.optionals stdenv.hostPlatform.isMusl [libexecinfo])
(lib.optionals enableXWayland [
xorg.libxcb
xorg.libXcursor
xorg.libXdmcp
xorg.xcbutil
xorg.xcbutilerrors

View file

@ -3,13 +3,12 @@
lib,
inputs,
}: let
props = builtins.fromJSON (builtins.readFile ../props.json);
mkDate = longDate: (lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
version = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
in {
# Contains what a user is most likely to care about:
# Hyprland itself, XDPH and the Share Picker.
@ -21,6 +20,7 @@ in {
# Packages for variations of Hyprland, dependencies included.
hyprland-packages = lib.composeManyExtensions [
# Dependencies
inputs.aquamarine.overlays.default
inputs.hyprcursor.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default
@ -32,7 +32,7 @@ in {
in {
hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv;
version = "${props.version}+date=${date}_${self.shortRev or "dirty"}";
version = "${version}+date=${date}_${self.shortRev or "dirty"}";
commit = self.rev or "";
inherit date;
};

View file

@ -1,17 +0,0 @@
#!/usr/bin/env -S nix shell nixpkgs#gawk nixpkgs#git nixpkgs#gnused nixpkgs#ripgrep -c bash
# get wlroots revision from submodule
SUB_REV=$(git submodule status | rg wlroots | awk '{ print substr($1,2) }')
# and from lockfile
CRT_REV=$(rg rev flake.nix | awk '{ print substr($3, 2, 40) }')
if [ "$SUB_REV" != "$CRT_REV" ]; then
echo "Updating wlroots..."
# update wlroots to submodule revision
sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix
nix flake lock
echo "wlroots: $CRT_REV -> $SUB_REV"
else
echo "wlroots is up to date!"
fi

View file

@ -1,3 +0,0 @@
{
"version": "0.41.2"
}

View file

@ -66,6 +66,8 @@ new_protocols = [
[wl_protocol_dir, 'staging/xwayland-shell/xwayland-shell-v1.xml'],
[wl_protocol_dir, 'stable/viewporter/viewporter.xml'],
[wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'],
[wl_protocol_dir, 'staging/drm-lease/drm-lease-v1.xml'],
[wl_protocol_dir, 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml'],
]
wl_protos_src = []

View file

@ -6,7 +6,10 @@
#include "managers/PointerManager.hpp"
#include "managers/SeatManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include <aquamarine/output/Output.hpp>
#include <random>
#include <cstring>
#include <filesystem>
#include <unordered_set>
#include "debug/HyprCtl.hpp"
#include "debug/CrashReporter.hpp"
@ -22,15 +25,20 @@
#include "protocols/core/Compositor.hpp"
#include "protocols/core/Subcompositor.hpp"
#include "desktop/LayerSurface.hpp"
#include "render/Renderer.hpp"
#include "xwayland/XWayland.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include <aquamarine/input/Input.hpp>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
using namespace Hyprutils::String;
using namespace Aquamarine;
int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal {}", signo);
@ -71,6 +79,23 @@ void handleUserSignal(int sig) {
}
}
static LogLevel aqLevelToHl(Aquamarine::eBackendLogLevel level) {
switch (level) {
case Aquamarine::eBackendLogLevel::AQ_LOG_TRACE: return TRACE;
case Aquamarine::eBackendLogLevel::AQ_LOG_DEBUG: return LOG;
case Aquamarine::eBackendLogLevel::AQ_LOG_ERROR: return ERR;
case Aquamarine::eBackendLogLevel::AQ_LOG_WARNING: return WARN;
case Aquamarine::eBackendLogLevel::AQ_LOG_CRITICAL: return CRIT;
default: break;
}
return NONE;
}
void aqLog(Aquamarine::eBackendLogLevel level, std::string msg) {
Debug::log(aqLevelToHl(level), "[AQ] {}", msg);
}
void CCompositor::bumpNofile() {
if (!getrlimit(RLIMIT_NOFILE, &m_sOriginalNofile))
Debug::log(LOG, "Old rlimit: soft -> {}, hard -> {}", m_sOriginalNofile.rlim_cur, m_sOriginalNofile.rlim_max);
@ -179,6 +204,9 @@ void CCompositor::setRandomSplash() {
m_szCurrentSplash = SPLASHES[distribution(engine)];
}
static std::vector<SP<Aquamarine::IOutput>> pendingOutputs;
//
void CCompositor::initServer() {
m_sWLDisplay = wl_display_create();
@ -199,102 +227,179 @@ void CCompositor::initServer() {
if (envEnabled("HYPRLAND_TRACE"))
Debug::trace = true;
wlr_log_init(WLR_INFO, NULL);
Aquamarine::SBackendOptions options;
options.logFunction = aqLog;
if (envEnabled("HYPRLAND_LOG_WLR"))
wlr_log_init(WLR_DEBUG, Debug::wlrLog);
else
wlr_log_init(WLR_ERROR, Debug::wlrLog);
std::vector<Aquamarine::SBackendImplementationOptions> implementations;
Aquamarine::SBackendImplementationOptions option;
option.backendType = Aquamarine::eBackendType::AQ_BACKEND_HEADLESS;
option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_MANDATORY;
implementations.emplace_back(option);
option.backendType = Aquamarine::eBackendType::AQ_BACKEND_DRM;
option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_IF_AVAILABLE;
implementations.emplace_back(option);
option.backendType = Aquamarine::eBackendType::AQ_BACKEND_WAYLAND;
option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_FALLBACK;
implementations.emplace_back(option);
m_sWLRBackend = wlr_backend_autocreate(m_sWLEventLoop, &m_sWLRSession);
m_pAqBackend = CBackend::create(implementations, options);
if (!m_sWLRBackend) {
Debug::log(CRIT, "m_sWLRBackend was NULL! This usually means wlroots could not find a GPU or enountered some issues.");
throwError("wlr_backend_autocreate() failed!");
if (!m_pAqBackend) {
Debug::log(CRIT,
"m_pAqBackend was null! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a Wayland "
"session, NOT an X11 one.");
throwError("CBackend::create() failed!");
}
bool isHeadlessOnly = true;
wlr_multi_for_each_backend(
m_sWLRBackend,
[](wlr_backend* backend, void* isHeadlessOnly) {
if (!wlr_backend_is_headless(backend) && !wlr_backend_is_libinput(backend))
*(bool*)isHeadlessOnly = false;
},
&isHeadlessOnly);
// TODO: headless only
if (isHeadlessOnly) {
m_sWLRRenderer = wlr_renderer_autocreate(m_sWLRBackend); // TODO: remove this, it's barely needed now.
} else {
m_iDRMFD = wlr_backend_get_drm_fd(m_sWLRBackend);
if (m_iDRMFD < 0) {
Debug::log(CRIT, "Couldn't query the DRM FD!");
throwError("wlr_backend_get_drm_fd() failed!");
initAllSignals();
if (!m_pAqBackend->start()) {
Debug::log(CRIT,
"m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a "
"Wayland session, NOT an X11 one.");
throwError("CBackend::create() failed!");
}
m_bInitialized = true;
m_iDRMFD = m_pAqBackend->drmFD();
Debug::log(LOG, "Running on DRMFD: {}", m_iDRMFD);
// get socket, avoid using 0
for (int candidate = 1; candidate <= 32; candidate++) {
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break;
} else {
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
}
m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD);
}
if (!m_sWLRRenderer) {
Debug::log(CRIT, "m_sWLRRenderer was NULL! This usually means wlroots could not find a GPU or enountered some issues.");
throwError("wlr_gles2_renderer_create_with_drm_fd() failed!");
if (m_szWLDisplaySocket.empty()) {
Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto");
const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay);
if (SOCKETSTR)
m_szWLDisplaySocket = SOCKETSTR;
}
m_sWLRAllocator = wlr_allocator_autocreate(m_sWLRBackend, m_sWLRRenderer);
if (!m_sWLRAllocator) {
Debug::log(CRIT, "m_sWLRAllocator was NULL!");
throwError("wlr_allocator_autocreate() failed!");
if (m_szWLDisplaySocket.empty()) {
Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
}
m_sWLREGL = wlr_gles2_renderer_get_egl(m_sWLRRenderer);
if (!m_sWLREGL) {
Debug::log(CRIT, "m_sWLREGL was NULL!");
throwError("wlr_gles2_renderer_get_egl() failed!");
}
Debug::log(LOG, "Setting WAYLAND_DISPLAY to {}", m_szWLDisplaySocket);
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
setenv("XDG_SESSION_TYPE", "wayland", 1);
initManagers(STAGE_BASICINIT);
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");
Debug::log(INFO, "VR will not be available");
}
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop);
if (!m_sWLRHeadlessBackend) {
Debug::log(CRIT, "Couldn't create the headless backend");
throwError("wlr_headless_backend_create() failed!");
}
wlr_multi_backend_add(m_sWLRBackend, m_sWLRHeadlessBackend);
initManagers(STAGE_LATE);
for (auto& o : pendingOutputs) {
onNewMonitor(o);
}
pendingOutputs.clear();
}
void CCompositor::initAllSignals() {
addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend");
addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
m_pAqBackend->events.newOutput.registerStaticListener(
[this](void* p, std::any data) {
auto output = std::any_cast<SP<Aquamarine::IOutput>>(data);
Debug::log(LOG, "New aquamarine output with name {}", output->name);
if (m_bInitialized)
onNewMonitor(output);
else
pendingOutputs.emplace_back(output);
},
nullptr);
if (m_sWRLDRMLeaseMgr)
addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM");
m_pAqBackend->events.newPointer.registerStaticListener(
[](void* data, std::any d) {
auto dev = std::any_cast<SP<Aquamarine::IPointer>>(d);
Debug::log(LOG, "New aquamarine pointer with name {}", dev->getName());
g_pInputManager->newMouse(dev);
g_pInputManager->updateCapabilities();
},
nullptr);
if (m_sWLRSession)
addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session");
m_pAqBackend->events.newKeyboard.registerStaticListener(
[](void* data, std::any d) {
auto dev = std::any_cast<SP<Aquamarine::IKeyboard>>(d);
Debug::log(LOG, "New aquamarine keyboard with name {}", dev->getName());
g_pInputManager->newKeyboard(dev);
g_pInputManager->updateCapabilities();
},
nullptr);
m_pAqBackend->events.newTouch.registerStaticListener(
[](void* data, std::any d) {
auto dev = std::any_cast<SP<Aquamarine::ITouch>>(d);
Debug::log(LOG, "New aquamarine touch with name {}", dev->getName());
g_pInputManager->newTouchDevice(dev);
g_pInputManager->updateCapabilities();
},
nullptr);
m_pAqBackend->events.newSwitch.registerStaticListener(
[](void* data, std::any d) {
auto dev = std::any_cast<SP<Aquamarine::ISwitch>>(d);
Debug::log(LOG, "New aquamarine switch with name {}", dev->getName());
g_pInputManager->newSwitch(dev);
},
nullptr);
m_pAqBackend->events.newTablet.registerStaticListener(
[](void* data, std::any d) {
auto dev = std::any_cast<SP<Aquamarine::ITablet>>(d);
Debug::log(LOG, "New aquamarine tablet with name {}", dev->getName());
g_pInputManager->newTablet(dev);
},
nullptr);
m_pAqBackend->events.newTabletPad.registerStaticListener(
[](void* data, std::any d) {
auto dev = std::any_cast<SP<Aquamarine::ITabletPad>>(d);
Debug::log(LOG, "New aquamarine tablet pad with name {}", dev->getName());
g_pInputManager->newTabletPad(dev);
},
nullptr);
if (m_pAqBackend->hasSession()) {
m_pAqBackend->session->events.changeActive.registerStaticListener(
[this](void*, std::any) {
if (m_pAqBackend->session->active) {
Debug::log(LOG, "Session got activated!");
m_bSessionActive = true;
for (auto& m : m_vMonitors) {
scheduleFrameForMonitor(m.get());
g_pHyprRenderer->applyMonitorRule(m.get(), &m->activeMonitorRule, true);
}
g_pConfigManager->m_bWantsMonitorReload = true;
} else {
Debug::log(LOG, "Session got deactivated!");
m_bSessionActive = false;
for (auto& m : m_vMonitors) {
m->noFrameSchedule = true;
m->framesToSkip = 1;
}
}
},
nullptr);
}
}
void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_newOutput);
removeWLSignal(&Events::listen_newInput);
removeWLSignal(&Events::listen_RendererDestroy);
if (m_sWRLDRMLeaseMgr)
removeWLSignal(&Events::listen_leaseRequest);
if (m_sWLRSession)
removeWLSignal(&Events::listen_sessionActive);
;
}
void CCompositor::cleanEnvironment() {
@ -308,7 +413,7 @@ void CCompositor::cleanEnvironment() {
unsetenv("XDG_BACKEND");
unsetenv("XDG_CURRENT_DESKTOP");
if (m_sWLRSession) {
if (m_pAqBackend->hasSession()) {
const auto CMD =
#ifdef USES_SYSTEMD
"systemctl --user unset-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
@ -351,7 +456,7 @@ void CCompositor::cleanup() {
for (auto& m : m_vMonitors) {
g_pHyprOpenGL->destroyMonitorResources(m.get());
wlr_output_state_set_enabled(m->state.wlr(), false);
m->output->state->setEnabled(false);
m->state.commit();
}
@ -388,14 +493,8 @@ void CCompositor::cleanup() {
g_pHyprCtl.reset();
g_pEventLoopManager.reset();
if (m_sWLRRenderer)
wlr_renderer_destroy(m_sWLRRenderer);
if (m_sWLRAllocator)
wlr_allocator_destroy(m_sWLRAllocator);
if (m_sWLRBackend)
wlr_backend_destroy(m_sWLRBackend);
if (m_pAqBackend)
m_pAqBackend.reset();
if (m_critSigSource)
wl_event_source_remove(m_critSigSource);
@ -441,6 +540,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the PointerManager!");
g_pPointerManager = std::make_unique<CPointerManager>();
Debug::log(LOG, "Creating the EventManager!");
g_pEventManager = std::make_unique<CEventManager>();
} break;
case STAGE_BASICINIT: {
Debug::log(LOG, "Creating the CHyprOpenGLImpl!");
@ -471,9 +573,6 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the SessionLockManager!");
g_pSessionLockManager = std::make_unique<CSessionLockManager>();
Debug::log(LOG, "Creating the EventManager!");
g_pEventManager = std::make_unique<CEventManager>();
Debug::log(LOG, "Creating the HyprDebugOverlay!");
g_pDebugOverlay = std::make_unique<CHyprDebugOverlay>();
@ -516,36 +615,42 @@ void CCompositor::removeLockFile() {
void CCompositor::prepareFallbackOutput() {
// create a backup monitor
wlr_backend* headless = nullptr;
wlr_multi_for_each_backend(
m_sWLRBackend,
[](wlr_backend* b, void* data) {
if (wlr_backend_is_headless(b))
*((wlr_backend**)data) = b;
},
&headless);
SP<Aquamarine::IBackendImplementation> headless;
for (auto& impl : m_pAqBackend->getImplementations()) {
if (impl->type() == Aquamarine::AQ_BACKEND_HEADLESS) {
headless = impl;
break;
}
}
if (!headless) {
Debug::log(WARN, "Unsafe state will be ineffective, no fallback output");
Debug::log(WARN, "No headless in prepareFallbackOutput?!");
return;
}
wlr_headless_add_output(headless, 1920, 1080);
headless->createOutput();
}
void CCompositor::startCompositor() {
initAllSignals();
// get socket, avoid using 0
for (int candidate = 1; candidate <= 32; candidate++) {
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
void CCompositor::startCompositor(std::string socketName, int socketFd) {
if (!socketName.empty() && socketFd != -1) {
fcntl(socketFd, F_SETFD, FD_CLOEXEC);
const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd);
if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break;
} else {
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
m_szWLDisplaySocket = socketName;
Debug::log(LOG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL);
} else
Debug::log(WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL);
} else {
// get socket, avoid using 0
for (int candidate = 1; candidate <= 32; candidate++) {
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
if (RETVAL >= 0) {
m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break;
} else
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
}
}
@ -558,15 +663,15 @@ void CCompositor::startCompositor() {
if (m_szWLDisplaySocket.empty()) {
Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
wlr_backend_destroy(m_sWLRBackend);
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
}
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
setenv("XDG_SESSION_TYPE", "wayland", 1);
signal(SIGPIPE, SIG_IGN);
if (m_sWLRSession /* Session-less Hyprland usually means a nest, don't update the env in that case */) {
if (m_pAqBackend->hasSession() /* Session-less Hyprland usually means a nest, don't update the env in that case */) {
const auto CMD =
#ifdef USES_SYSTEMD
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
@ -578,13 +683,6 @@ void CCompositor::startCompositor() {
Debug::log(LOG, "Running on WAYLAND_DISPLAY: {}", m_szWLDisplaySocket);
if (!wlr_backend_start(m_sWLRBackend)) {
Debug::log(CRIT, "Backend did not start!");
wlr_backend_destroy(m_sWLRBackend);
wl_display_destroy(m_sWLDisplay);
throwError("The backend could not start!");
}
prepareFallbackOutput();
g_pHyprRenderer->setCursorFromName("left_ptr");
@ -872,7 +970,7 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo
return vec - pWindow->m_vRealPosition.goal() - std::get<1>(iterData) + Vector2D{geom.x, geom.y};
}
CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
CMonitor* CCompositor::getMonitorFromOutput(SP<Aquamarine::IOutput> out) {
for (auto& m : m_vMonitors) {
if (m->output == out) {
return m.get();
@ -882,7 +980,7 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
return nullptr;
}
CMonitor* CCompositor::getRealMonitorFromOutput(wlr_output* out) {
CMonitor* CCompositor::getRealMonitorFromOutput(SP<Aquamarine::IOutput> out) {
for (auto& m : m_vRealMonitors) {
if (m->output == out) {
return m.get();
@ -2230,8 +2328,12 @@ void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMod
g_pInputManager->recheckIdleInhibitorStatus();
// DMAbuf stuff for direct scanout
g_pHyprRenderer->setWindowScanoutMode(pWindow);
// further updates require a monitor
if (!PMONITOR)
return;
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
g_pHyprRenderer->setSurfaceScanoutMode(pWindow->m_pWLSurface->resource(), on ? PMONITOR->self.lock() : nullptr);
g_pConfigManager->ensureVRR(PMONITOR);
}
@ -2272,8 +2374,8 @@ void CCompositor::updateWorkspaceWindowData(const int& id) {
}
}
void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) {
if ((m_sWLRSession && !m_sWLRSession->active) || !m_bSessionActive)
void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor, IOutput::scheduleFrameReason reason) {
if ((m_pAqBackend->hasSession() && !m_pAqBackend->session->active) || !m_bSessionActive)
return;
if (!pMonitor->m_bEnabled)
@ -2282,7 +2384,7 @@ void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) {
if (pMonitor->renderingActive)
pMonitor->pendingFrame = true;
wlr_output_schedule_frame(pMonitor->output);
pMonitor->output->scheduleFrame(reason);
}
PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) {
@ -2798,3 +2900,84 @@ PHLWINDOW CCompositor::windowForCPointer(CWindow* pWindow) {
return {};
}
static void checkDefaultCursorWarp(SP<CMonitor> PNEWMONITOR, std::string monitorName) {
static auto PCURSORMONITOR = CConfigValue<std::string>("cursor:default_monitor");
static auto firstMonitorAdded = std::chrono::system_clock::now();
static bool cursorDefaultDone = false;
static bool firstLaunch = true;
const auto POS = PNEWMONITOR->middle();
// by default, cursor should be set to first monitor detected
// this is needed as a default if the monitor given in config above doesn't exist
if (firstLaunch) {
firstLaunch = false;
g_pCompositor->warpCursorTo(POS, true);
g_pInputManager->refocus();
}
if (cursorDefaultDone || *PCURSORMONITOR == STRVAL_EMPTY)
return;
// after 10s, don't set cursor to default monitor
auto timePassedSec = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - firstMonitorAdded);
if (timePassedSec.count() > 10) {
cursorDefaultDone = true;
return;
}
if (*PCURSORMONITOR == monitorName) {
cursorDefaultDone = true;
g_pCompositor->warpCursorTo(POS, true);
g_pInputManager->refocus();
}
}
void CCompositor::onNewMonitor(SP<Aquamarine::IOutput> output) {
// add it to real
auto PNEWMONITOR = g_pCompositor->m_vRealMonitors.emplace_back(makeShared<CMonitor>());
if (std::string("HEADLESS-1") == output->name) {
g_pCompositor->m_pUnsafeOutput = PNEWMONITOR.get();
output->name = "FALLBACK"; // we are allowed to do this :)
}
Debug::log(LOG, "New output with name {}", output->name);
PNEWMONITOR->szName = output->name;
PNEWMONITOR->output = output;
PNEWMONITOR->self = PNEWMONITOR;
const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? output == g_pCompositor->m_pUnsafeOutput->output : false;
PNEWMONITOR->ID = FALLBACK ? -1 : g_pCompositor->getNextAvailableMonitorID(output->name);
PNEWMONITOR->isUnsafeFallback = FALLBACK;
EMIT_HOOK_EVENT("newMonitor", PNEWMONITOR);
if (!FALLBACK)
PNEWMONITOR->onConnect(false);
if (!PNEWMONITOR->m_bEnabled || FALLBACK)
return;
// ready to process if we have a real monitor
if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled)
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR.get();
g_pCompositor->m_bReadyToProcess = true;
g_pConfigManager->m_bWantsMonitorReload = true;
g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR.get(), IOutput::AQ_SCHEDULE_NEW_MONITOR);
checkDefaultCursorWarp(PNEWMONITOR, output->name);
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1;
w->updateSurfaceScaleTransformDetails();
}
}
g_pHyprRenderer->damageMonitor(PNEWMONITOR.get());
Events::listener_monitorFrame(PNEWMONITOR.get(), nullptr);
}

View file

@ -30,6 +30,9 @@
#include "plugins/PluginSystem.hpp"
#include "helpers/Watchdog.hpp"
#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/output/Output.hpp>
class CWLSurfaceResource;
enum eManagersInitStage {
@ -43,22 +46,11 @@ class CCompositor {
CCompositor();
~CCompositor();
// ------------------ WLR BASICS ------------------ //
wl_display* m_sWLDisplay;
wl_event_loop* m_sWLEventLoop;
wlr_backend* m_sWLRBackend;
wlr_session* m_sWLRSession;
wlr_renderer* m_sWLRRenderer;
wlr_allocator* m_sWLRAllocator;
wlr_compositor* m_sWLRCompositor;
wlr_subcompositor* m_sWLRSubCompositor;
wlr_drm* m_sWRLDRM;
wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
wlr_backend* m_sWLRHeadlessBackend;
// ------------------------------------------------- //
wl_display* m_sWLDisplay;
wl_event_loop* m_sWLEventLoop;
int m_iDRMFD = -1;
bool m_bInitialized = false;
SP<Aquamarine::CBackend> m_pAqBackend;
std::string m_szHyprTempDataRoot = "";
@ -78,7 +70,7 @@ class CCompositor {
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap;
void initServer();
void startCompositor();
void startCompositor(std::string socketName, int socketFd);
void cleanup();
void createLockFile();
void removeLockFile();
@ -94,10 +86,9 @@ class CCompositor {
bool m_bReadyToProcess = false;
bool m_bSessionActive = true;
bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false; // because wlroots
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false;
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
bool m_bExitTriggered = false; // For exit dispatcher
bool m_bIsShuttingDown = false;
// ------------------------------------------------- //
@ -116,8 +107,8 @@ class CCompositor {
SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, PHLLS*);
SP<CWLSurfaceResource> vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP<CWLSurfaceResource>);
CMonitor* getMonitorFromOutput(wlr_output*);
CMonitor* getRealMonitorFromOutput(wlr_output*);
CMonitor* getMonitorFromOutput(SP<Aquamarine::IOutput>);
CMonitor* getRealMonitorFromOutput(SP<Aquamarine::IOutput>);
PHLWINDOW getWindowFromSurface(SP<CWLSurfaceResource>);
PHLWINDOW getWindowFromHandle(uint32_t);
bool isWorkspaceVisible(PHLWORKSPACE);
@ -157,7 +148,7 @@ class CCompositor {
void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID);
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
PHLWINDOW getX11Parent(PHLWINDOW);
void scheduleFrameForMonitor(CMonitor*);
void scheduleFrameForMonitor(CMonitor*, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
void addToFadingOutSafe(PHLLS);
void removeFromFadingOutSafe(PHLLS);
void addToFadingOutSafe(PHLWINDOW);
@ -182,6 +173,7 @@ class CCompositor {
void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform);
void updateSuspendedStates();
PHLWINDOW windowForCPointer(CWindow*);
void onNewMonitor(SP<Aquamarine::IOutput> output);
std::string explicitConfigPath;

View file

@ -28,6 +28,7 @@
#include <ranges>
#include <unordered_set>
#include <hyprutils/string/String.hpp>
#include <filesystem>
using namespace Hyprutils::String;
extern "C" char** environ;
@ -491,6 +492,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("input:scroll_button_lock", Hyprlang::INT{0});
m_pConfig->addConfigValue("input:scroll_factor", {1.f});
m_pConfig->addConfigValue("input:scroll_points", {STRVAL_EMPTY});
m_pConfig->addConfigValue("input:emulate_discrete_scroll", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:touchpad:natural_scroll", Hyprlang::INT{0});
m_pConfig->addConfigValue("input:touchpad:disable_while_typing", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:touchpad:clickfinger_behavior", Hyprlang::INT{0});
@ -536,6 +538,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("gestures:workspace_swipe_forever", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_use_r", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_touch", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_touch_invert", Hyprlang::INT{0});
m_pConfig->addConfigValue("xwayland:use_nearest_neighbor", Hyprlang::INT{1});
m_pConfig->addConfigValue("xwayland:force_zero_scaling", Hyprlang::INT{0});
@ -557,6 +560,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("cursor:enable_hyprcursor", Hyprlang::INT{1});
m_pConfig->addConfigValue("cursor:hide_on_key_press", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:hide_on_touch", Hyprlang::INT{1});
m_pConfig->addConfigValue("cursor:allow_dumb_copy", Hyprlang::INT{0});
m_pConfig->addConfigValue("autogenerated", Hyprlang::INT{0});
@ -575,7 +579,10 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"});
m_pConfig->addConfigValue("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"});
// Devices
m_pConfig->addConfigValue("experimental:explicit_sync", Hyprlang::INT{0});
m_pConfig->addSpecialCategory("device", {"name"});
m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F});
m_pConfig->addSpecialConfigValue("device", "accel_profile", {STRVAL_EMPTY});
@ -1324,7 +1331,7 @@ void CConfigManager::dispatchExecOnce() {
return;
// update dbus env
if (g_pCompositor->m_sWLRSession)
if (g_pCompositor->m_pAqBackend->hasSession())
handleRawExec("",
#ifdef USES_SYSTEMD
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
@ -1449,7 +1456,8 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
if (USEVRR == 0) {
if (m->vrrActive) {
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
m->output->state->setAdaptiveSync(false);
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name);
@ -1458,11 +1466,11 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
return;
} else if (USEVRR == 1) {
if (!m->vrrActive) {
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1);
m->output->state->setAdaptiveSync(true);
if (!m->state.test()) {
Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
m->output->state->setAdaptiveSync(false);
}
if (!m->state.commit())
@ -1481,19 +1489,19 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
if (WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED) {
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1);
if (WORKSPACEFULL) {
m->output->state->setAdaptiveSync(true);
if (!m->state.test()) {
Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name);
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
m->output->state->setAdaptiveSync(false);
}
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
} else if (!WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) {
wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0);
} else if (!WORKSPACEFULL) {
m->output->state->setAdaptiveSync(false);
if (!m->state.commit())
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name);

View file

@ -5,6 +5,7 @@
#include <time.h>
#include <errno.h>
#include <sys/stat.h>
#include <filesystem>
#include "../plugins/PluginSystem.hpp"
#include "../signal-safe.hpp"

View file

@ -12,6 +12,8 @@
#include <sys/un.h>
#include <unistd.h>
#include <sys/poll.h>
#include <filesystem>
#include <ranges>
#include <sstream>
#include <string>
@ -20,6 +22,7 @@
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
#include <aquamarine/input/Input.hpp>
#include "../config/ConfigDataValues.hpp"
#include "../config/ConfigValue.hpp"
@ -53,20 +56,15 @@ static std::string formatToString(uint32_t drmFormat) {
static std::string availableModesForOutput(CMonitor* pMonitor, eHyprCtlOutputFormat format) {
std::string result;
if (!wl_list_empty(&pMonitor->output->modes)) {
wlr_output_mode* mode;
wl_list_for_each(mode, &pMonitor->output->modes, link) {
if (format == FORMAT_NORMAL)
result += std::format("{}x{}@{:.2f}Hz ", mode->width, mode->height, mode->refresh / 1000.0);
else
result += std::format("\"{}x{}@{:.2f}Hz\",", mode->width, mode->height, mode->refresh / 1000.0);
}
result.pop_back();
for (auto& m : pMonitor->output->modes) {
if (format == FORMAT_NORMAL)
result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
else
result += std::format("\"{}x{}@{:.2f}Hz\",", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
}
trimTrailingComma(result);
return result;
}
@ -75,8 +73,10 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
if (!m->output || m->ID == -1ull)
return "";
result += std::format(
R"#({{
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += std::format(
R"#({{
"id": {},
"name": "{}",
"description": "{}",
@ -107,14 +107,27 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
"currentFormat": "{}",
"availableModes": [{}]
}},)#",
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szShortDescription), escapeJSONStrings(m->output->make ? m->output->make : ""),
escapeJSONStrings(m->output->model ? m->output->model : ""), escapeJSONStrings(m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y,
m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : escapeJSONStrings(m->activeWorkspace->m_szName)),
m->activeSpecialWorkspaceID(), escapeJSONStrings(m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x,
(int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform,
(m == g_pCompositor->m_pLastMonitor ? "true" : "false"), (m->dpmsStatus ? "true" : "false"),
(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED ? "true" : "false"), (m->tearingState.activelyTearing ? "true" : "false"),
(m->m_bEnabled ? "false" : "true"), formatToString(m->drmFormat), availableModesForOutput(m.get(), format));
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szShortDescription), escapeJSONStrings(m->output->make), escapeJSONStrings(m->output->model),
escapeJSONStrings(m->output->serial), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
m->activeWorkspaceID(), (!m->activeWorkspace ? "" : escapeJSONStrings(m->activeWorkspace->m_szName)), m->activeSpecialWorkspaceID(),
escapeJSONStrings(m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
(int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m == g_pCompositor->m_pLastMonitor ? "true" : "false"),
(m->dpmsStatus ? "true" : "false"), (m->output->state->state().adaptiveSync ? "true" : "false"), (m->tearingState.activelyTearing ? "true" : "false"),
(m->m_bEnabled ? "false" : "true"), formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
} else {
result += std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
"special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
"dpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n",
m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription,
m->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName),
m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x,
(int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform,
(m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, m->output->state->state().adaptiveSync, m->tearingState.activelyTearing,
!m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
}
return result;
}
@ -144,16 +157,16 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
if (!m->output || m->ID == -1ull)
continue;
result += std::format(
"Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
"special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
"dpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n",
m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription,
(m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspaceID(),
(!m->activeWorkspace ? "" : m->activeWorkspace->m_szName), m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""),
(int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform,
(m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, (int)(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED),
m->tearingState.activelyTearing, !m->m_bEnabled, formatToString(m->drmFormat), availableModesForOutput(m.get(), format));
result +=
std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
"special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
"dpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n",
m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription,
m->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName),
m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x,
(int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform,
(m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, (int)(m->output->state ? m->output->state->state().adaptiveSync : false),
m->tearingState.activelyTearing, !m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
}
}
@ -552,8 +565,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"defaultSpeed": {:.5f}
}},)#",
(uintptr_t)m.get(), escapeJSONStrings(m->hlName),
wlr_input_device_is_libinput(&m->wlr()->base) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base)) :
0.f);
m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f);
}
trimTrailingComma(result);
@ -611,9 +623,8 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
R"#( {{
"address": "0x{:x}",
"type": "tabletTool",
"belongsTo": "0x{:x}"
}},)#",
(uintptr_t)d.get(), d->wlr() ? (uintptr_t)d->wlr()->data : 0);
(uintptr_t)d.get());
}
trimTrailingComma(result);
@ -641,7 +652,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"address": "0x{:x}",
"name": "{}"
}},)#",
(uintptr_t)&d, escapeJSONStrings(d.pWlrDevice ? d.pWlrDevice->name : ""));
(uintptr_t)&d, escapeJSONStrings(d.pDevice ? d.pDevice->getName() : ""));
}
trimTrailingComma(result);
@ -654,9 +665,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
for (auto& m : g_pInputManager->m_vPointers) {
result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)m.get(), m->hlName,
(wlr_input_device_is_libinput(&m->wlr()->base) ?
libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base)) :
0.f));
(m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f));
}
result += "\n\nKeyboards:\n";
@ -675,11 +684,11 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
}
for (auto& d : g_pInputManager->m_vTablets) {
result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->hlName, d->wlr()->width_mm, d->wlr()->height_mm);
result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->hlName, d->aq()->physicalSize.x, d->aq()->physicalSize.y);
}
for (auto& d : g_pInputManager->m_vTabletTools) {
result += std::format("\tTablet Tool at {:x} (belongs to {:x})\n", (uintptr_t)d.get(), d->wlr() ? (uintptr_t)d->wlr()->data : 0);
result += std::format("\tTablet Tool at {:x}\n", (uintptr_t)d.get());
}
result += "\n\nTouch:\n";
@ -691,7 +700,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n\nSwitches:\n";
for (auto& d : g_pInputManager->m_lSwitches) {
result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.pWlrDevice ? d.pWlrDevice->name : "");
result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.pDevice ? d.pDevice->getName() : "");
}
}
@ -1125,24 +1134,22 @@ std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string requ
if (PKEYBOARD == g_pInputManager->m_vKeyboards.end())
return "device not found";
const auto PWLRKEYBOARD = (*PKEYBOARD)->wlr();
const auto LAYOUTS = xkb_keymap_num_layouts(PWLRKEYBOARD->keymap);
const auto KEEB = *PKEYBOARD;
const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->xkbKeymap);
xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) {
if (xkb_state_layout_index_is_active(PWLRKEYBOARD->xkb_state, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1)
if (xkb_state_layout_index_is_active(KEEB->xkbState, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1)
break;
activeLayout++;
}
if (CMD == "next") {
wlr_keyboard_notify_modifiers(PWLRKEYBOARD, PWLRKEYBOARD->modifiers.depressed, PWLRKEYBOARD->modifiers.latched, PWLRKEYBOARD->modifiers.locked,
activeLayout > LAYOUTS ? 0 : activeLayout + 1);
} else if (CMD == "prev") {
wlr_keyboard_notify_modifiers(PWLRKEYBOARD, PWLRKEYBOARD->modifiers.depressed, PWLRKEYBOARD->modifiers.latched, PWLRKEYBOARD->modifiers.locked,
activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
} else {
if (CMD == "next")
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1);
else if (CMD == "prev")
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
else {
int requestedLayout = 0;
try {
requestedLayout = std::stoi(CMD);
@ -1152,7 +1159,7 @@ std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string requ
return "layout idx out of range of " + std::to_string(LAYOUTS);
}
wlr_keyboard_notify_modifiers(PWLRKEYBOARD, PWLRKEYBOARD->modifiers.depressed, PWLRKEYBOARD->modifiers.latched, PWLRKEYBOARD->modifiers.locked, requestedLayout);
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, requestedLayout);
}
return "ok";
@ -1368,43 +1375,6 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request)
return result;
}
static bool addOutput(wlr_backend* backend, const std::string& type, const std::string& name) {
wlr_output* output = nullptr;
if (type.empty() || type == "auto") {
if (wlr_backend_is_wl(backend))
output = wlr_wl_output_create(backend);
else if (wlr_backend_is_headless(backend))
output = wlr_headless_add_output(backend, 1920, 1080);
} else {
if (wlr_backend_is_wl(backend) && type == "wayland")
output = wlr_wl_output_create(backend);
else if (wlr_backend_is_headless(backend) && type == "headless")
output = wlr_headless_add_output(backend, 1920, 1080);
}
if (output && !name.empty())
g_pCompositor->getMonitorFromOutput(output)->szName = name;
return output != nullptr;
}
struct outputData {
std::string type;
std::string name;
bool added;
};
void createOutputIter(wlr_backend* backend, void* data) {
const auto DATA = static_cast<outputData*>(data);
if (DATA->added)
return;
if (addOutput(backend, DATA->type, DATA->name))
DATA->added = true;
}
std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
@ -1413,15 +1383,36 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
const auto MODE = vars[1];
bool added = false;
if (!vars[3].empty()) {
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->szName == vars[3])
return "Name already taken";
}
}
if (MODE == "create" || MODE == "add") {
if (g_pCompositor->getMonitorFromName(vars[3]))
return "A real monitor already uses that name.";
outputData result{vars[2], vars[3], false};
for (auto& impl : g_pCompositor->m_pAqBackend->getImplementations() | std::views::reverse) {
auto type = impl->type();
wlr_multi_for_each_backend(g_pCompositor->m_sWLRBackend, createOutputIter, &result);
if (type == Aquamarine::AQ_BACKEND_HEADLESS && (vars[2] == "headless" || vars[2] == "auto")) {
added = true;
impl->createOutput(vars[3]);
break;
}
if (!result.added)
if (type == Aquamarine::AQ_BACKEND_WAYLAND && (vars[2] == "wayland" || vars[2] == "auto")) {
added = true;
impl->createOutput(vars[3]);
break;
}
}
if (!added)
return "no backend replied to the request";
} else if (MODE == "destroy" || MODE == "remove") {
@ -1433,7 +1424,7 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
if (!PMONITOR->createdByUser)
return "cannot remove a real display. Use the monitor keyword.";
wlr_output_destroy(PMONITOR->output);
PMONITOR->output->destroy();
}
return "ok";

View file

@ -10,30 +10,6 @@ void Debug::init(const std::string& IS) {
logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
}
void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) {
if (level > wlr_log_get_verbosity())
return;
char* outputStr = nullptr;
vasprintf(&outputStr, fmt, args);
std::string output = std::string(outputStr);
free(outputStr);
rollingLog += output + "\n";
if (!disableLogs || !**disableLogs) {
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
ofs << "[wlr] " << output << "\n";
ofs.close();
}
if (!disableStdout)
std::cout << output << "\n";
}
void Debug::log(LogLevel level, std::string str) {
if (level == TRACE && !trace)
return;

View file

@ -67,6 +67,4 @@ namespace Debug {
log(level, logMsg);
}
void wlrLog(wlr_log_importance level, const char* fmt, va_list args);
};

View file

@ -55,7 +55,7 @@ void CPopup::initAllSignals() {
}
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
const auto POPUP = m_vChildren.emplace_back(std::make_unique<CPopup>(popup, this)).get();
const auto POPUP = m_vChildren.emplace_back(makeShared<CPopup>(popup, this)).get();
Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP);
}
@ -250,7 +250,8 @@ void CPopup::recheckTree() {
}
void CPopup::recheckChildrenRecursive() {
for (auto& c : m_vChildren) {
auto cpy = m_vChildren;
for (auto& c : cpy) {
c->onCommit(true);
c->recheckChildrenRecursive();
}

View file

@ -60,8 +60,8 @@ class CPopup {
bool m_bMapped = false;
//
std::vector<std::unique_ptr<CPopup>> m_vChildren;
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
std::vector<SP<CPopup>> m_vChildren;
std::unique_ptr<CSubsurface> m_pSubsurfaceHead;
struct {
CHyprSignalListener newPopup;

View file

@ -1155,7 +1155,8 @@ int CWindow::getRealBorderSize() {
}
bool CWindow::canBeTorn() {
return m_sWindowData.tearing.valueOr(m_bTearingHint);
static auto PTEARING = CConfigValue<Hyprlang::INT>("general:allow_tearing");
return m_sWindowData.tearing.valueOr(m_bTearingHint) && *PTEARING;
}
bool CWindow::shouldSendFullscreenState() {

View file

@ -2,7 +2,21 @@
#include "../defines.hpp"
#include "../helpers/varlist/VarList.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/SeatManager.hpp"
#include "../config/ConfigManager.hpp"
#include <sys/mman.h>
#include <aquamarine/input/Input.hpp>
#include <cstring>
#define LED_COUNT 3
constexpr static std::array<const char*, 8> MODNAMES = {
XKB_MOD_NAME_SHIFT, XKB_MOD_NAME_CAPS, XKB_MOD_NAME_CTRL, XKB_MOD_NAME_ALT, XKB_MOD_NAME_NUM, "Mod3", XKB_MOD_NAME_LOGO, "Mod5",
};
constexpr static std::array<const char*, 3> LEDNAMES = {XKB_LED_NAME_NUM, XKB_LED_NAME_CAPS, XKB_LED_NAME_SCROLL};
//
uint32_t IKeyboard::getCapabilities() {
return HID_INPUT_CAPABILITY_KEYBOARD;
}
@ -14,27 +28,149 @@ eHIDType IKeyboard::getType() {
IKeyboard::~IKeyboard() {
events.destroy.emit();
if (!xkbTranslationState)
return;
clearManuallyAllocd();
}
xkb_state_unref(xkbTranslationState);
xkbTranslationState = nullptr;
void IKeyboard::clearManuallyAllocd() {
if (xkbStaticState)
xkb_state_unref(xkbStaticState);
if (xkbState)
xkb_state_unref(xkbState);
if (xkbKeymap)
xkb_keymap_unref(xkbKeymap);
if (xkbKeymapFD >= 0)
close(xkbKeymapFD);
xkbKeymap = nullptr;
xkbState = nullptr;
xkbStaticState = nullptr;
xkbKeymapFD = -1;
}
void IKeyboard::setKeymap(const SStringRuleNames& rules) {
currentRules = rules;
xkb_rule_names XKBRULES = {
.rules = rules.rules.c_str(),
.model = rules.model.c_str(),
.layout = rules.layout.c_str(),
.variant = rules.variant.c_str(),
.options = rules.options.c_str(),
};
const auto CONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!CONTEXT) {
Debug::log(ERR, "setKeymap: CONTEXT null??");
return;
}
clearManuallyAllocd();
Debug::log(LOG, "Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {})", rules.layout, rules.variant, rules.rules, rules.model,
rules.options);
if (!xkbFilePath.empty()) {
auto path = absolutePath(xkbFilePath, g_pConfigManager->configCurrentPath);
if (FILE* const KEYMAPFILE = fopen(path.c_str(), "r"); !KEYMAPFILE)
Debug::log(ERR, "Cannot open input:kb_file= file for reading");
else {
xkbKeymap = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
fclose(KEYMAPFILE);
}
}
if (!xkbKeymap)
xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!xkbKeymap) {
g_pConfigManager->addParseError("Invalid keyboard layout passed. ( rules: " + rules.rules + ", model: " + rules.model + ", variant: " + rules.variant +
", options: " + rules.options + ", layout: " + rules.layout + " )");
Debug::log(ERR, "Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, rules.variant, rules.rules, rules.model,
rules.options);
memset(&XKBRULES, 0, sizeof(XKBRULES));
currentRules.rules = "";
currentRules.model = "";
currentRules.variant = "";
currentRules.options = "";
currentRules.layout = "us";
xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
// set internal translation state
// demo sunao ni ienai
xkbStaticState = xkb_state_new(xkbKeymap);
updateXKBTranslationState(xkbKeymap);
const auto NUMLOCKON = g_pConfigManager->getDeviceInt(hlName, "numlock_by_default", "input:numlock_by_default");
if (NUMLOCKON == 1) {
// lock numlock
const auto IDX = xkb_map_mod_get_index(xkbKeymap, XKB_MOD_NAME_NUM);
if (IDX != XKB_MOD_INVALID)
modifiersState.locked |= (uint32_t)1 << IDX;
updateModifiers(modifiersState.depressed, modifiersState.latched, modifiersState.locked, modifiersState.group);
}
for (size_t i = 0; i < LEDNAMES.size(); ++i) {
ledIndexes.at(i) = xkb_map_led_get_index(xkbKeymap, LEDNAMES.at(i));
Debug::log(LOG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES.at(i), ledIndexes.at(i));
}
for (size_t i = 0; i < MODNAMES.size(); ++i) {
modIndexes.at(i) = xkb_map_mod_get_index(xkbKeymap, MODNAMES.at(i));
Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES.at(i), modIndexes.at(i));
}
auto cKeymapStr = xkb_keymap_get_as_string(xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1);
xkbKeymapString = cKeymapStr;
free(cKeymapStr);
int rw, ro;
if (!allocateSHMFilePair(xkbKeymapString.length() + 1, &rw, &ro))
Debug::log(ERR, "IKeyboard: failed to allocate shm pair for the keymap");
else {
auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw, 0);
close(rw);
if (keymapFDDest == MAP_FAILED) {
Debug::log(ERR, "IKeyboard: failed to mmap a shm pair for the keymap");
close(ro);
} else {
memcpy(keymapFDDest, xkbKeymapString.c_str(), xkbKeymapString.length());
munmap(keymapFDDest, xkbKeymapString.length() + 1);
xkbKeymapFD = ro;
}
}
xkb_context_unref(CONTEXT);
g_pSeatManager->updateActiveKeyboardData();
}
void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
if (xkbTranslationState)
xkb_state_unref(xkbTranslationState);
if (xkbState)
xkb_state_unref(xkbState);
xkbState = nullptr;
if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
xkbTranslationState = xkb_state_new(keymap);
xkbState = xkb_state_new(keymap);
return;
}
const auto WLRKB = wlr();
const auto KEYMAP = WLRKB->keymap;
const auto STATE = WLRKB->xkb_state;
const auto KEYMAP = xkbKeymap;
const auto STATE = xkbState;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
@ -73,7 +209,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
xkbTranslationState = xkb_state_new(KEYMAP);
xkbState = xkb_state_new(KEYMAP);
xkb_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT);
@ -94,16 +230,15 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
const auto NEWKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
xkbTranslationState = xkb_state_new(NEWKEYMAP);
xkbState = xkb_state_new(NEWKEYMAP);
xkb_keymap_unref(NEWKEYMAP);
xkb_context_unref(PCONTEXT);
}
std::string IKeyboard::getActiveLayout() {
const auto WLRKB = wlr();
const auto KEYMAP = WLRKB->keymap;
const auto STATE = WLRKB->xkb_state;
const auto KEYMAP = xkbKeymap;
const auto STATE = xkbState;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
@ -120,14 +255,12 @@ std::string IKeyboard::getActiveLayout() {
}
void IKeyboard::updateLEDs() {
auto keyboard = wlr();
if (!keyboard || keyboard->xkb_state == nullptr)
if (xkbState == 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]))
for (uint32_t i = 0; i < LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(xkbState, ledIndexes.at(i)))
leds |= (1 << i);
}
@ -135,13 +268,88 @@ void IKeyboard::updateLEDs() {
}
void IKeyboard::updateLEDs(uint32_t leds) {
auto keyboard = wlr();
if (!keyboard || keyboard->xkb_state == nullptr)
if (!xkbState)
return;
if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock()))
return;
wlr_keyboard_led_update(keyboard, leds);
if (!aq())
return;
aq()->updateLEDs(leds);
}
uint32_t IKeyboard::getModifiers() {
uint32_t modMask = modifiersState.depressed | modifiersState.latched;
uint32_t mods = 0;
for (size_t i = 0; i < modIndexes.size(); ++i) {
if (modIndexes.at(i) == XKB_MOD_INVALID)
continue;
if (!(modMask & (1 << modIndexes.at(i))))
continue;
mods |= (1 << i);
}
return mods;
}
void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
if (!xkbState)
return;
xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0, group);
if (!updateModifiersState())
return;
updateLEDs();
}
bool IKeyboard::updateModifiersState() {
if (!xkbState)
return false;
auto depressed = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_DEPRESSED);
auto latched = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED);
auto locked = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED);
auto group = xkb_state_serialize_layout(xkbState, XKB_STATE_LAYOUT_EFFECTIVE);
if (depressed == modifiersState.depressed && latched == modifiersState.latched && locked == modifiersState.locked && group == modifiersState.group)
return false;
modifiersState.depressed = depressed;
modifiersState.latched = latched;
modifiersState.locked = locked;
modifiersState.group = group;
return true;
}
void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) {
const auto contains = std::find(pressedXKB.begin(), pressedXKB.end(), xkbKey) != pressedXKB.end();
if (contains && pressed)
return;
if (!contains && !pressed)
return;
if (contains)
std::erase(pressedXKB, xkbKey);
else
pressedXKB.emplace_back(xkbKey);
xkb_state_update_key(xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
if (updateModifiersState()) {
keyboardEvents.modifiers.emit(SModifiersEvent{
.depressed = modifiersState.depressed,
.latched = modifiersState.latched,
.locked = modifiersState.locked,
.group = modifiersState.group,
});
}
}

View file

@ -7,15 +7,26 @@
#include <xkbcommon/xkbcommon.h>
struct wlr_keyboard;
AQUAMARINE_FORWARD(IKeyboard);
enum eKeyboardModifiers {
HL_MODIFIER_SHIFT = (1 << 0),
HL_MODIFIER_CAPS = (1 << 1),
HL_MODIFIER_CTRL = (1 << 2),
HL_MODIFIER_ALT = (1 << 3),
HL_MODIFIER_MOD2 = (1 << 4),
HL_MODIFIER_MOD3 = (1 << 5),
HL_MODIFIER_META = (1 << 6),
HL_MODIFIER_MOD5 = (1 << 7),
};
class IKeyboard : public IHID {
public:
virtual ~IKeyboard();
virtual uint32_t getCapabilities();
virtual eHIDType getType();
virtual bool isVirtual() = 0;
virtual wlr_keyboard* wlr() = 0;
virtual uint32_t getCapabilities();
virtual eHIDType getType();
virtual bool isVirtual() = 0;
virtual SP<Aquamarine::IKeyboard> aq() = 0;
struct SKeyEvent {
uint32_t timeMs = 0;
@ -24,6 +35,17 @@ class IKeyboard : public IHID {
wl_keyboard_key_state state = WL_KEYBOARD_KEY_STATE_PRESSED;
};
struct SKeymapEvent {
xkb_keymap* keymap = nullptr;
};
struct SModifiersEvent {
uint32_t depressed = 0;
uint32_t latched = 0;
uint32_t locked = 0;
uint32_t group = 0;
};
struct {
CSignal key;
CSignal modifiers;
@ -39,25 +61,46 @@ class IKeyboard : public IHID {
std::string rules = "";
};
void setKeymap(const SStringRuleNames& rules);
void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
std::string getActiveLayout();
void updateLEDs();
void updateLEDs(uint32_t leds);
uint32_t getModifiers();
void updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
bool updateModifiersState(); // rets whether changed
void updateXkbStateWithKey(uint32_t xkbKey, bool pressed);
bool active = false;
bool enabled = true;
xkb_layout_index_t activeLayout = 0;
xkb_state* xkbTranslationState = nullptr;
xkb_layout_index_t activeLayout = 0;
xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr;
xkb_keymap* xkbKeymap = nullptr;
std::string hlName = "";
std::string xkbFilePath = "";
struct {
uint32_t depressed = 0, latched = 0, locked = 0, group = 0;
} modifiersState;
SStringRuleNames currentRules;
int repeatRate = 0;
int repeatDelay = 0;
int numlockOn = -1;
bool resolveBindsBySym = false;
std::array<xkb_led_index_t, 3> ledIndexes = {XKB_MOD_INVALID};
std::array<xkb_mod_index_t, 8> modIndexes = {XKB_MOD_INVALID};
uint32_t leds = 0;
WP<IKeyboard> self;
std::string hlName = "";
std::string xkbFilePath = "";
std::string xkbKeymapString = "";
int xkbKeymapFD = -1;
SStringRuleNames currentRules;
int repeatRate = 0;
int repeatDelay = 0;
int numlockOn = -1;
bool resolveBindsBySym = false;
WP<IKeyboard> self;
private:
void clearManuallyAllocd();
std::vector<uint32_t> pressedXKB;
};

View file

@ -5,17 +5,17 @@
#include "../macros.hpp"
#include "../helpers/math/Math.hpp"
struct wlr_pointer;
AQUAMARINE_FORWARD(IPointer);
/*
Base class for a pointer.
*/
class IPointer : public IHID {
public:
virtual uint32_t getCapabilities();
virtual eHIDType getType();
virtual bool isVirtual() = 0;
virtual wlr_pointer* wlr() = 0;
virtual uint32_t getCapabilities();
virtual eHIDType getType();
virtual bool isVirtual() = 0;
virtual SP<Aquamarine::IPointer> aq() = 0;
struct SMotionEvent {
uint32_t timeMs = 0;

View file

@ -5,14 +5,14 @@
#include "../macros.hpp"
#include "../helpers/math/Math.hpp"
struct wlr_touch;
AQUAMARINE_FORWARD(ITouch);
class ITouch : public IHID {
public:
virtual uint32_t getCapabilities();
virtual eHIDType getType();
virtual bool isVirtual() = 0;
virtual wlr_touch* wlr() = 0;
virtual uint32_t getCapabilities();
virtual eHIDType getType();
virtual bool isVirtual() = 0;
virtual SP<Aquamarine::ITouch> aq() = 0;
struct SDownEvent {
uint32_t timeMs = 0;

View file

@ -1,7 +1,10 @@
#include "Keyboard.hpp"
#include "../defines.hpp"
#include "../Compositor.hpp"
SP<CKeyboard> CKeyboard::create(wlr_keyboard* keeb) {
#include <aquamarine/input/Input.hpp>
SP<CKeyboard> CKeyboard::create(SP<Aquamarine::IKeyboard> keeb) {
SP<CKeyboard> pKeeb = SP<CKeyboard>(new CKeyboard(keeb));
pKeeb->self = pKeeb;
@ -13,52 +16,41 @@ bool CKeyboard::isVirtual() {
return false;
}
wlr_keyboard* CKeyboard::wlr() {
return keyboard;
SP<Aquamarine::IKeyboard> CKeyboard::aq() {
return keyboard.lock();
}
CKeyboard::CKeyboard(wlr_keyboard* keeb) : keyboard(keeb) {
CKeyboard::CKeyboard(SP<Aquamarine::IKeyboard> keeb) : keyboard(keeb) {
if (!keeb)
return;
// clang-format off
hyprListener_destroy.initCallback(&keeb->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
keyboard = nullptr;
events.destroy.emit();
}, this, "CKeyboard");
listeners.destroy = keeb->events.destroy.registerListener([this](std::any d) {
keyboard.reset();
events.destroy.emit();
});
hyprListener_key.initCallback(&keeb->events.key, [this] (void* owner, void* data) {
auto E = (wlr_keyboard_key_event*)data;
listeners.key = keeb->events.key.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IKeyboard::SKeyEvent>(d);
updateXkbStateWithKey(E.key + 8, E.pressed);
keyboardEvents.key.emit(SKeyEvent{
.timeMs = E->time_msec,
.keycode = E->keycode,
.updateMods = E->update_state,
.state = E->state,
.timeMs = E.timeMs,
.keycode = E.key,
.state = E.pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
});
}, this, "CKeyboard");
});
hyprListener_keymap.initCallback(&keeb->events.keymap, [this] (void* owner, void* data) {
keyboardEvents.keymap.emit();
}, this, "CKeyboard");
listeners.modifiers = keeb->events.modifiers.registerListener([this](std::any d) {
updateModifiersState();
hyprListener_modifiers.initCallback(&keeb->events.modifiers, [this] (void* owner, void* data) {
keyboardEvents.modifiers.emit();
}, this, "CKeyboard");
keyboardEvents.modifiers.emit(SModifiersEvent{
.depressed = modifiersState.depressed,
.latched = modifiersState.latched,
.locked = modifiersState.locked,
.group = modifiersState.group,
});
});
hyprListener_repeatInfo.initCallback(&keeb->events.repeat_info, [this] (void* owner, void* data) {
keyboardEvents.repeatInfo.emit();
}, this, "CKeyboard");
// clang-format on
deviceName = keeb->base.name ? keeb->base.name : "UNKNOWN";
}
void CKeyboard::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_key.removeCallback();
hyprListener_keymap.removeCallback();
hyprListener_repeatInfo.removeCallback();
hyprListener_modifiers.removeCallback();
deviceName = keeb->getName();
}

View file

@ -4,21 +4,19 @@
class CKeyboard : public IKeyboard {
public:
static SP<CKeyboard> create(wlr_keyboard* keeb);
static SP<CKeyboard> create(SP<Aquamarine::IKeyboard> keeb);
virtual bool isVirtual();
virtual wlr_keyboard* wlr();
virtual bool isVirtual();
virtual SP<Aquamarine::IKeyboard> aq();
private:
CKeyboard(wlr_keyboard* keeb);
CKeyboard(SP<Aquamarine::IKeyboard> keeb);
wlr_keyboard* keyboard = nullptr;
WP<Aquamarine::IKeyboard> keyboard;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(key);
DYNLISTENER(modifiers);
DYNLISTENER(keymap);
DYNLISTENER(repeatInfo);
struct {
CHyprSignalListener destroy;
CHyprSignalListener key;
CHyprSignalListener modifiers;
} listeners;
};

View file

@ -1,7 +1,8 @@
#include "Mouse.hpp"
#include "../defines.hpp"
#include <aquamarine/input/Input.hpp>
SP<CMouse> CMouse::create(wlr_pointer* mouse) {
SP<CMouse> CMouse::create(SP<Aquamarine::IPointer> mouse) {
SP<CMouse> pMouse = SP<CMouse>(new CMouse(mouse));
pMouse->self = pMouse;
@ -9,166 +10,143 @@ SP<CMouse> CMouse::create(wlr_pointer* mouse) {
return pMouse;
}
CMouse::CMouse(wlr_pointer* mouse_) : mouse(mouse_) {
CMouse::CMouse(SP<Aquamarine::IPointer> mouse_) : mouse(mouse_) {
if (!mouse)
return;
// clang-format off
hyprListener_destroy.initCallback(&mouse->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
mouse = nullptr;
listeners.destroy = mouse->events.destroy.registerListener([this](std::any d) {
mouse.reset();
events.destroy.emit();
}, this, "CMouse");
});
hyprListener_motion.initCallback(&mouse->events.motion, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_event*)data;
listeners.motion = mouse->events.move.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SMoveEvent>(d);
pointerEvents.motion.emit(SMotionEvent{
.timeMs = E->time_msec,
.delta = {E->delta_x, E->delta_y},
.unaccel = {E->unaccel_dx, E->unaccel_dy},
.timeMs = E.timeMs,
.delta = E.delta,
.unaccel = E.unaccel,
});
}, this, "CMouse");
});
hyprListener_motionAbsolute.initCallback(&mouse->events.motion_absolute, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_absolute_event*)data;
listeners.motionAbsolute = mouse->events.warp.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SWarpEvent>(d);
pointerEvents.motionAbsolute.emit(SMotionAbsoluteEvent{
.timeMs = E->time_msec,
.absolute = {E->x, E->y},
.timeMs = E.timeMs,
.absolute = E.absolute,
.device = self.lock(),
});
}, this, "CMouse");
});
hyprListener_button.initCallback(&mouse->events.button, [this] (void* owner, void* data) {
auto E = (wlr_pointer_button_event*)data;
listeners.button = mouse->events.button.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SButtonEvent>(d);
pointerEvents.button.emit(SButtonEvent{
.timeMs = E->time_msec,
.button = E->button,
.state = (wl_pointer_button_state)E->state,
.timeMs = E.timeMs,
.button = E.button,
.state = E.pressed ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED,
});
}, this, "CMouse");
});
hyprListener_axis.initCallback(&mouse->events.axis, [this] (void* owner, void* data) {
auto E = (wlr_pointer_axis_event*)data;
listeners.axis = mouse->events.axis.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SAxisEvent>(d);
pointerEvents.axis.emit(SAxisEvent{
.timeMs = E->time_msec,
.source = E->source,
.axis = E->orientation,
.relativeDirection = E->relative_direction,
.delta = E->delta,
.deltaDiscrete = E->delta_discrete,
.timeMs = E.timeMs,
.source = (wl_pointer_axis_source)E.source,
.axis = (wl_pointer_axis)E.axis,
.relativeDirection = (wl_pointer_axis_relative_direction)E.direction,
.delta = E.delta,
.deltaDiscrete = E.discrete,
});
}, this, "CMouse");
});
hyprListener_frame.initCallback(&mouse->events.frame, [this] (void* owner, void* data) {
pointerEvents.frame.emit();
}, this, "CMouse");
listeners.frame = mouse->events.frame.registerListener([this](std::any d) { pointerEvents.frame.emit(); });
hyprListener_swipeBegin.initCallback(&mouse->events.swipe_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_begin_event*)data;
listeners.swipeBegin = mouse->events.swipeBegin.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SSwipeBeginEvent>(d);
pointerEvents.swipeBegin.emit(SSwipeBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.timeMs = E.timeMs,
.fingers = E.fingers,
});
}, this, "CMouse");
});
hyprListener_swipeEnd.initCallback(&mouse->events.swipe_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_end_event*)data;
listeners.swipeEnd = mouse->events.swipeEnd.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SSwipeEndEvent>(d);
pointerEvents.swipeEnd.emit(SSwipeEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
.timeMs = E.timeMs,
.cancelled = E.cancelled,
});
}, this, "CMouse");
});
hyprListener_swipeUpdate.initCallback(&mouse->events.swipe_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_update_event*)data;
listeners.swipeUpdate = mouse->events.swipeUpdate.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SSwipeUpdateEvent>(d);
pointerEvents.swipeUpdate.emit(SSwipeUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
.timeMs = E.timeMs,
.fingers = E.fingers,
.delta = E.delta,
});
}, this, "CMouse");
});
hyprListener_pinchBegin.initCallback(&mouse->events.pinch_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_begin_event*)data;
listeners.pinchBegin = mouse->events.pinchBegin.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SPinchBeginEvent>(d);
pointerEvents.pinchBegin.emit(SPinchBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.timeMs = E.timeMs,
.fingers = E.fingers,
});
}, this, "CMouse");
});
hyprListener_pinchEnd.initCallback(&mouse->events.pinch_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_end_event*)data;
listeners.pinchEnd = mouse->events.pinchEnd.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SPinchEndEvent>(d);
pointerEvents.pinchEnd.emit(SPinchEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
.timeMs = E.timeMs,
.cancelled = E.cancelled,
});
}, this, "CMouse");
});
hyprListener_pinchUpdate.initCallback(&mouse->events.pinch_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_update_event*)data;
listeners.pinchUpdate = mouse->events.pinchUpdate.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SPinchUpdateEvent>(d);
pointerEvents.pinchUpdate.emit(SPinchUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
.scale = E->scale,
.rotation = E->rotation,
.timeMs = E.timeMs,
.fingers = E.fingers,
.delta = E.delta,
.scale = E.scale,
.rotation = E.rotation,
});
}, this, "CMouse");
});
hyprListener_holdBegin.initCallback(&mouse->events.hold_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_begin_event*)data;
listeners.holdBegin = mouse->events.holdBegin.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SHoldBeginEvent>(d);
pointerEvents.holdBegin.emit(SHoldBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.timeMs = E.timeMs,
.fingers = E.fingers,
});
}, this, "CMouse");
});
hyprListener_holdEnd.initCallback(&mouse->events.hold_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_end_event*)data;
listeners.holdEnd = mouse->events.holdEnd.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IPointer::SHoldEndEvent>(d);
pointerEvents.holdEnd.emit(SHoldEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
.timeMs = E.timeMs,
.cancelled = E.cancelled,
});
}, this, "CMouse");
});
// clang-format on
deviceName = mouse->base.name ? mouse->base.name : "UNKNOWN";
}
void CMouse::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_motion.removeCallback();
hyprListener_motionAbsolute.removeCallback();
hyprListener_button.removeCallback();
hyprListener_axis.removeCallback();
hyprListener_frame.removeCallback();
hyprListener_swipeBegin.removeCallback();
hyprListener_swipeEnd.removeCallback();
hyprListener_swipeUpdate.removeCallback();
hyprListener_pinchBegin.removeCallback();
hyprListener_pinchEnd.removeCallback();
hyprListener_pinchUpdate.removeCallback();
hyprListener_holdBegin.removeCallback();
hyprListener_holdEnd.removeCallback();
deviceName = mouse->getName();
}
bool CMouse::isVirtual() {
return false;
}
wlr_pointer* CMouse::wlr() {
return mouse;
SP<Aquamarine::IPointer> CMouse::aq() {
return mouse.lock();
}

View file

@ -4,33 +4,34 @@
class CMouse : public IPointer {
public:
static SP<CMouse> create(wlr_pointer* mouse);
static SP<CMouse> create(SP<Aquamarine::IPointer> mouse);
virtual bool isVirtual();
virtual wlr_pointer* wlr();
virtual bool isVirtual();
virtual SP<Aquamarine::IPointer> aq();
private:
CMouse(wlr_pointer* mouse);
CMouse(SP<Aquamarine::IPointer> mouse);
wlr_pointer* mouse = nullptr;
WP<Aquamarine::IPointer> mouse;
void disconnectCallbacks();
struct {
CHyprSignalListener destroy;
DYNLISTENER(destroy);
DYNLISTENER(motion);
DYNLISTENER(motionAbsolute);
DYNLISTENER(button);
DYNLISTENER(axis);
DYNLISTENER(frame);
CHyprSignalListener motion;
CHyprSignalListener motionAbsolute;
CHyprSignalListener button;
CHyprSignalListener axis;
CHyprSignalListener frame;
DYNLISTENER(swipeBegin);
DYNLISTENER(swipeEnd);
DYNLISTENER(swipeUpdate);
CHyprSignalListener swipeBegin;
CHyprSignalListener swipeEnd;
CHyprSignalListener swipeUpdate;
DYNLISTENER(pinchBegin);
DYNLISTENER(pinchEnd);
DYNLISTENER(pinchUpdate);
CHyprSignalListener pinchBegin;
CHyprSignalListener pinchEnd;
CHyprSignalListener pinchUpdate;
DYNLISTENER(holdBegin);
DYNLISTENER(holdEnd);
CHyprSignalListener holdBegin;
CHyprSignalListener holdEnd;
} listeners;
};

View file

@ -2,8 +2,9 @@
#include "../defines.hpp"
#include "../protocols/Tablet.hpp"
#include "../protocols/core/Compositor.hpp"
#include <aquamarine/input/Input.hpp>
SP<CTablet> CTablet::create(wlr_tablet* tablet) {
SP<CTablet> CTablet::create(SP<Aquamarine::ITablet> tablet) {
SP<CTablet> pTab = SP<CTablet>(new CTablet(tablet));
pTab->self = pTab;
@ -13,7 +14,7 @@ SP<CTablet> CTablet::create(wlr_tablet* tablet) {
return pTab;
}
SP<CTabletTool> CTabletTool::create(wlr_tablet_tool* tablet) {
SP<CTabletTool> CTabletTool::create(SP<Aquamarine::ITabletTool> tablet) {
SP<CTabletTool> pTab = SP<CTabletTool>(new CTabletTool(tablet));
pTab->self = pTab;
@ -23,7 +24,7 @@ SP<CTabletTool> CTabletTool::create(wlr_tablet_tool* tablet) {
return pTab;
}
SP<CTabletPad> CTabletPad::create(wlr_tablet_pad* tablet) {
SP<CTabletPad> CTabletPad::create(SP<Aquamarine::ITabletPad> tablet) {
SP<CTabletPad> pTab = SP<CTabletPad>(new CTabletPad(tablet));
pTab->self = pTab;
@ -33,33 +34,25 @@ SP<CTabletPad> CTabletPad::create(wlr_tablet_pad* tablet) {
return pTab;
}
SP<CTabletTool> CTabletTool::fromWlr(wlr_tablet_tool* tool) {
return ((CTabletTool*)tool->data)->self.lock();
}
SP<CTablet> CTablet::fromWlr(wlr_tablet* tablet) {
return ((CTablet*)tablet->data)->self.lock();
}
static uint32_t wlrUpdateToHl(uint32_t wlr) {
static uint32_t aqUpdateToHl(uint32_t aq) {
uint32_t result = 0;
if (wlr & WLR_TABLET_TOOL_AXIS_X)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_X)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X;
if (wlr & WLR_TABLET_TOOL_AXIS_Y)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_Y)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y;
if (wlr & WLR_TABLET_TOOL_AXIS_DISTANCE)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_DISTANCE)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_DISTANCE;
if (wlr & WLR_TABLET_TOOL_AXIS_PRESSURE)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_PRESSURE)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_PRESSURE;
if (wlr & WLR_TABLET_TOOL_AXIS_TILT_X)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_TILT_X)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X;
if (wlr & WLR_TABLET_TOOL_AXIS_TILT_Y)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_TILT_Y)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y;
if (wlr & WLR_TABLET_TOOL_AXIS_ROTATION)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_ROTATION)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_ROTATION;
if (wlr & WLR_TABLET_TOOL_AXIS_SLIDER)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_SLIDER)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_SLIDER;
if (wlr & WLR_TABLET_TOOL_AXIS_WHEEL)
if (aq & Aquamarine::AQ_TABLET_TOOL_AXIS_WHEEL)
result |= CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_WHEEL;
return result;
}
@ -68,97 +61,81 @@ uint32_t CTablet::getCapabilities() {
return HID_INPUT_CAPABILITY_POINTER | HID_INPUT_CAPABILITY_TABLET;
}
wlr_tablet* CTablet::wlr() {
return tablet;
SP<Aquamarine::ITablet> CTablet::aq() {
return tablet.lock();
}
CTablet::CTablet(wlr_tablet* tablet_) : tablet(tablet_) {
CTablet::CTablet(SP<Aquamarine::ITablet> tablet_) : tablet(tablet_) {
if (!tablet)
return;
tablet->data = this;
// clang-format off
hyprListener_destroy.initCallback(&tablet->base.events.destroy, [this] (void* owner, void* data) {
tablet = nullptr;
disconnectCallbacks();
listeners.destroy = tablet->events.destroy.registerListener([this](std::any d) {
tablet.reset();
events.destroy.emit();
}, this, "CTablet");
});
hyprListener_axis.initCallback(&tablet->events.axis, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_axis_event*)data;
listeners.axis = tablet->events.axis.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITablet::SAxisEvent>(d);
tabletEvents.axis.emit(SAxisEvent{
.tool = E->tool,
.tool = E.tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.updatedAxes = wlrUpdateToHl(E->updated_axes),
.axis = {E->x, E->y},
.axisDelta = {E->dx, E->dy},
.tilt = {E->tilt_x, E->tilt_y},
.pressure = E->pressure,
.distance = E->distance,
.rotation = E->rotation,
.slider = E->slider,
.wheelDelta = E->wheel_delta,
.timeMs = E.timeMs,
.updatedAxes = aqUpdateToHl(E.updatedAxes),
.axis = E.absolute,
.axisDelta = E.delta,
.tilt = E.tilt,
.pressure = E.pressure,
.distance = E.distance,
.rotation = E.rotation,
.slider = E.slider,
.wheelDelta = E.wheelDelta,
});
}, this, "CTablet");
});
hyprListener_proximity.initCallback(&tablet->events.proximity, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_proximity_event*)data;
listeners.proximity = tablet->events.proximity.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITablet::SProximityEvent>(d);
tabletEvents.proximity.emit(SProximityEvent{
.tool = E->tool,
.tool = E.tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.proximity = {E->x, E->y},
.in = E->state == WLR_TABLET_TOOL_PROXIMITY_IN,
.timeMs = E.timeMs,
.proximity = E.absolute,
.in = E.in,
});
}, this, "CTablet");
});
hyprListener_tip.initCallback(&tablet->events.tip, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_tip_event*)data;
listeners.tip = tablet->events.tip.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITablet::STipEvent>(d);
tabletEvents.tip.emit(STipEvent{
.tool = E->tool,
.tool = E.tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.tip = {E->x, E->y},
.in = E->state == WLR_TABLET_TOOL_TIP_DOWN,
.timeMs = E.timeMs,
.tip = E.absolute,
.in = E.down,
});
}, this, "CTablet");
});
hyprListener_button.initCallback(&tablet->events.button, [this] (void* owner, void* data) {
auto E = (wlr_tablet_tool_button_event*)data;
listeners.button = tablet->events.button.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITablet::SButtonEvent>(d);
tabletEvents.button.emit(SButtonEvent{
.tool = E->tool,
.tool = E.tool,
.tablet = self.lock(),
.timeMs = E->time_msec,
.button = E->button,
.down = E->state == WLR_BUTTON_PRESSED,
.timeMs = E.timeMs,
.button = E.button,
.down = E.down,
});
}, this, "CTablet");
// clang-format on
});
deviceName = tablet->base.name ? tablet->base.name : "UNKNOWN";
deviceName = tablet->getName();
}
CTablet::~CTablet() {
if (tablet)
tablet->data = nullptr;
PROTO::tablet->recheckRegisteredDevices();
}
void CTablet::disconnectCallbacks() {
hyprListener_axis.removeCallback();
hyprListener_button.removeCallback();
hyprListener_destroy.removeCallback();
hyprListener_proximity.removeCallback();
hyprListener_tip.removeCallback();
}
eHIDType CTablet::getType() {
return HID_TYPE_TABLET;
}
@ -167,138 +144,111 @@ uint32_t CTabletPad::getCapabilities() {
return HID_INPUT_CAPABILITY_TABLET;
}
wlr_tablet_pad* CTabletPad::wlr() {
return pad;
SP<Aquamarine::ITabletPad> CTabletPad::aq() {
return pad.lock();
}
eHIDType CTabletPad::getType() {
return HID_TYPE_TABLET_PAD;
}
CTabletPad::CTabletPad(wlr_tablet_pad* pad_) : pad(pad_) {
CTabletPad::CTabletPad(SP<Aquamarine::ITabletPad> pad_) : pad(pad_) {
if (!pad)
return;
// clang-format off
hyprListener_destroy.initCallback(&pad->base.events.destroy, [this] (void* owner, void* data) {
pad = nullptr;
disconnectCallbacks();
listeners.destroy = pad->events.destroy.registerListener([this](std::any d) {
pad.reset();
events.destroy.emit();
}, this, "CTabletPad");
});
hyprListener_button.initCallback(&pad->events.button, [this] (void* owner, void* data) {
auto E = (wlr_tablet_pad_button_event*)data;
listeners.button = pad->events.button.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITabletPad::SButtonEvent>(d);
padEvents.button.emit(SButtonEvent{
.timeMs = E->time_msec,
.button = E->button,
.down = E->state == WLR_BUTTON_PRESSED,
.mode = E->mode,
.group = E->group,
.timeMs = E.timeMs,
.button = E.button,
.down = E.down,
.mode = E.mode,
.group = E.group,
});
}, this, "CTabletPad");
});
hyprListener_ring.initCallback(&pad->events.ring, [this] (void* owner, void* data) {
auto E = (wlr_tablet_pad_ring_event*)data;
listeners.ring = pad->events.ring.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITabletPad::SRingEvent>(d);
padEvents.ring.emit(SRingEvent{
.timeMs = E->time_msec,
.finger = E->source == WLR_TABLET_PAD_RING_SOURCE_FINGER,
.ring = E->ring,
.position = E->position,
.mode = E->mode,
.timeMs = E.timeMs,
.finger = E.source == Aquamarine::ITabletPad::AQ_TABLET_PAD_RING_SOURCE_FINGER,
.ring = E.ring,
.position = E.pos,
.mode = E.mode,
});
}, this, "CTabletPad");
});
hyprListener_strip.initCallback(&pad->events.strip, [this] (void* owner, void* data) {
auto E = (wlr_tablet_pad_strip_event*)data;
listeners.strip = pad->events.strip.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITabletPad::SStripEvent>(d);
padEvents.strip.emit(SStripEvent{
.timeMs = E->time_msec,
.finger = E->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER,
.strip = E->strip,
.position = E->position,
.mode = E->mode,
.timeMs = E.timeMs,
.finger = E.source == Aquamarine::ITabletPad::AQ_TABLET_PAD_STRIP_SOURCE_FINGER,
.strip = E.strip,
.position = E.pos,
.mode = E.mode,
});
}, this, "CTabletPad");
});
hyprListener_attach.initCallback(&pad->events.attach_tablet, [this] (void* owner, void* data) {
if (!data)
return;
padEvents.attach.emit(CTabletTool::fromWlr((wlr_tablet_tool*)data));
}, this, "CTabletPad");
// clang-format on
listeners.attach = pad->events.attach.registerListener([this](std::any d) {
; // TODO: this doesn't do anything in aq atm
});
deviceName = pad->base.name ? pad->base.name : "UNKNOWN";
deviceName = pad->getName();
}
CTabletPad::~CTabletPad() {
PROTO::tablet->recheckRegisteredDevices();
}
void CTabletPad::disconnectCallbacks() {
hyprListener_ring.removeCallback();
hyprListener_button.removeCallback();
hyprListener_destroy.removeCallback();
hyprListener_strip.removeCallback();
hyprListener_attach.removeCallback();
}
uint32_t CTabletTool::getCapabilities() {
return HID_INPUT_CAPABILITY_POINTER | HID_INPUT_CAPABILITY_TABLET;
}
wlr_tablet_tool* CTabletTool::wlr() {
return tool;
SP<Aquamarine::ITabletTool> CTabletTool::aq() {
return tool.lock();
}
eHIDType CTabletTool::getType() {
return HID_TYPE_TABLET_TOOL;
}
CTabletTool::CTabletTool(wlr_tablet_tool* tool_) : tool(tool_) {
CTabletTool::CTabletTool(SP<Aquamarine::ITabletTool> tool_) : tool(tool_) {
if (!tool)
return;
// clang-format off
hyprListener_destroy.initCallback(&tool->events.destroy, [this] (void* owner, void* data) {
tool = nullptr;
disconnectCallbacks();
listeners.destroyTool = tool->events.destroy.registerListener([this](std::any d) {
tool.reset();
events.destroy.emit();
}, this, "CTabletTool");
// clang-format on
});
if (tool->tilt)
if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_TILT)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_TILT;
if (tool->pressure)
if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_PRESSURE)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_PRESSURE;
if (tool->distance)
if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_DISTANCE)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_DISTANCE;
if (tool->rotation)
if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_ROTATION)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_ROTATION;
if (tool->slider)
if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_SLIDER)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_SLIDER;
if (tool->wheel)
if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_WHEEL)
toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_WHEEL;
tool->data = this;
deviceName = std::to_string(tool->hardware_serial) + std::to_string(tool->hardware_wacom);
deviceName = std::format("{:x}-{:x}", tool->serial, tool->id);
}
CTabletTool::~CTabletTool() {
if (tool)
tool->data = nullptr;
PROTO::tablet->recheckRegisteredDevices();
}
void CTabletTool::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
listeners.destroySurface.reset();
}
SP<CWLSurfaceResource> CTabletTool::getSurface() {
return pSurface.lock();
}

View file

@ -6,9 +6,9 @@
#include "../helpers/math/Math.hpp"
#include "../helpers/math/Math.hpp"
struct wlr_tablet;
struct wlr_tablet_tool;
struct wlr_tablet_pad;
AQUAMARINE_FORWARD(ITablet);
AQUAMARINE_FORWARD(ITabletTool);
AQUAMARINE_FORWARD(ITabletPad);
class CTabletTool;
class CTabletPad;
@ -21,13 +21,12 @@ class CWLSurfaceResource;
*/
class CTablet : public IHID {
public:
static SP<CTablet> create(wlr_tablet* tablet);
static SP<CTablet> fromWlr(wlr_tablet* tablet);
static SP<CTablet> create(SP<Aquamarine::ITablet> tablet);
~CTablet();
virtual uint32_t getCapabilities();
virtual eHIDType getType();
wlr_tablet* wlr();
virtual uint32_t getCapabilities();
virtual eHIDType getType();
SP<Aquamarine::ITablet> aq();
enum eTabletToolAxes {
HID_TABLET_TOOL_AXIS_X = (1 << 0),
@ -42,46 +41,46 @@ class CTablet : public IHID {
};
struct SAxisEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
uint32_t updatedAxes = 0; // eTabletToolAxes
Vector2D axis;
Vector2D axisDelta;
Vector2D tilt;
double pressure = 0;
double distance = 0;
double rotation = 0;
double slider = 0;
double wheelDelta = 0;
uint32_t timeMs = 0;
uint32_t updatedAxes = 0; // eTabletToolAxes
Vector2D axis;
Vector2D axisDelta;
Vector2D tilt;
double pressure = 0;
double distance = 0;
double rotation = 0;
double slider = 0;
double wheelDelta = 0;
};
struct SProximityEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
Vector2D proximity;
bool in = false;
uint32_t timeMs = 0;
Vector2D proximity;
bool in = false;
};
struct STipEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
Vector2D tip;
bool in = false;
uint32_t timeMs = 0;
Vector2D tip;
bool in = false;
};
struct SButtonEvent {
wlr_tablet_tool* tool;
SP<CTablet> tablet;
SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet;
uint32_t timeMs = 0;
uint32_t button;
bool down = false;
uint32_t timeMs = 0;
uint32_t button;
bool down = false;
};
struct {
@ -100,27 +99,27 @@ class CTablet : public IHID {
CBox boundBox; // output-local
private:
CTablet(wlr_tablet* tablet);
CTablet(SP<Aquamarine::ITablet> tablet);
void disconnectCallbacks();
WP<Aquamarine::ITablet> tablet;
wlr_tablet* tablet = nullptr;
DYNLISTENER(destroy);
DYNLISTENER(axis);
DYNLISTENER(proximity);
DYNLISTENER(tip);
DYNLISTENER(button);
struct {
CHyprSignalListener destroy;
CHyprSignalListener axis;
CHyprSignalListener proximity;
CHyprSignalListener tip;
CHyprSignalListener button;
} listeners;
};
class CTabletPad : public IHID {
public:
static SP<CTabletPad> create(wlr_tablet_pad* pad);
static SP<CTabletPad> create(SP<Aquamarine::ITabletPad> pad);
~CTabletPad();
virtual uint32_t getCapabilities();
virtual eHIDType getType();
wlr_tablet_pad* wlr();
virtual uint32_t getCapabilities();
virtual eHIDType getType();
SP<Aquamarine::ITabletPad> aq();
struct SButtonEvent {
uint32_t timeMs = 0;
@ -159,23 +158,22 @@ class CTabletPad : public IHID {
std::string hlName;
private:
CTabletPad(wlr_tablet_pad* pad);
CTabletPad(SP<Aquamarine::ITabletPad> pad);
void disconnectCallbacks();
WP<Aquamarine::ITabletPad> pad;
wlr_tablet_pad* pad = nullptr;
DYNLISTENER(destroy);
DYNLISTENER(ring);
DYNLISTENER(strip);
DYNLISTENER(button);
DYNLISTENER(attach);
struct {
CHyprSignalListener destroy;
CHyprSignalListener ring;
CHyprSignalListener strip;
CHyprSignalListener button;
CHyprSignalListener attach;
} listeners;
};
class CTabletTool : public IHID {
public:
static SP<CTabletTool> create(wlr_tablet_tool* tool);
static SP<CTabletTool> fromWlr(wlr_tablet_tool* tool);
static SP<CTabletTool> create(SP<Aquamarine::ITabletTool> tool);
~CTabletTool();
enum eTabletToolType {
@ -198,35 +196,31 @@ class CTabletTool : public IHID {
HID_TABLET_TOOL_CAPABILITY_WHEEL = (1 << 5),
};
virtual uint32_t getCapabilities();
wlr_tablet_tool* wlr();
virtual eHIDType getType();
SP<CWLSurfaceResource> getSurface();
void setSurface(SP<CWLSurfaceResource>);
virtual uint32_t getCapabilities();
SP<Aquamarine::ITabletTool> aq();
virtual eHIDType getType();
SP<CWLSurfaceResource> getSurface();
void setSurface(SP<CWLSurfaceResource>);
WP<CTabletTool> self;
Vector2D tilt;
bool active = false; // true if in proximity
uint32_t toolCapabilities = 0;
WP<CTabletTool> self;
Vector2D tilt;
bool active = false; // true if in proximity
uint32_t toolCapabilities = 0;
bool isDown = false;
std::vector<uint32_t> buttonsDown;
Vector2D absolutePos; // last known absolute position.
bool isDown = false;
std::vector<uint32_t> buttonsDown;
Vector2D absolutePos; // last known absolute position.
std::string hlName;
std::string hlName;
private:
CTabletTool(wlr_tablet_tool* tool);
CTabletTool(SP<Aquamarine::ITabletTool> tool);
void disconnectCallbacks();
WP<CWLSurfaceResource> pSurface;
wlr_tablet_tool* tool = nullptr;
DYNLISTENER(destroy);
WP<CWLSurfaceResource> pSurface;
WP<Aquamarine::ITabletTool> tool;
struct {
CHyprSignalListener destroySurface;
CHyprSignalListener destroyTool;
} listeners;
};

View file

@ -1,7 +1,8 @@
#include "TouchDevice.hpp"
#include "../defines.hpp"
#include <aquamarine/input/Input.hpp>
SP<CTouchDevice> CTouchDevice::create(wlr_touch* touch) {
SP<CTouchDevice> CTouchDevice::create(SP<Aquamarine::ITouch> touch) {
SP<CTouchDevice> pTouch = SP<CTouchDevice>(new CTouchDevice(touch));
pTouch->self = pTouch;
@ -9,78 +10,63 @@ SP<CTouchDevice> CTouchDevice::create(wlr_touch* touch) {
return pTouch;
}
CTouchDevice::CTouchDevice(wlr_touch* touch_) : touch(touch_) {
CTouchDevice::CTouchDevice(SP<Aquamarine::ITouch> touch_) : touch(touch_) {
if (!touch)
return;
// clang-format off
hyprListener_destroy.initCallback(&touch->base.events.destroy, [this] (void* owner, void* data) {
listeners.destroy = touch->events.destroy.registerListener([this](std::any d) {
events.destroy.emit();
disconnectCallbacks();
touch = nullptr;
}, this, "CTouchDevice");
touch.reset();
});
hyprListener_down.initCallback(&touch->events.down, [this] (void* owner, void* data) {
auto E = (wlr_touch_down_event*)data;
listeners.down = touch->events.down.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITouch::SDownEvent>(d);
touchEvents.down.emit(SDownEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id,
.pos = {E->x, E->y},
.timeMs = E.timeMs,
.touchID = E.touchID,
.pos = E.pos,
.device = self.lock(),
});
}, this, "CTouchDevice");
});
hyprListener_up.initCallback(&touch->events.up, [this] (void* owner, void* data) {
auto E = (wlr_touch_up_event*)data;
listeners.up = touch->events.up.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITouch::SUpEvent>(d);
touchEvents.up.emit(SUpEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id
.timeMs = E.timeMs,
.touchID = E.touchID,
});
}, this, "CTouchDevice");
});
hyprListener_motion.initCallback(&touch->events.motion, [this] (void* owner, void* data) {
auto E = (wlr_touch_motion_event*)data;
listeners.motion = touch->events.move.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITouch::SMotionEvent>(d);
touchEvents.motion.emit(SMotionEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id,
.pos = {E->x, E->y},
.timeMs = E.timeMs,
.touchID = E.touchID,
.pos = E.pos,
});
}, this, "CTouchDevice");
});
hyprListener_cancel.initCallback(&touch->events.cancel, [this] (void* owner, void* data) {
auto E = (wlr_touch_cancel_event*)data;
listeners.cancel = touch->events.cancel.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::ITouch::SCancelEvent>(d);
touchEvents.cancel.emit(SCancelEvent{
.timeMs = E->time_msec,
.touchID = E->touch_id
.timeMs = E.timeMs,
.touchID = E.touchID,
});
}, this, "CTouchDevice");
});
hyprListener_frame.initCallback(&touch->events.frame, [this] (void* owner, void* data) {
touchEvents.frame.emit();
}, this, "CTouchDevice");
listeners.frame = touch->events.frame.registerListener([this](std::any d) { touchEvents.frame.emit(); });
// clang-format on
deviceName = touch->base.name ? touch->base.name : "UNKNOWN";
deviceName = touch->getName();
}
bool CTouchDevice::isVirtual() {
return false;
}
wlr_touch* CTouchDevice::wlr() {
return touch;
}
void CTouchDevice::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_down.removeCallback();
hyprListener_up.removeCallback();
hyprListener_motion.removeCallback();
hyprListener_cancel.removeCallback();
hyprListener_frame.removeCallback();
SP<Aquamarine::ITouch> CTouchDevice::aq() {
return touch.lock();
}

View file

@ -4,22 +4,22 @@
class CTouchDevice : public ITouch {
public:
static SP<CTouchDevice> create(wlr_touch* touch);
static SP<CTouchDevice> create(SP<Aquamarine::ITouch> touch);
virtual bool isVirtual();
virtual wlr_touch* wlr();
virtual bool isVirtual();
virtual SP<Aquamarine::ITouch> aq();
private:
CTouchDevice(wlr_touch* touch);
CTouchDevice(SP<Aquamarine::ITouch> touch);
wlr_touch* touch = nullptr;
WP<Aquamarine::ITouch> touch;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(down);
DYNLISTENER(up);
DYNLISTENER(motion);
DYNLISTENER(cancel);
DYNLISTENER(frame);
struct {
CHyprSignalListener destroy;
CHyprSignalListener down;
CHyprSignalListener up;
CHyprSignalListener motion;
CHyprSignalListener cancel;
CHyprSignalListener frame;
} listeners;
};

View file

@ -14,58 +14,37 @@ CVirtualKeyboard::CVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keeb_) : keybo
if (!keeb_)
return;
auto keeb = keeb_->wlr();
// clang-format off
hyprListener_destroy.initCallback(&keeb->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
listeners.destroy = keeb_->events.destroy.registerListener([this](std::any d) {
keyboard.reset();
events.destroy.emit();
}, this, "CVirtualKeyboard");
events.destroy.emit();
});
hyprListener_key.initCallback(&keeb->events.key, [this] (void* owner, void* data) {
auto E = (wlr_keyboard_key_event*)data;
keyboardEvents.key.emit(SKeyEvent{
.timeMs = E->time_msec,
.keycode = E->keycode,
.updateMods = E->update_state,
.state = E->state,
listeners.key = keeb_->events.key.registerListener([this](std::any d) { keyboardEvents.key.emit(d); });
listeners.modifiers = keeb_->events.modifiers.registerListener([this](std::any d) {
auto E = std::any_cast<SModifiersEvent>(d);
updateModifiers(E.depressed, E.latched, E.locked, E.group);
keyboardEvents.modifiers.emit(SModifiersEvent{
.depressed = modifiersState.depressed,
.latched = modifiersState.latched,
.locked = modifiersState.locked,
.group = modifiersState.group,
});
}, this, "CVirtualKeyboard");
});
listeners.keymap = keeb_->events.keymap.registerListener([this](std::any d) {
auto E = std::any_cast<SKeymapEvent>(d);
xkbKeymap = xkb_keymap_ref(E.keymap);
keyboardEvents.keymap.emit(d);
});
hyprListener_keymap.initCallback(&keeb->events.keymap, [this] (void* owner, void* data) {
keyboardEvents.keymap.emit();
}, this, "CVirtualKeyboard");
hyprListener_modifiers.initCallback(&keeb->events.modifiers, [this] (void* owner, void* data) {
keyboardEvents.modifiers.emit();
}, this, "CVirtualKeyboard");
hyprListener_repeatInfo.initCallback(&keeb->events.repeat_info, [this] (void* owner, void* data) {
keyboardEvents.repeatInfo.emit();
}, this, "CVirtualKeyboard");
// clang-format on
deviceName = keeb->base.name ? keeb->base.name : "UNKNOWN";
deviceName = keeb_->name;
}
bool CVirtualKeyboard::isVirtual() {
return true;
}
wlr_keyboard* CVirtualKeyboard::wlr() {
if (keyboard.expired())
return nullptr;
return keyboard->wlr();
}
void CVirtualKeyboard::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_key.removeCallback();
hyprListener_keymap.removeCallback();
hyprListener_repeatInfo.removeCallback();
hyprListener_modifiers.removeCallback();
SP<Aquamarine::IKeyboard> CVirtualKeyboard::aq() {
return nullptr;
}
wl_client* CVirtualKeyboard::getClient() {

View file

@ -6,23 +6,22 @@ class CVirtualKeyboardV1Resource;
class CVirtualKeyboard : public IKeyboard {
public:
static SP<CVirtualKeyboard> create(SP<CVirtualKeyboardV1Resource> keeb);
static SP<CVirtualKeyboard> create(SP<CVirtualKeyboardV1Resource> keeb);
virtual bool isVirtual();
virtual wlr_keyboard* wlr();
virtual bool isVirtual();
virtual SP<Aquamarine::IKeyboard> aq();
wl_client* getClient();
wl_client* getClient();
private:
CVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keeb);
WP<CVirtualKeyboardV1Resource> keyboard;
void disconnectCallbacks();
DYNLISTENER(destroy);
DYNLISTENER(key);
DYNLISTENER(modifiers);
DYNLISTENER(keymap);
DYNLISTENER(repeatInfo);
struct {
CHyprSignalListener destroy;
CHyprSignalListener key;
CHyprSignalListener modifiers;
CHyprSignalListener keymap;
} listeners;
};

View file

@ -1,5 +1,6 @@
#include "VirtualPointer.hpp"
#include "../protocols/VirtualPointer.hpp"
#include <aquamarine/input/Input.hpp>
SP<CVirtualPointer> CVirtualPointer::create(SP<CVirtualPointerV1Resource> resource) {
SP<CVirtualPointer> pPointer = SP<CVirtualPointer>(new CVirtualPointer(resource));
@ -13,165 +14,32 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
if (!resource->good())
return;
auto mouse = resource->wlr();
// clang-format off
hyprListener_destroy.initCallback(&mouse->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
listeners.destroy = pointer->events.destroy.registerListener([this](std::any d) {
pointer.reset();
events.destroy.emit();
}, this, "CVirtualPointer");
});
hyprListener_motion.initCallback(&mouse->events.motion, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_event*)data;
listeners.motion = pointer->events.move.registerListener([this](std::any d) { pointerEvents.motion.emit(d); });
listeners.motionAbsolute = pointer->events.warp.registerListener([this](std::any d) { pointerEvents.motionAbsolute.emit(d); });
listeners.button = pointer->events.button.registerListener([this](std::any d) { pointerEvents.button.emit(d); });
listeners.axis = pointer->events.axis.registerListener([this](std::any d) { pointerEvents.axis.emit(d); });
listeners.frame = pointer->events.frame.registerListener([this](std::any d) { pointerEvents.frame.emit(); });
listeners.swipeBegin = pointer->events.swipeBegin.registerListener([this](std::any d) { pointerEvents.swipeBegin.emit(d); });
listeners.swipeEnd = pointer->events.swipeEnd.registerListener([this](std::any d) { pointerEvents.swipeEnd.emit(d); });
listeners.swipeUpdate = pointer->events.swipeUpdate.registerListener([this](std::any d) { pointerEvents.swipeUpdate.emit(d); });
listeners.pinchBegin = pointer->events.pinchBegin.registerListener([this](std::any d) { pointerEvents.pinchBegin.emit(d); });
listeners.pinchEnd = pointer->events.pinchEnd.registerListener([this](std::any d) { pointerEvents.pinchEnd.emit(d); });
listeners.pinchUpdate = pointer->events.pinchUpdate.registerListener([this](std::any d) { pointerEvents.pinchUpdate.emit(d); });
listeners.holdBegin = pointer->events.holdBegin.registerListener([this](std::any d) { pointerEvents.holdBegin.emit(d); });
listeners.holdEnd = pointer->events.holdEnd.registerListener([this](std::any d) { pointerEvents.holdEnd.emit(d); });
pointerEvents.motion.emit(SMotionEvent{
.timeMs = E->time_msec,
.delta = {E->delta_x, E->delta_y},
.unaccel = {E->unaccel_dx, E->unaccel_dy},
});
}, this, "CVirtualPointer");
hyprListener_motionAbsolute.initCallback(&mouse->events.motion_absolute, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_absolute_event*)data;
pointerEvents.motionAbsolute.emit(SMotionAbsoluteEvent{
.timeMs = E->time_msec,
.absolute = {E->x, E->y},
.device = self.lock(),
});
}, this, "CVirtualPointer");
hyprListener_button.initCallback(&mouse->events.button, [this] (void* owner, void* data) {
auto E = (wlr_pointer_button_event*)data;
pointerEvents.button.emit(SButtonEvent{
.timeMs = E->time_msec,
.button = E->button,
.state = (wl_pointer_button_state)E->state,
});
}, this, "CVirtualPointer");
hyprListener_axis.initCallback(&mouse->events.axis, [this] (void* owner, void* data) {
auto E = (wlr_pointer_axis_event*)data;
pointerEvents.axis.emit(SAxisEvent{
.timeMs = E->time_msec,
.source = E->source,
.axis = E->orientation,
.relativeDirection = E->relative_direction,
.delta = E->delta,
.deltaDiscrete = E->delta_discrete,
});
}, this, "CVirtualPointer");
hyprListener_frame.initCallback(&mouse->events.frame, [this] (void* owner, void* data) {
pointerEvents.frame.emit();
}, this, "CVirtualPointer");
hyprListener_swipeBegin.initCallback(&mouse->events.swipe_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_begin_event*)data;
pointerEvents.swipeBegin.emit(SSwipeBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CVirtualPointer");
hyprListener_swipeEnd.initCallback(&mouse->events.swipe_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_end_event*)data;
pointerEvents.swipeEnd.emit(SSwipeEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CVirtualPointer");
hyprListener_swipeUpdate.initCallback(&mouse->events.swipe_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_swipe_update_event*)data;
pointerEvents.swipeUpdate.emit(SSwipeUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
});
}, this, "CVirtualPointer");
hyprListener_pinchBegin.initCallback(&mouse->events.pinch_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_begin_event*)data;
pointerEvents.pinchBegin.emit(SPinchBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CVirtualPointer");
hyprListener_pinchEnd.initCallback(&mouse->events.pinch_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_end_event*)data;
pointerEvents.pinchEnd.emit(SPinchEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CVirtualPointer");
hyprListener_pinchUpdate.initCallback(&mouse->events.pinch_update, [this] (void* owner, void* data) {
auto E = (wlr_pointer_pinch_update_event*)data;
pointerEvents.pinchUpdate.emit(SPinchUpdateEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
.delta = {E->dx, E->dy},
.scale = E->scale,
.rotation = E->rotation,
});
}, this, "CVirtualPointer");
hyprListener_holdBegin.initCallback(&mouse->events.hold_begin, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_begin_event*)data;
pointerEvents.holdBegin.emit(SHoldBeginEvent{
.timeMs = E->time_msec,
.fingers = E->fingers,
});
}, this, "CVirtualPointer");
hyprListener_holdEnd.initCallback(&mouse->events.hold_end, [this] (void* owner, void* data) {
auto E = (wlr_pointer_hold_end_event*)data;
pointerEvents.holdEnd.emit(SHoldEndEvent{
.timeMs = E->time_msec,
.cancelled = E->cancelled,
});
}, this, "CVirtualPointer");
// clang-format on
deviceName = mouse->base.name ? mouse->base.name : "UNKNOWN";
deviceName = pointer->name;
}
bool CVirtualPointer::isVirtual() {
return true;
}
void CVirtualPointer::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_motion.removeCallback();
hyprListener_motionAbsolute.removeCallback();
hyprListener_button.removeCallback();
hyprListener_axis.removeCallback();
hyprListener_frame.removeCallback();
hyprListener_swipeBegin.removeCallback();
hyprListener_swipeEnd.removeCallback();
hyprListener_swipeUpdate.removeCallback();
hyprListener_pinchBegin.removeCallback();
hyprListener_pinchEnd.removeCallback();
hyprListener_pinchUpdate.removeCallback();
hyprListener_holdBegin.removeCallback();
hyprListener_holdEnd.removeCallback();
}
wlr_pointer* CVirtualPointer::wlr() {
if (pointer.expired())
return nullptr;
return pointer->wlr();
SP<Aquamarine::IPointer> CVirtualPointer::aq() {
return nullptr;
}

View file

@ -6,33 +6,34 @@ class CVirtualPointerV1Resource;
class CVirtualPointer : public IPointer {
public:
static SP<CVirtualPointer> create(SP<CVirtualPointerV1Resource> resource);
static SP<CVirtualPointer> create(SP<CVirtualPointerV1Resource> resource);
virtual bool isVirtual();
virtual wlr_pointer* wlr();
virtual bool isVirtual();
virtual SP<Aquamarine::IPointer> aq();
private:
CVirtualPointer(SP<CVirtualPointerV1Resource>);
WP<CVirtualPointerV1Resource> pointer;
void disconnectCallbacks();
struct {
CHyprSignalListener destroy;
DYNLISTENER(destroy);
DYNLISTENER(motion);
DYNLISTENER(motionAbsolute);
DYNLISTENER(button);
DYNLISTENER(axis);
DYNLISTENER(frame);
CHyprSignalListener motion;
CHyprSignalListener motionAbsolute;
CHyprSignalListener button;
CHyprSignalListener axis;
CHyprSignalListener frame;
DYNLISTENER(swipeBegin);
DYNLISTENER(swipeEnd);
DYNLISTENER(swipeUpdate);
CHyprSignalListener swipeBegin;
CHyprSignalListener swipeEnd;
CHyprSignalListener swipeUpdate;
DYNLISTENER(pinchBegin);
DYNLISTENER(pinchEnd);
DYNLISTENER(pinchUpdate);
CHyprSignalListener pinchBegin;
CHyprSignalListener pinchEnd;
CHyprSignalListener pinchUpdate;
DYNLISTENER(holdBegin);
DYNLISTENER(holdEnd);
CHyprSignalListener holdBegin;
CHyprSignalListener holdEnd;
} listeners;
};

View file

@ -1,50 +0,0 @@
#include "Events.hpp"
#include "../Compositor.hpp"
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../render/Renderer.hpp"
// ---------------------------------------------------- //
// _____ ________ _______ _____ ______ _____ //
// | __ \| ____\ \ / /_ _/ ____| ____|/ ____| //
// | | | | |__ \ \ / / | || | | |__ | (___ //
// | | | | __| \ \/ / | || | | __| \___ \ //
// | |__| | |____ \ / _| || |____| |____ ____) | //
// |_____/|______| \/ |_____\_____|______|_____/ //
// //
// ---------------------------------------------------- //
void Events::listener_newInput(wl_listener* listener, void* data) {
const auto DEVICE = (wlr_input_device*)data;
switch (DEVICE->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
Debug::log(LOG, "Attached a keyboard with name {}", DEVICE->name);
g_pInputManager->newKeyboard(DEVICE);
break;
case WLR_INPUT_DEVICE_POINTER:
Debug::log(LOG, "Attached a mouse with name {}", DEVICE->name);
g_pInputManager->newMouse(DEVICE);
break;
case WLR_INPUT_DEVICE_TOUCH:
Debug::log(LOG, "Attached a touch device with name {}", DEVICE->name);
g_pInputManager->newTouchDevice(DEVICE);
break;
case WLR_INPUT_DEVICE_TABLET:
Debug::log(LOG, "Attached a tablet with name {}", DEVICE->name);
g_pInputManager->newTablet(DEVICE);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
Debug::log(LOG, "Attached a tablet pad with name {}", DEVICE->name);
g_pInputManager->newTabletPad(DEVICE);
break;
case WLR_INPUT_DEVICE_SWITCH:
Debug::log(LOG, "Attached a switch device with name {}", DEVICE->name);
g_pInputManager->newSwitch(DEVICE);
break;
default: Debug::log(WARN, "Unrecognized input device plugged in: {}", DEVICE->name); break;
}
g_pInputManager->updateCapabilities();
}

View file

@ -8,16 +8,6 @@
//
namespace Events {
// Monitor events
LISTENER(change);
LISTENER(newOutput);
// DRM events
LISTENER(leaseRequest);
// Layer events
LISTENER(newLayerSurface);
// Window events
DYNLISTENFUNC(commitWindow);
DYNLISTENFUNC(mapWindow);
@ -35,15 +25,6 @@ namespace Events {
DYNLISTENFUNC(setOverrideRedirect);
DYNLISTENFUNC(ackConfigure);
LISTENER(newInput);
// Virt Ptr
LISTENER(newVirtPtr);
// Various
LISTENER(requestSetSel);
LISTENER(requestSetPrimarySel);
// Monitor part 2 the sequel
DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorDestroy);
@ -52,16 +33,4 @@ namespace Events {
DYNLISTENFUNC(monitorNeedsFrame);
DYNLISTENFUNC(monitorCommit);
DYNLISTENFUNC(monitorBind);
// XWayland
LISTENER(surfaceXWayland);
// Renderer destroy
LISTENER(RendererDestroy);
// session
LISTENER(sessionActive);
// Session Lock
LISTENER(newSessionLock);
};

View file

@ -1,54 +0,0 @@
#include "Events.hpp"
#include "../Compositor.hpp"
#include "../helpers/WLClasses.hpp"
#include "../managers/input/InputManager.hpp"
#include "../render/Renderer.hpp"
#include "../managers/CursorManager.hpp"
// ------------------------------ //
// __ __ _____ _____ _____ //
// | \/ |_ _|/ ____|/ ____| //
// | \ / | | | | (___ | | //
// | |\/| | | | \___ \| | //
// | | | |_| |_ ____) | |____ //
// |_| |_|_____|_____/ \_____| //
// //
// ------------------------------ //
void Events::listener_leaseRequest(wl_listener* listener, void* data) {
const auto REQUEST = (wlr_drm_lease_request_v1*)data;
struct wlr_drm_lease_v1* lease = wlr_drm_lease_request_v1_grant(REQUEST);
if (!lease) {
Debug::log(ERR, "Failed to grant lease request!");
wlr_drm_lease_request_v1_reject(REQUEST);
}
}
void Events::listener_RendererDestroy(wl_listener* listener, void* data) {
Debug::log(LOG, "!!Renderer destroyed!!");
}
void Events::listener_sessionActive(wl_listener* listener, void* data) {
if (g_pCompositor->m_sWLRSession->active) {
Debug::log(LOG, "Session got activated!");
g_pCompositor->m_bSessionActive = true;
for (auto& m : g_pCompositor->m_vMonitors) {
g_pCompositor->scheduleFrameForMonitor(m.get());
g_pHyprRenderer->applyMonitorRule(m.get(), &m->activeMonitorRule, true);
}
g_pConfigManager->m_bWantsMonitorReload = true;
} else {
Debug::log(LOG, "Session got inactivated!");
g_pCompositor->m_bSessionActive = false;
for (auto& m : g_pCompositor->m_vMonitors) {
m->noFrameSchedule = true;
m->framesToSkip = 1;
}
}
}

View file

@ -5,6 +5,7 @@
#include "Events.hpp"
#include "../debug/HyprCtl.hpp"
#include "../config/ConfigValue.hpp"
#include <aquamarine/output/Output.hpp>
// --------------------------------------------------------- //
// __ __ ____ _ _ _____ _______ ____ _____ _____ //
@ -16,99 +17,10 @@
// //
// --------------------------------------------------------- //
static void checkDefaultCursorWarp(SP<CMonitor> PNEWMONITOR, std::string monitorName) {
static auto PCURSORMONITOR = CConfigValue<std::string>("cursor:default_monitor");
static auto firstMonitorAdded = std::chrono::steady_clock::now();
static bool cursorDefaultDone = false;
static bool firstLaunch = true;
const auto POS = PNEWMONITOR->middle();
// by default, cursor should be set to first monitor detected
// this is needed as a default if the monitor given in config above doesn't exist
if (firstLaunch) {
firstLaunch = false;
g_pCompositor->warpCursorTo(POS, true);
g_pInputManager->refocus();
}
if (cursorDefaultDone || *PCURSORMONITOR == STRVAL_EMPTY)
return;
// after 10s, don't set cursor to default monitor
auto timePassedSec = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - firstMonitorAdded);
if (timePassedSec.count() > 10) {
cursorDefaultDone = true;
return;
}
if (*PCURSORMONITOR == monitorName) {
cursorDefaultDone = true;
g_pCompositor->warpCursorTo(POS, true);
g_pInputManager->refocus();
}
}
void Events::listener_newOutput(wl_listener* listener, void* data) {
// new monitor added, let's accommodate for that.
const auto OUTPUT = (wlr_output*)data;
if (!OUTPUT->name) {
Debug::log(ERR, "New monitor has no name?? Ignoring");
return;
}
// add it to real
auto PNEWMONITOR = g_pCompositor->m_vRealMonitors.emplace_back(makeShared<CMonitor>());
if (std::string("HEADLESS-1") == OUTPUT->name)
g_pCompositor->m_pUnsafeOutput = PNEWMONITOR.get();
PNEWMONITOR->output = OUTPUT;
PNEWMONITOR->self = PNEWMONITOR;
const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? OUTPUT == g_pCompositor->m_pUnsafeOutput->output : false;
PNEWMONITOR->ID = FALLBACK ? -1 : g_pCompositor->getNextAvailableMonitorID(OUTPUT->name);
PNEWMONITOR->isUnsafeFallback = FALLBACK;
EMIT_HOOK_EVENT("newMonitor", PNEWMONITOR);
if (!FALLBACK)
PNEWMONITOR->onConnect(false);
if (!PNEWMONITOR->m_bEnabled || FALLBACK)
return;
// ready to process if we have a real monitor
if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled)
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR.get();
g_pCompositor->m_bReadyToProcess = true;
g_pConfigManager->m_bWantsMonitorReload = true;
g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR.get());
checkDefaultCursorWarp(PNEWMONITOR, OUTPUT->name);
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1;
w->updateSurfaceScaleTransformDetails();
}
}
}
void Events::listener_monitorFrame(void* owner, void* data) {
if (g_pCompositor->m_bExitTriggered) {
// Only signal cleanup once
g_pCompositor->m_bExitTriggered = false;
g_pCompositor->cleanup();
return;
}
CMonitor* const PMONITOR = (CMonitor*)owner;
if ((g_pCompositor->m_sWLRSession && !g_pCompositor->m_sWLRSession->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
if ((g_pCompositor->m_pAqBackend->hasSession() && !g_pCompositor->m_pAqBackend->session->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Attempted to render frame on inactive session!");
if (g_pCompositor->m_bUnsafeState && std::ranges::any_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& m) {
@ -172,12 +84,10 @@ void Events::listener_monitorFrame(void* owner, void* data) {
}
void Events::listener_monitorDestroy(void* owner, void* data) {
const auto OUTPUT = (wlr_output*)data;
CMonitor* pMonitor = nullptr;
CMonitor* pMonitor = (CMonitor*)owner;
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->output == OUTPUT) {
if (m->output == pMonitor->output) {
pMonitor = m.get();
break;
}
@ -188,9 +98,6 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name);
if (pMonitor->output->idle_frame)
wl_event_source_remove(pMonitor->output->idle_frame);
pMonitor->onDisconnect(true);
pMonitor->output = nullptr;
@ -201,44 +108,18 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
std::erase_if(g_pCompositor->m_vRealMonitors, [&](SP<CMonitor>& el) { return el.get() == pMonitor; });
}
void Events::listener_monitorStateRequest(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_request_state*)data;
if (!PMONITOR->createdByUser)
return;
const auto SIZE = E->state->mode ? Vector2D{E->state->mode->width, E->state->mode->height} : Vector2D{E->state->custom_mode.width, E->state->custom_mode.height};
PMONITOR->forceSize = SIZE;
SMonitorRule rule = PMONITOR->activeMonitorRule;
rule.resolution = SIZE;
g_pHyprRenderer->applyMonitorRule(PMONITOR, &rule);
}
void Events::listener_monitorDamage(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_damage*)data;
PMONITOR->addDamage(E->damage);
}
void Events::listener_monitorNeedsFrame(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME);
}
void Events::listener_monitorCommit(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_commit*)data;
if (E->state->committed & WLR_OUTPUT_STATE_BUFFER) {
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR, E);
if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR);
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR);
}
}

View file

@ -0,0 +1,43 @@
#pragma once
#include <array>
// clang-format off
constexpr std::array<const char*, 35> CURSOR_SHAPE_NAMES = {
"invalid",
"default",
"context-menu",
"help",
"pointer",
"progress",
"wait",
"cell",
"crosshair",
"text",
"vertical-text",
"alias",
"copy",
"move",
"no-drop",
"not-allowed",
"grab",
"grabbing",
"e-resize",
"n-resize",
"ne-resize",
"nw-resize",
"s-resize",
"se-resize",
"sw-resize",
"w-resize",
"ew-resize",
"ns-resize",
"nesw-resize",
"nwse-resize",
"col-resize",
"row-resize",
"all-scroll",
"zoom-in",
"zoom-out",
};
// clang-format on

View file

@ -3,6 +3,8 @@
#include "../includes.hpp"
#include "debug/Log.hpp"
#include "../macros.hpp"
#include <xf86drm.h>
#include <drm_fourcc.h>
/*
DRM formats are LE, while OGL is BE. The two primary formats
@ -309,3 +311,17 @@ uint32_t FormatUtils::glFormatToType(uint32_t gl) {
#endif
GL_UNSIGNED_BYTE;
}
std::string FormatUtils::drmFormatName(DRMFormat drm) {
auto n = drmGetFormatName(drm);
std::string name = n;
free(n);
return name;
}
std::string FormatUtils::drmModifierName(uint64_t mod) {
auto n = drmGetFormatModifierName(mod);
std::string name = n;
free(n);
return name;
}

View file

@ -1,7 +1,9 @@
#pragma once
#include <cstdint>
#include <string>
#include "math/Math.hpp"
#include <aquamarine/backend/Misc.hpp>
typedef uint32_t DRMFormat;
typedef uint32_t SHMFormat;
@ -18,10 +20,7 @@ struct SPixelFormat {
Vector2D blockSize;
};
struct SDRMFormat {
uint32_t format = 0;
std::vector<uint64_t> mods;
};
typedef Aquamarine::SDRMFormat SDRMFormat;
namespace FormatUtils {
SHMFormat drmToShm(DRMFormat drm);
@ -34,4 +33,6 @@ namespace FormatUtils {
int minStride(const SPixelFormat* const fmt, int32_t width);
uint32_t drmFormatToGL(DRMFormat drm);
uint32_t glFormatToType(uint32_t gl);
std::string drmFormatName(DRMFormat drm);
std::string drmModifierName(uint64_t mod);
};

View file

@ -4,9 +4,12 @@
#include "../Compositor.hpp"
#include "../managers/TokenManager.hpp"
#include <optional>
#include <cstring>
#include <cmath>
#include <set>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iomanip>
#include <sstream>

View file

@ -3,7 +3,6 @@
#include <optional>
#include <string>
#include <wayland-server.h>
#include <wlr/util/box.h>
#include "math/Math.hpp"
#include <vector>
#include <format>

View file

@ -1,12 +1,18 @@
#include "Monitor.hpp"
#include "MiscFunctions.hpp"
#include "math/Math.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/GammaControl.hpp"
#include "../devices/ITouch.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/core/Output.hpp"
#include "../managers/PointerManager.hpp"
#include "../protocols/core/Compositor.hpp"
#include "sync/SyncTimeline.hpp"
#include <aquamarine/output/Output.hpp>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
@ -21,62 +27,73 @@ CMonitor::CMonitor() : state(this) {
}
CMonitor::~CMonitor() {
hyprListener_monitorDestroy.removeCallback();
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorStateRequest.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
events.destroy.emit();
}
static void onPresented(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
auto E = (wlr_output_event_present*)data;
PROTO::presentation->onPresented(PMONITOR, E->when, E->refresh, E->seq, E->flags);
}
void CMonitor::onConnect(bool noRule) {
hyprListener_monitorDestroy.removeCallback();
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorStateRequest.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
hyprListener_monitorPresented.removeCallback();
hyprListener_monitorFrame.initCallback(&output->events.frame, &Events::listener_monitorFrame, this, "CMonitor");
hyprListener_monitorDestroy.initCallback(&output->events.destroy, &Events::listener_monitorDestroy, this, "CMonitor");
hyprListener_monitorStateRequest.initCallback(&output->events.request_state, &Events::listener_monitorStateRequest, this, "CMonitor");
hyprListener_monitorDamage.initCallback(&output->events.damage, &Events::listener_monitorDamage, this, "CMonitor");
hyprListener_monitorNeedsFrame.initCallback(&output->events.needs_frame, &Events::listener_monitorNeedsFrame, this, "CMonitor");
hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this, "CMonitor");
hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this, "CMonitor");
hyprListener_monitorPresented.initCallback(&output->events.present, ::onPresented, this, "CMonitor");
tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm
if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
}
listeners.frame = output->events.frame.registerListener([this](std::any d) { Events::listener_monitorFrame(this, nullptr); });
listeners.destroy = output->events.destroy.registerListener([this](std::any d) { Events::listener_monitorDestroy(this, nullptr); });
listeners.commit = output->events.commit.registerListener([this](std::any d) { Events::listener_monitorCommit(this, nullptr); });
listeners.needsFrame =
output->events.needsFrame.registerListener([this](std::any d) { g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); });
listeners.presented = output->events.present.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IOutput::SPresentEvent>(d);
PROTO::presentation->onPresented(this, E.when, E.refresh, E.seq, E.flags);
});
listeners.state = output->events.state.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IOutput::SStateEvent>(d);
if (E.size == Vector2D{}) {
// an indication to re-set state
// we can't do much for createdByUser displays I think
if (createdByUser)
return;
Debug::log(LOG, "Reapplying monitor rule for {} from a state request", szName);
g_pHyprRenderer->applyMonitorRule(this, &activeMonitorRule, true);
return;
}
if (!createdByUser)
return;
const auto SIZE = E.size;
forceSize = SIZE;
SMonitorRule rule = activeMonitorRule;
rule.resolution = SIZE;
g_pHyprRenderer->applyMonitorRule(this, &rule);
});
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
if (m_bEnabled) {
wlr_output_state_set_enabled(state.wlr(), true);
output->state->setEnabled(true);
state.commit();
return;
}
szName = output->name;
szDescription = output->description ? output->description : "";
szDescription = output->description;
// remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description
std::erase(szDescription, ',');
// field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix
szShortDescription = trim(std::format("{} {} {}", output->make ? output->make : "", output->model ? output->model : "", output->serial ? output->serial : ""));
szShortDescription = trim(std::format("{} {} {}", output->make, output->model, output->serial));
std::erase(szShortDescription, ',');
if (!wlr_backend_is_drm(output->backend))
createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable
if (output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM)
createdByUser = true; // should be true. WL and Headless backends should be addable / removable
// get monitor rule that matches
SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(*this);
@ -84,54 +101,23 @@ void CMonitor::onConnect(bool noRule) {
// if it's disabled, disable and ignore
if (monitorRule.disabled) {
wlr_output_state_set_scale(state.wlr(), 1);
wlr_output_state_set_transform(state.wlr(), WL_OUTPUT_TRANSFORM_NORMAL);
auto PREFSTATE = wlr_output_preferred_mode(output);
if (!PREFSTATE) {
wlr_output_mode* mode;
wl_list_for_each(mode, &output->modes, link) {
wlr_output_state_set_mode(state.wlr(), mode);
if (!wlr_output_test_state(output, state.wlr()))
continue;
PREFSTATE = mode;
break;
}
}
if (PREFSTATE)
wlr_output_state_set_mode(state.wlr(), PREFSTATE);
else
Debug::log(WARN, "No mode found for disabled output {}", output->name);
wlr_output_state_set_enabled(state.wlr(), 0);
output->state->setEnabled(false);
if (!state.commit())
Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name);
m_bEnabled = false;
hyprListener_monitorFrame.removeCallback();
listeners.frame.reset();
return;
}
if (output->non_desktop) {
if (output->nonDesktop) {
Debug::log(LOG, "Not configuring non-desktop output");
if (g_pCompositor->m_sWRLDRMLeaseMgr) {
wlr_drm_lease_v1_manager_offer_output(g_pCompositor->m_sWRLDRMLeaseMgr, output);
}
return;
}
if (PROTO::lease)
PROTO::lease->offer(self.lock());
if (!m_bRenderingInitPassed) {
output->allocator = nullptr;
output->renderer = nullptr;
wlr_output_init_render(output, g_pCompositor->m_sWLRAllocator, g_pCompositor->m_sWLRRenderer);
m_bRenderingInitPassed = true;
return;
}
SP<CMonitor>* thisWrapper = nullptr;
@ -151,14 +137,14 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true;
wlr_output_state_set_enabled(state.wlr(), 1);
output->state->setEnabled(true);
// set mode, also applies
if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit");
Debug::log(WARN, "state.commit() failed in CMonitor::onCommit");
damage.setSize(vecTransformedSize);
@ -214,7 +200,7 @@ void CMonitor::onConnect(bool noRule) {
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this);
g_pCompositor->scheduleFrameForMonitor(this);
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_NEW_MONITOR);
PROTO::gamma->applyGammaToState(this);
@ -261,12 +247,10 @@ void CMonitor::onDisconnect(bool destroy) {
g_pConfigManager->m_bWantsMonitorReload = true;
}
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorPresented.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
listeners.frame.reset();
listeners.presented.reset();
listeners.needsFrame.reset();
listeners.commit.reset();
for (size_t i = 0; i < 4; ++i) {
for (auto& ls : m_aLayerSurfaceLayers[i]) {
@ -316,10 +300,10 @@ void CMonitor::onDisconnect(bool destroy) {
activeWorkspace->m_bVisible = false;
activeWorkspace.reset();
wlr_output_state_set_enabled(state.wlr(), false);
output->state->setEnabled(false);
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onDisconnect");
Debug::log(WARN, "state.commit() failed in CMonitor::onDisconnect");
if (g_pCompositor->m_pLastMonitor.get() == this)
g_pCompositor->setActiveMonitor(BACKUPMON ? BACKUPMON : g_pCompositor->m_pUnsafeOutput);
@ -344,9 +328,9 @@ void CMonitor::addDamage(const pixman_region32_t* rg) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
damage.damageEntire();
g_pCompositor->scheduleFrameForMonitor(this);
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
} else if (damage.damage(rg))
g_pCompositor->scheduleFrameForMonitor(this);
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
void CMonitor::addDamage(const CRegion* rg) {
@ -357,11 +341,11 @@ void CMonitor::addDamage(const CBox* box) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
damage.damageEntire();
g_pCompositor->scheduleFrameForMonitor(this);
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
if (damage.damage(*box))
g_pCompositor->scheduleFrameForMonitor(this);
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}
bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
@ -369,8 +353,8 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
static auto PMINRR = CConfigValue<Hyprlang::INT>("cursor:min_refresh_rate");
// skip scheduling extra frames for fullsreen apps with vrr
bool shouldSkip = *PNOBREAK && output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow &&
activeWorkspace->m_efFullscreenMode == FULLSCREEN_FULL;
bool shouldSkip =
*PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FULLSCREEN_FULL;
// keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) {
@ -563,7 +547,7 @@ float CMonitor::getDefaultScale() {
static constexpr double MMPERINCH = 25.4;
const auto DIAGONALPX = sqrt(pow(vecPixelSize.x, 2) + pow(vecPixelSize.y, 2));
const auto DIAGONALIN = sqrt(pow(output->phys_width / MMPERINCH, 2) + pow(output->phys_height / MMPERINCH, 2));
const auto DIAGONALIN = sqrt(pow(output->physicalSize.x / MMPERINCH, 2) + pow(output->physicalSize.y / MMPERINCH, 2));
const auto PPI = DIAGONALPX / DIAGONALIN;
@ -767,11 +751,11 @@ Vector2D CMonitor::middle() {
}
void CMonitor::updateMatrix() {
wlr_matrix_identity(projMatrix.data());
matrixIdentity(projMatrix.data());
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
wlr_matrix_translate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
wlr_matrix_transform(projMatrix.data(), transform);
wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
matrixTranslate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
matrixTransform(projMatrix.data(), wlTransformToHyprutils(transform));
matrixTranslate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
}
}
@ -787,31 +771,124 @@ CBox CMonitor::logicalBox() {
return {vecPosition, vecSize};
}
static void onDoneSource(void* data) {
auto pMonitor = (CMonitor*)data;
if (!PROTO::outputs.contains(pMonitor->szName))
return;
PROTO::outputs.at(pMonitor->szName)->sendDone();
}
void CMonitor::scheduleDone() {
if (doneSource)
return;
doneSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, ::onDoneSource, this);
}
bool CMonitor::attemptDirectScanout() {
if (!mirrors.empty() || isMirror() || g_pHyprRenderer->m_bDirectScanoutBlocked)
return false; // do not DS if this monitor is being mirrored. Will break the functionality.
if (g_pPointerManager->softwareLockedFor(self.lock()))
return false;
const auto PCANDIDATE = solitaryClient.lock();
if (!PCANDIDATE)
return false;
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE);
if (!PSURFACE || !PSURFACE->current.buffer || PSURFACE->current.buffer->size != vecPixelSize || PSURFACE->current.transform != transform)
return false;
// we can't scanout shm buffers.
if (!PSURFACE->current.buffer->dmabuf().success)
return false;
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
output->state->setBuffer(PSURFACE->current.buffer);
output->state->setPresentationMode(Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
if (!state.test())
return false;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
Debug::log(TRACE, "presentFeedback for DS");
PSURFACE->presentFeedback(&now, this, true);
if (state.commit()) {
if (lastScanout.expired()) {
lastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
}
} else {
lastScanout.reset();
return false;
}
return true;
}
CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner;
wlr_output_state_init(&m_state);
}
CMonitorState::~CMonitorState() {
wlr_output_state_finish(&m_state);
;
}
wlr_output_state* CMonitorState::wlr() {
return &m_state;
}
void CMonitorState::ensureBufferPresent() {
if (!m_pOwner->output->state->state().enabled) {
Debug::log(TRACE, "CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled");
return;
}
void CMonitorState::clear() {
wlr_output_state_finish(&m_state);
m_state = {0};
wlr_output_state_init(&m_state);
if (m_pOwner->output->state->state().buffer)
return;
// this is required for modesetting being possible and might be missing in case of first tests in the renderer
// where we test modes and buffers
Debug::log(LOG, "CMonitorState::ensureBufferPresent: no buffer, attaching one from the swapchain for modeset being possible");
m_pOwner->output->state->setBuffer(m_pOwner->output->swapchain->next(nullptr));
m_pOwner->output->swapchain->rollback(); // restore the counter, don't advance the swapchain
}
bool CMonitorState::commit() {
bool ret = wlr_output_commit_state(m_pOwner->output, &m_state);
clear();
if (!updateSwapchain())
return false;
ensureBufferPresent();
bool ret = m_pOwner->output->commit();
return ret;
}
bool CMonitorState::test() {
return wlr_output_test_state(m_pOwner->output, &m_state);
if (!updateSwapchain())
return false;
ensureBufferPresent();
return m_pOwner->output->test();
}
bool CMonitorState::updateSwapchain() {
auto options = m_pOwner->output->swapchain->currentOptions();
const auto& STATE = m_pOwner->output->state->state();
const auto& MODE = STATE.mode ? STATE.mode : STATE.customMode;
if (!MODE) {
Debug::log(WARN, "updateSwapchain: No mode?");
return true;
}
options.format = STATE.drmFormat;
options.scanout = true;
options.length = 2;
options.size = MODE->pixelSize;
return m_pOwner->output->swapchain->reconfigure(options);
}

View file

@ -12,6 +12,8 @@
#include <optional>
#include "signal/Signal.hpp"
#include "DamageRing.hpp"
#include <aquamarine/output/Output.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
// Enum for the different types of auto directions, e.g. auto-left, auto-up.
enum eAutoDirs {
@ -38,22 +40,21 @@ struct SMonitorRule {
};
class CMonitor;
class CSyncTimeline;
// Class for wrapping the wlr state
class CMonitorState {
public:
CMonitorState(CMonitor* owner);
~CMonitorState();
wlr_output_state* wlr();
void clear();
// commit() will also clear()
bool commit();
bool test();
bool updateSwapchain();
private:
wlr_output_state m_state = {0};
CMonitor* m_pOwner;
void ensureBufferPresent();
CMonitor* m_pOwner;
};
class CMonitor {
@ -61,61 +62,69 @@ class CMonitor {
CMonitor();
~CMonitor();
Vector2D vecPosition = Vector2D(-1, -1); // means unset
Vector2D vecXWaylandPosition = Vector2D(-1, -1); // means unset
Vector2D vecSize = Vector2D(0, 0);
Vector2D vecPixelSize = Vector2D(0, 0);
Vector2D vecTransformedSize = Vector2D(0, 0);
Vector2D vecPosition = Vector2D(-1, -1); // means unset
Vector2D vecXWaylandPosition = Vector2D(-1, -1); // means unset
Vector2D vecSize = Vector2D(0, 0);
Vector2D vecPixelSize = Vector2D(0, 0);
Vector2D vecTransformedSize = Vector2D(0, 0);
bool primary = false;
bool primary = false;
uint64_t ID = -1;
PHLWORKSPACE activeWorkspace = nullptr;
PHLWORKSPACE activeSpecialWorkspace = nullptr;
float setScale = 1; // scale set by cfg
float scale = 1; // real scale
uint64_t ID = -1;
PHLWORKSPACE activeWorkspace = nullptr;
PHLWORKSPACE activeSpecialWorkspace = nullptr;
float setScale = 1; // scale set by cfg
float scale = 1; // real scale
std::string szName = "";
std::string szDescription = "";
std::string szShortDescription = "";
std::string szName = "";
std::string szDescription = "";
std::string szShortDescription = "";
Vector2D vecReservedTopLeft = Vector2D(0, 0);
Vector2D vecReservedBottomRight = Vector2D(0, 0);
Vector2D vecReservedTopLeft = Vector2D(0, 0);
Vector2D vecReservedBottomRight = Vector2D(0, 0);
drmModeModeInfo customDrmMode = {};
drmModeModeInfo customDrmMode = {};
CMonitorState state;
CDamageRing damage;
CMonitorState state;
CDamageRing damage;
wlr_output* output = nullptr;
float refreshRate = 60;
int framesToSkip = 0;
int forceFullFrames = 0;
bool noFrameSchedule = false;
bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
float xwaylandScale = 1.f;
std::array<float, 9> projMatrix = {0};
std::optional<Vector2D> forceSize;
wlr_output_mode* currentMode = nullptr;
SP<Aquamarine::IOutput> output;
float refreshRate = 60;
int framesToSkip = 0;
int forceFullFrames = 0;
bool noFrameSchedule = false;
bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
float xwaylandScale = 1.f;
std::array<float, 9> projMatrix = {0};
std::optional<Vector2D> forceSize;
SP<Aquamarine::SOutputMode> currentMode;
SP<Aquamarine::CSwapchain> cursorSwapchain;
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
bool createdByUser = false;
uint32_t drmFormat = DRM_FORMAT_INVALID;
bool isUnsafeFallback = false;
bool dpmsStatus = true;
bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
bool createdByUser = false;
bool isUnsafeFallback = false;
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool renderingActive = false;
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool renderingActive = false;
wl_event_source* renderTimer = nullptr; // for RAT
bool RATScheduled = false;
CTimer lastPresentationTimer;
wl_event_source* renderTimer = nullptr; // for RAT
bool RATScheduled = false;
CTimer lastPresentationTimer;
SMonitorRule activeMonitorRule;
bool isBeingLeased = false;
WP<CMonitor> self;
SMonitorRule activeMonitorRule;
// explicit sync
SP<CSyncTimeline> inTimeline;
SP<CSyncTimeline> outTimeline;
uint64_t lastWaitPoint = 0;
uint64_t commitSeq = 0;
WP<CMonitor> self;
// mirroring
CMonitor* pMirrorOf = nullptr;
@ -124,6 +133,9 @@ class CMonitor {
// for tearing
PHLWINDOWREF solitaryClient;
// for direct scanout
PHLWINDOWREF lastScanout;
struct {
bool canTear = false;
bool nextRenderTorn = false;
@ -143,15 +155,6 @@ class CMonitor {
std::array<std::vector<PHLLSREF>, 4> m_aLayerSurfaceLayers;
DYNLISTENER(monitorFrame);
DYNLISTENER(monitorDestroy);
DYNLISTENER(monitorStateRequest);
DYNLISTENER(monitorDamage);
DYNLISTENER(monitorNeedsFrame);
DYNLISTENER(monitorCommit);
DYNLISTENER(monitorBind);
DYNLISTENER(monitorPresented);
// methods
void onConnect(bool noRule);
void onDisconnect(bool destroy = false);
@ -173,6 +176,8 @@ class CMonitor {
int64_t activeWorkspaceID();
int64_t activeSpecialWorkspaceID();
CBox logicalBox();
void scheduleDone();
bool attemptDirectScanout();
bool m_bEnabled = false;
bool m_bRenderingInitPassed = false;
@ -184,6 +189,17 @@ class CMonitor {
}
private:
void setupDefaultWS(const SMonitorRule&);
int findAvailableDefaultWS();
void setupDefaultWS(const SMonitorRule&);
int findAvailableDefaultWS();
wl_event_source* doneSource = nullptr;
struct {
CHyprSignalListener frame;
CHyprSignalListener destroy;
CHyprSignalListener state;
CHyprSignalListener needsFrame;
CHyprSignalListener presented;
CHyprSignalListener commit;
} listeners;
};

View file

@ -15,6 +15,8 @@ class IPointer;
class IKeyboard;
class CWLSurfaceResource;
AQUAMARINE_FORWARD(ISwitch);
struct SRenderData {
CMonitor* pMonitor;
timespec* when;
@ -70,14 +72,14 @@ struct SSwipeGesture {
};
struct SSwitchDevice {
wlr_input_device* pWlrDevice = nullptr;
WP<Aquamarine::ISwitch> pDevice;
int status = -1; // uninitialized
DYNLISTENER(destroy);
DYNLISTENER(toggle);
struct {
CHyprSignalListener destroy;
CHyprSignalListener fire;
} listeners;
bool operator==(const SSwitchDevice& other) const {
return pWlrDevice == other.pWlrDevice;
return pDevice == other.pDevice;
}
};

View file

@ -1,7 +0,0 @@
#pragma once
inline bool wlr_backend_is_x11(void*) {
return false;
}
inline void wlr_x11_output_create(void*) {}

View file

@ -17,14 +17,14 @@ Hyprutils::Math::eTransform wlTransformToHyprutils(wl_output_transform t) {
return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_NORMAL;
}
static void matrixIdentity(float mat[9]) {
void matrixIdentity(float mat[9]) {
static const float identity[9] = {
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
};
memcpy(mat, identity, sizeof(identity));
}
static void matrixMultiply(float mat[9], const float a[9], const float b[9]) {
void matrixMultiply(float mat[9], const float a[9], const float b[9]) {
float product[9];
product[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
@ -42,141 +42,56 @@ static void matrixMultiply(float mat[9], const float a[9], const float b[9]) {
memcpy(mat, product, sizeof(product));
}
static void matrixTranspose(float mat[9], const float a[9]) {
void matrixTranspose(float mat[9], const float a[9]) {
float transposition[9] = {
a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8],
};
memcpy(mat, transposition, sizeof(transposition));
}
static void matrixTranslate(float mat[9], float x, float y) {
void matrixTranslate(float mat[9], float x, float y) {
float translate[9] = {
1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f,
};
matrixMultiply(mat, mat, translate);
}
static void matrixScale(float mat[9], float x, float y) {
void matrixScale(float mat[9], float x, float y) {
float scale[9] = {
x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f,
};
matrixMultiply(mat, mat, scale);
}
static void matrixRotate(float mat[9], float rad) {
void matrixRotate(float mat[9], float rad) {
float rotate[9] = {
cos(rad), -sin(rad), 0.0f, sin(rad), cos(rad), 0.0f, 0.0f, 0.0f, 1.0f,
};
matrixMultiply(mat, mat, rotate);
}
static std::unordered_map<eTransform, std::array<float, 9>> transforms = {
{HYPRUTILS_TRANSFORM_NORMAL,
{
1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
{HYPRUTILS_TRANSFORM_90,
{
0.0f,
1.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
{HYPRUTILS_TRANSFORM_180,
{
-1.0f,
0.0f,
0.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
{HYPRUTILS_TRANSFORM_270,
{
0.0f,
-1.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
{HYPRUTILS_TRANSFORM_FLIPPED,
{
-1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
{HYPRUTILS_TRANSFORM_FLIPPED_90,
{
0.0f,
1.0f,
0.0f,
1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
{HYPRUTILS_TRANSFORM_FLIPPED_180,
{
1.0f,
0.0f,
0.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
{HYPRUTILS_TRANSFORM_FLIPPED_270,
{
0.0f,
-1.0f,
0.0f,
-1.0f,
0.0f,
0.0f,
0.0f,
0.0f,
1.0f,
}},
};
static void matrixTransform(float mat[9], eTransform transform) {
matrixMultiply(mat, mat, transforms.at(transform).data());
const std::unordered_map<eTransform, std::array<float, 9>>& getTransforms() {
static std::unordered_map<eTransform, std::array<float, 9>> transforms = {
{HYPRUTILS_TRANSFORM_NORMAL, {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
{HYPRUTILS_TRANSFORM_90, {0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
{HYPRUTILS_TRANSFORM_180, {-1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
{HYPRUTILS_TRANSFORM_270, {0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
{HYPRUTILS_TRANSFORM_FLIPPED, {-1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
{HYPRUTILS_TRANSFORM_FLIPPED_90, {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
{HYPRUTILS_TRANSFORM_FLIPPED_180, {1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
{HYPRUTILS_TRANSFORM_FLIPPED_270, {0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
};
return transforms;
}
static void matrixProjection(float mat[9], int width, int height, eTransform transform) {
void matrixTransform(float mat[9], eTransform transform) {
matrixMultiply(mat, mat, getTransforms().at(transform).data());
}
void matrixProjection(float mat[9], int width, int height, eTransform transform) {
memset(mat, 0, sizeof(*mat) * 9);
const float* t = transforms.at(transform).data();
const float* t = getTransforms().at(transform).data();
float x = 2.0f / width;
float y = 2.0f / height;
@ -219,3 +134,10 @@ void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, c
matrixMultiply(mat, projection, mat);
}
wl_output_transform invertTransform(wl_output_transform tr) {
if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED))
tr = (wl_output_transform)(tr ^ (int)WL_OUTPUT_TRANSFORM_180);
return tr;
}

View file

@ -7,5 +7,14 @@
using namespace Hyprutils::Math;
eTransform wlTransformToHyprutils(wl_output_transform t);
void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]);
eTransform wlTransformToHyprutils(wl_output_transform t);
void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]);
void matrixProjection(float mat[9], int width, int height, eTransform transform);
void matrixTransform(float mat[9], eTransform transform);
void matrixRotate(float mat[9], float rad);
void matrixScale(float mat[9], float x, float y);
void matrixTranslate(float mat[9], float x, float y);
void matrixTranspose(float mat[9], const float a[9]);
void matrixMultiply(float mat[9], const float a[9], const float b[9]);
void matrixIdentity(float mat[9]);
wl_output_transform invertTransform(wl_output_transform tr);

View file

@ -0,0 +1,190 @@
#include "SyncTimeline.hpp"
#include "../../defines.hpp"
#include "../../managers/eventLoop/EventLoopManager.hpp"
#include <xf86drm.h>
#include <sys/eventfd.h>
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
timeline->drmFD = drmFD_;
timeline->self = timeline;
if (drmSyncobjCreate(drmFD_, 0, &timeline->handle)) {
Debug::log(ERR, "CSyncTimeline: failed to create a drm syncobj??");
return nullptr;
}
return timeline;
}
SP<CSyncTimeline> CSyncTimeline::create(int drmFD_, int drmSyncobjFD) {
auto timeline = SP<CSyncTimeline>(new CSyncTimeline);
timeline->drmFD = drmFD_;
timeline->self = timeline;
if (drmSyncobjFDToHandle(drmFD_, drmSyncobjFD, &timeline->handle)) {
Debug::log(ERR, "CSyncTimeline: failed to create a drm syncobj from fd??");
return nullptr;
}
return timeline;
}
CSyncTimeline::~CSyncTimeline() {
if (handle == 0)
return;
drmSyncobjDestroy(drmFD, handle);
}
std::optional<bool> CSyncTimeline::check(uint64_t point, uint32_t flags) {
#ifdef __FreeBSD__
constexpr int ETIME_ERR = ETIMEDOUT;
#else
constexpr int ETIME_ERR = ETIME;
#endif
uint32_t signaled = 0;
int ret = drmSyncobjTimelineWait(drmFD, &handle, &point, 1, 0, flags, &signaled);
if (ret != 0 && ret != -ETIME_ERR) {
Debug::log(ERR, "CSyncTimeline::check: drmSyncobjTimelineWait failed");
return std::nullopt;
}
return ret == 0;
}
static int handleWaiterFD(int fd, uint32_t mask, void* data) {
auto waiter = (CSyncTimeline::SWaiter*)data;
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
Debug::log(ERR, "handleWaiterFD: eventfd error");
return 0;
}
if (mask & WL_EVENT_READABLE) {
uint64_t value = 0;
if (read(fd, &value, sizeof(value)) <= 0)
Debug::log(ERR, "handleWaiterFD: failed to read from eventfd");
}
wl_event_source_remove(waiter->source);
waiter->source = nullptr;
if (waiter->fn)
waiter->fn();
if (waiter->timeline)
waiter->timeline->removeWaiter(waiter);
return 0;
}
bool CSyncTimeline::addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags) {
auto w = makeShared<SWaiter>();
w->fn = waiter;
w->timeline = self;
int eventFD = eventfd(0, EFD_CLOEXEC);
if (eventFD < 0) {
Debug::log(ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd");
return false;
}
drm_syncobj_eventfd syncobjEventFD = {
.handle = handle,
.flags = flags,
.point = point,
.fd = eventFD,
};
if (drmIoctl(drmFD, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobjEventFD) != 0) {
Debug::log(ERR, "CSyncTimeline::addWaiter: drmIoctl failed");
close(eventFD);
return false;
}
w->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, eventFD, WL_EVENT_READABLE, ::handleWaiterFD, w.get());
if (!w->source) {
Debug::log(ERR, "CSyncTimeline::addWaiter: wl_event_loop_add_fd failed");
close(eventFD);
return false;
}
waiters.emplace_back(w);
return true;
}
void CSyncTimeline::removeWaiter(SWaiter* w) {
if (w->source) {
wl_event_source_remove(w->source);
w->source = nullptr;
}
std::erase_if(waiters, [w](const auto& e) { return e.get() == w; });
}
int CSyncTimeline::exportAsSyncFileFD(uint64_t src) {
int sync = -1;
uint32_t syncHandle = 0;
if (drmSyncobjCreate(drmFD, 0, &syncHandle)) {
Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjCreate failed");
return -1;
}
if (drmSyncobjTransfer(drmFD, syncHandle, 0, handle, src, 0)) {
Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjTransfer failed");
drmSyncobjDestroy(drmFD, syncHandle);
return -1;
}
if (drmSyncobjExportSyncFile(drmFD, syncHandle, &sync)) {
Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjExportSyncFile failed");
drmSyncobjDestroy(drmFD, syncHandle);
return -1;
}
drmSyncobjDestroy(drmFD, syncHandle);
return sync;
}
bool CSyncTimeline::importFromSyncFileFD(uint64_t dst, int fd) {
uint32_t syncHandle = 0;
if (drmSyncobjCreate(drmFD, 0, &syncHandle)) {
Debug::log(ERR, "importFromSyncFileFD: drmSyncobjCreate failed");
return false;
}
if (drmSyncobjImportSyncFile(drmFD, syncHandle, fd)) {
Debug::log(ERR, "importFromSyncFileFD: drmSyncobjImportSyncFile failed");
drmSyncobjDestroy(drmFD, syncHandle);
return false;
}
if (drmSyncobjTransfer(drmFD, handle, dst, syncHandle, 0, 0)) {
Debug::log(ERR, "importFromSyncFileFD: drmSyncobjTransfer failed");
drmSyncobjDestroy(drmFD, syncHandle);
return false;
}
drmSyncobjDestroy(drmFD, syncHandle);
return true;
}
bool CSyncTimeline::transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint) {
if (drmFD != from->drmFD) {
Debug::log(ERR, "CSyncTimeline::transfer: cannot transfer timelines between gpus, {} -> {}", from->drmFD, drmFD);
return false;
}
if (drmSyncobjTransfer(drmFD, handle, toPoint, from->handle, fromPoint, 0)) {
Debug::log(ERR, "CSyncTimeline::transfer: drmSyncobjTransfer failed");
return false;
}
return true;
}

View file

@ -0,0 +1,47 @@
#pragma once
#include <cstdint>
#include <optional>
#include <vector>
#include <functional>
#include "../memory/Memory.hpp"
/*
Hyprland synchronization timelines are based on the wlroots' ones, which
are based on Vk timeline semaphores: https://www.khronos.org/blog/vulkan-timeline-semaphores
*/
struct wl_event_source;
class CSyncTimeline {
public:
static SP<CSyncTimeline> create(int drmFD_);
static SP<CSyncTimeline> create(int drmFD_, int drmSyncobjFD);
~CSyncTimeline();
struct SWaiter {
std::function<void()> fn;
wl_event_source* source = nullptr;
WP<CSyncTimeline> timeline;
};
// check if the timeline point has been signaled
// flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE
// std::nullopt on fail
std::optional<bool> check(uint64_t point, uint32_t flags);
bool addWaiter(const std::function<void()>& waiter, uint64_t point, uint32_t flags);
void removeWaiter(SWaiter*);
int exportAsSyncFileFD(uint64_t src);
bool importFromSyncFileFD(uint64_t dst, int fd);
bool transfer(SP<CSyncTimeline> from, uint64_t fromPoint, uint64_t toPoint);
int drmFD = -1;
uint32_t handle = 0;
WP<CSyncTimeline> self;
private:
CSyncTimeline() = default;
std::vector<SP<SWaiter>> waiters;
};

View file

@ -16,78 +16,6 @@
#include <time.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <mutex>
#include <thread>
#include <filesystem>
#include <climits>
#if true
// wlroots uses dumb-ass shit that makes it not compile on C++, let's fix that.
// https://github.com/swaywm/wlroots/issues/682
// pthread first because it uses class in a C++ way and XWayland includes that...
#include <pthread.h>
#define class _class
#define namespace _namespace
#define static
#define delete delete_
extern "C" {
#include <wlr/backend.h>
#include <wlr/backend/libinput.h>
#include <wlr/backend/drm.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm_lease_v1.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.h>
#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_viewporter.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include <wlr/util/edges.h>
#include <wlr/types/wlr_tablet_pad.h>
#include <wlr/types/wlr_tablet_tool.h>
#include <xkbcommon/xkbcommon.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_texture.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_switch.h>
#include <wlr/config.h>
#include <wlr/backend/headless.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/wayland.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/util/box.h>
#include <wlr/util/transform.h>
#include <wlr/render/swapchain.h>
#include <wlr/render/egl.h>
#include <libdrm/drm_fourcc.h>
#if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h>
#endif
}
#undef delete
#undef class
#undef namespace
#undef static
#endif
#ifdef LEGACY_RENDERER
#include <GLES2/gl2.h>
@ -99,10 +27,6 @@ extern "C" {
#include <GLES3/gl3ext.h>
#endif
#if !WLR_HAS_X11_BACKEND
#include "helpers/X11Stubs.hpp"
#endif
#ifdef NO_XWAYLAND
#define XWAYLAND false
#else

View file

@ -105,3 +105,8 @@
class name; \
} \
}
#define AQUAMARINE_FORWARD(name) \
namespace Aquamarine { \
class name; \
}

View file

@ -4,6 +4,7 @@
#include "config/ConfigManager.hpp"
#include "init/initHelpers.hpp"
#include <fcntl.h>
#include <iostream>
#include <iterator>
#include <vector>
@ -16,6 +17,8 @@ void help() {
std::cout << "\nArguments:\n";
std::cout << " --help -h - Show this message again\n";
std::cout << " --config FILE -c FILE - Specify config file to use\n";
std::cout << " --socket NAME - Sets the Wayland socket name (for Wayland socket handover)\n";
std::cout << " --wayland-fd FD - Sets the Wayland socket fd (for Wayland socket handover)\n";
std::cout << " --i-am-really-stupid - Omits root user privileges check (why would you do that?)\n";
}
@ -37,6 +40,8 @@ int main(int argc, char** argv) {
// parse some args
std::string configPath;
std::string socketName;
int socketFd = -1;
bool ignoreSudo = false;
std::vector<std::string> args{argv + 1, argv + argc};
@ -46,6 +51,36 @@ int main(int argc, char** argv) {
std::cout << "[ WARNING ] Running Hyprland with superuser privileges might damage your system\n";
ignoreSudo = true;
} else if (it->compare("--socket") == 0) {
if (std::next(it) == args.end()) {
help();
return 1;
}
socketName = *std::next(it);
it++;
} else if (it->compare("--wayland-fd") == 0) {
if (std::next(it) == args.end()) {
help();
return 1;
}
try {
socketFd = std::stoi(std::next(it)->c_str());
// check if socketFd is a valid file descriptor
if (fcntl(socketFd, F_GETFD) == -1)
throw std::exception();
} catch (...) {
std::cerr << "[ ERROR ] Invalid Wayland FD!\n";
help();
return 1;
}
it++;
} else if (it->compare("-c") == 0 || it->compare("--config") == 0) {
if (std::next(it) == args.end()) {
help();
@ -93,6 +128,13 @@ int main(int argc, char** argv) {
std::cout << "Superuser privileges check is omitted. I hope you know what you're doing.\n";
}
if (socketName.empty() ^ (socketFd == -1)) {
std::cerr << "[ ERROR ] Hyprland was launched with only one of --socket and --wayland-fd.\n";
std::cerr << " Hint: Pass both --socket and --wayland-fd to perform Wayland socket handover.\n";
return 1;
}
std::cout << "Welcome to Hyprland!\n";
// let's init the compositor.
@ -113,7 +155,7 @@ int main(int argc, char** argv) {
Debug::log(LOG, "Hyprland init finished.");
// If all's good to go, start.
g_pCompositor->startCompositor();
g_pCompositor->startCompositor(socketName, socketFd);
g_pCompositor->m_bIsShuttingDown = true;

View file

@ -259,7 +259,7 @@ void CAnimationManager::tick() {
// manually schedule a frame
if (PMONITOR)
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE);
}
// do it here, because if this alters the animation vars deque we would be in trouble above.

View file

@ -3,10 +3,11 @@
#include "../config/ConfigValue.hpp"
#include "PointerManager.hpp"
#include "../xwayland/XWayland.hpp"
#include <cstring>
#include "../helpers/CursorShapes.hpp"
extern "C" {
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <X11/Xcursor/Xcursor.h>
}
static int cursorAnimTimer(void* data) {
@ -45,8 +46,7 @@ CCursorManager::CCursorManager() {
if (m_iSize == 0)
m_iSize = 24;
m_pWLRXCursorMgr = wlr_xcursor_manager_create(getenv("XCURSOR_THEME"), m_iSize);
wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0);
xcursor.loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "", m_iSize * std::ceil(m_fCursorScale));
m_pAnimationTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ::cursorAnimTimer, nullptr);
@ -56,9 +56,6 @@ CCursorManager::CCursorManager() {
}
CCursorManager::~CCursorManager() {
if (m_pWLRXCursorMgr)
wlr_xcursor_manager_destroy(m_pWLRXCursorMgr);
if (m_pAnimationTimer)
wl_event_source_remove(m_pAnimationTimer);
}
@ -67,54 +64,61 @@ void CCursorManager::dropBufferRef(CCursorManager::CCursorBuffer* ref) {
std::erase_if(m_vCursorBuffers, [ref](const auto& buf) { return buf.get() == ref; });
}
static void cursorBufferDestroy(struct wlr_buffer* wlr_buffer) {
CCursorManager::CCursorBuffer::SCursorWlrBuffer* buffer = wl_container_of(wlr_buffer, buffer, base);
g_pCursorManager->dropBufferRef(buffer->parent);
CCursorManager::CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
surface = surf;
size = size_;
stride = cairo_image_surface_get_stride(surf);
}
static bool cursorBufferBeginDataPtr(struct wlr_buffer* wlr_buffer, uint32_t flags, void** data, uint32_t* format, size_t* stride) {
CCursorManager::CCursorBuffer::SCursorWlrBuffer* buffer = wl_container_of(wlr_buffer, buffer, base);
if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE)
return false;
*data = buffer->pixelData ? buffer->pixelData : cairo_image_surface_get_data(buffer->surface);
*stride = buffer->stride;
*format = DRM_FORMAT_ARGB8888;
return true;
}
static void cursorBufferEndDataPtr(struct wlr_buffer* wlr_buffer) {
;
}
//
static const wlr_buffer_impl bufferImpl = {
.destroy = cursorBufferDestroy,
.begin_data_ptr_access = cursorBufferBeginDataPtr,
.end_data_ptr_access = cursorBufferEndDataPtr,
};
CCursorManager::CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : size(size_), hotspot(hot_) {
wlrBuffer.surface = surf;
wlr_buffer_init(&wlrBuffer.base, &bufferImpl, size.x, size.y);
wlrBuffer.parent = this;
wlrBuffer.stride = cairo_image_surface_get_stride(surf);
}
CCursorManager::CCursorBuffer::CCursorBuffer(uint8_t* pixelData, const Vector2D& size_, const Vector2D& hot_) : size(size_), hotspot(hot_) {
wlrBuffer.pixelData = pixelData;
wlr_buffer_init(&wlrBuffer.base, &bufferImpl, size.x, size.y);
wlrBuffer.parent = this;
wlrBuffer.stride = 4 * size_.x;
CCursorManager::CCursorBuffer::CCursorBuffer(uint8_t* pixelData_, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
pixelData = pixelData_;
size = size_;
stride = 4 * size_.x;
}
CCursorManager::CCursorBuffer::~CCursorBuffer() {
; // will be freed in .destroy
;
}
wlr_buffer* CCursorManager::getCursorBuffer() {
return !m_vCursorBuffers.empty() ? &m_vCursorBuffers.back()->wlrBuffer.base : nullptr;
Aquamarine::eBufferCapability CCursorManager::CCursorBuffer::caps() {
return Aquamarine::eBufferCapability::BUFFER_CAPABILITY_DATAPTR;
}
Aquamarine::eBufferType CCursorManager::CCursorBuffer::type() {
return Aquamarine::eBufferType::BUFFER_TYPE_SHM;
}
void CCursorManager::CCursorBuffer::update(const Hyprutils::Math::CRegion& damage) {
;
}
bool CCursorManager::CCursorBuffer::isSynchronous() {
return true;
}
bool CCursorManager::CCursorBuffer::good() {
return true;
}
Aquamarine::SSHMAttrs CCursorManager::CCursorBuffer::shm() {
Aquamarine::SSHMAttrs attrs;
attrs.success = true;
attrs.format = DRM_FORMAT_ARGB8888;
attrs.size = size;
attrs.stride = stride;
return attrs;
}
std::tuple<uint8_t*, uint32_t, size_t> CCursorManager::CCursorBuffer::beginDataPtr(uint32_t flags) {
return {pixelData ? pixelData : cairo_image_surface_get_data(surface), DRM_FORMAT_ARGB8888, stride};
}
void CCursorManager::CCursorBuffer::endDataPtr() {
;
}
SP<Aquamarine::IBuffer> CCursorManager::getCursorBuffer() {
return !m_vCursorBuffers.empty() ? m_vCursorBuffers.back() : nullptr;
}
void CCursorManager::setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotspot) {
@ -127,34 +131,24 @@ void CCursorManager::setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotsp
}
void CCursorManager::setXCursor(const std::string& name) {
if (!m_pWLRXCursorMgr) {
g_pPointerManager->resetCursorImage();
return;
}
float scale = std::ceil(m_fCursorScale);
wlr_xcursor_manager_load(m_pWLRXCursorMgr, scale);
auto xcursor = wlr_xcursor_manager_get_xcursor(m_pWLRXCursorMgr, name.c_str(), scale);
if (!xcursor) {
Debug::log(ERR, "XCursor has no shape {}, retrying with left-ptr", name);
xcursor = wlr_xcursor_manager_get_xcursor(m_pWLRXCursorMgr, "left-ptr", scale);
}
if (!xcursor || !xcursor->images[0]) {
Debug::log(ERR, "XCursor is broken. F this garbage.");
if (!xcursor.themeLoaded) {
Debug::log(ERR, "XCursor failed to find theme in setXCursor");
g_pPointerManager->resetCursorImage();
return;
}
auto image = xcursor->images[0];
auto& icon = xcursor.defaultCursor;
// try to get an icon we know if we have one
if (xcursor.cursors.contains(name))
icon = xcursor.cursors.at(name);
m_vCursorBuffers.emplace_back(
std::make_unique<CCursorBuffer>(image->buffer, Vector2D{(int)image->width, (int)image->height}, Vector2D{(double)image->hotspot_x, (double)image->hotspot_y}));
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>((uint8_t*)icon->pixels.data(), icon->size, icon->hotspot));
g_pPointerManager->setCursorBuffer(getCursorBuffer(), Vector2D{(double)image->hotspot_x, (double)image->hotspot_y} / scale, scale);
g_pPointerManager->setCursorBuffer(getCursorBuffer(), icon->hotspot / scale, scale);
if (m_vCursorBuffers.size() > 1)
wlr_buffer_drop(&m_vCursorBuffers.front()->wlrBuffer.base);
dropBufferRef(m_vCursorBuffers.at(0).get());
m_bOurBufferConnected = true;
}
@ -196,14 +190,14 @@ void CCursorManager::setCursorFromName(const std::string& name) {
}
}
m_vCursorBuffers.emplace_back(std::make_unique<CCursorBuffer>(m_sCurrentCursorShapeData.images[0].surface,
Vector2D{m_sCurrentCursorShapeData.images[0].size, m_sCurrentCursorShapeData.images[0].size},
Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY}));
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>(m_sCurrentCursorShapeData.images[0].surface,
Vector2D{m_sCurrentCursorShapeData.images[0].size, m_sCurrentCursorShapeData.images[0].size},
Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY}));
g_pPointerManager->setCursorBuffer(getCursorBuffer(), Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY} / m_fCursorScale,
m_fCursorScale);
if (m_vCursorBuffers.size() > 1)
wlr_buffer_drop(&m_vCursorBuffers.front()->wlrBuffer.base);
dropBufferRef(m_vCursorBuffers.at(0).get());
m_bOurBufferConnected = true;
@ -225,7 +219,7 @@ void CCursorManager::tickAnimatedCursor() {
if ((size_t)m_iCurrentAnimationFrame >= m_sCurrentCursorShapeData.images.size())
m_iCurrentAnimationFrame = 0;
m_vCursorBuffers.emplace_back(std::make_unique<CCursorBuffer>(
m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>(
m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].surface,
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size},
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY}));
@ -256,10 +250,9 @@ void CCursorManager::setXWaylandCursor() {
if (CURSOR.surface) {
g_pXWayland->setCursor(cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), {CURSOR.size, CURSOR.size},
{CURSOR.hotspotX, CURSOR.hotspotY});
} else if (const auto XCURSOR = wlr_xcursor_manager_get_xcursor(m_pWLRXCursorMgr, "left_ptr", 1); XCURSOR) {
g_pXWayland->setCursor(XCURSOR->images[0]->buffer, XCURSOR->images[0]->width * 4, {(int)XCURSOR->images[0]->width, (int)XCURSOR->images[0]->height},
{(double)XCURSOR->images[0]->hotspot_x, (double)XCURSOR->images[0]->hotspot_y});
} else
} else if (xcursor.themeLoaded)
g_pXWayland->setCursor((uint8_t*)xcursor.defaultCursor->pixels.data(), xcursor.defaultCursor->size.x * 4, xcursor.defaultCursor->size, xcursor.defaultCursor->hotspot);
else
Debug::log(ERR, "CursorManager: no valid cursor for xwayland");
}
@ -284,7 +277,7 @@ void CCursorManager::updateTheme() {
for (auto& m : g_pCompositor->m_vMonitors) {
m->forceFullFrames = 5;
g_pCompositor->scheduleFrameForMonitor(m.get());
g_pCompositor->scheduleFrameForMonitor(m.get(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE);
}
}
@ -303,37 +296,163 @@ bool CCursorManager::changeTheme(const std::string& name, const int size) {
Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to X.", name);
if (m_pWLRXCursorMgr)
wlr_xcursor_manager_destroy(m_pWLRXCursorMgr);
xcursor.loadTheme(name, size);
m_pWLRXCursorMgr = wlr_xcursor_manager_create(name.empty() ? "" : name.c_str(), size);
bool xSuccess = wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0) == 1;
m_szTheme = name;
m_iSize = size;
updateTheme();
return true;
}
// this basically checks if xcursor changed used theme to default but better
bool diffTheme = false;
wlr_xcursor_manager_theme* theme;
wl_list_for_each(theme, &m_pWLRXCursorMgr->scaled_themes, link) {
if (std::string{theme->theme->name} != name) {
diffTheme = true;
break;
// Taken from https://gitlab.freedesktop.org/xorg/lib/libxcursor/-/blob/master/src/library.c
// however modified to fit wayland cursor shape names better.
// _ -> -
// clang-format off
static std::array<const char*, 77> XCURSOR_STANDARD_NAMES = {
"X_cursor",
"default", // arrow
"ns-resize", // based-arrow-down
"ns-resize", // based-arrow-up
"boat",
"bogosity",
"sw-resize", // bottom-left-corner
"se-resize", // bottom-right-corner
"s-resize", // bottom-side
"bottom-tee",
"box-spiral",
"center-ptr",
"circle",
"clock",
"coffee-mug",
"cross",
"cross-reverse",
"crosshair",
"diamond-cross",
"dot",
"dotbox",
"double-arrow",
"draft-large",
"draft-small",
"draped-box",
"exchange",
"move", // fleur
"gobbler",
"gumby",
"pointer", // hand1
"grabbing", // hand2
"heart",
"icon",
"iron-cross",
"default", // left-ptr
"w-resize", // left-side
"left-tee",
"leftbutton",
"ll-angle",
"lr-angle",
"man",
"middlebutton",
"mouse",
"pencil",
"pirate",
"plus",
"help", // question-arrow
"right-ptr",
"e-resize", // right-side
"right-tee",
"rightbutton",
"rtl-logo",
"sailboat",
"ns-resize", // sb-down-arrow
"ew-resize", // sb-h-double-arrow
"ew-resize", // sb-left-arrow
"ew-resize", // sb-right-arrow
"n-resize", // sb-up-arrow
"s-resize", // sb-v-double-arrow
"shuttle",
"sizing",
"spider",
"spraycan",
"star",
"target",
"cell", // tcross
"nw-resize", // top-left-arrow
"nw-resize", // top-left-corner
"ne-resize", // top-right-corner
"n-resize", // top-side
"top-tee",
"trek",
"ul-angle",
"umbrella",
"ur-angle",
"wait", // watch
"text", // xterm
};
// clang-format on
void CCursorManager::SXCursorManager::loadTheme(const std::string& name, int size) {
if (lastLoadSize == size && themeName == name)
return;
lastLoadSize = size;
themeLoaded = false;
themeName = name.empty() ? "default" : name;
auto img = XcursorShapeLoadImage(2, themeName.c_str(), size);
if (!img) {
Debug::log(ERR, "XCursor failed finding theme \"{}\". Trying size 24.", themeName);
size = 24;
img = XcursorShapeLoadImage(2, themeName.c_str(), size);
if (!img) {
Debug::log(ERR, "XCursor failed finding theme \"{}\".", themeName);
return;
}
}
if (xSuccess && !diffTheme) {
m_szTheme = name;
m_iSize = size;
updateTheme();
return true;
defaultCursor = makeShared<SXCursor>();
defaultCursor->size = {(int)img->width, (int)img->height};
defaultCursor->hotspot = {(int)img->xhot, (int)img->yhot};
defaultCursor->pixels.resize(img->width * img->height);
std::memcpy(defaultCursor->pixels.data(), img->pixels, img->width * img->height * sizeof(uint32_t));
themeLoaded = true;
XcursorImageDestroy(img);
// gather as many shapes as we can find.
cursors.clear();
for (auto& shape : CURSOR_SHAPE_NAMES) {
int id = -1;
for (size_t i = 0; i < XCURSOR_STANDARD_NAMES.size(); ++i) {
if (XCURSOR_STANDARD_NAMES.at(i) == std::string{shape}) {
id = i;
break;
}
}
if (id < 0) {
Debug::log(LOG, "XCursor has no shape {}, skipping", shape);
continue;
}
auto xImage = XcursorShapeLoadImage(id << 1 /* wtf xcursor? */, themeName.c_str(), size);
if (!xImage) {
Debug::log(LOG, "XCursor failed to find a shape with name {}, skipping", shape);
continue;
}
auto xcursor = makeShared<SXCursor>();
xcursor->size = {(int)xImage->width, (int)xImage->height};
xcursor->hotspot = {(int)xImage->xhot, (int)xImage->yhot};
xcursor->pixels.resize(xImage->width * xImage->height);
std::memcpy(xcursor->pixels.data(), xImage->pixels, xImage->width * xImage->height * sizeof(uint32_t));
cursors.emplace(std::string{shape}, xcursor);
XcursorImageDestroy(xImage);
}
Debug::log(ERR, "X also failed loading theme \"{}\", falling back to previous theme.", name);
m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(m_szTheme.c_str(), hcLogger);
wlr_xcursor_manager_destroy(m_pWLRXCursorMgr);
m_pWLRXCursorMgr = wlr_xcursor_manager_create(m_szTheme.c_str(), m_iSize);
wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0);
updateTheme();
return false;
}

View file

@ -6,47 +6,51 @@
#include "../includes.hpp"
#include "../helpers/math/Math.hpp"
#include "../helpers/memory/Memory.hpp"
#include "../macros.hpp"
#include <aquamarine/buffer/Buffer.hpp>
struct wlr_buffer;
struct wlr_xcursor_manager;
class CWLSurface;
AQUAMARINE_FORWARD(IBuffer);
class CCursorManager {
public:
CCursorManager();
~CCursorManager();
wlr_buffer* getCursorBuffer();
SP<Aquamarine::IBuffer> getCursorBuffer();
void setCursorFromName(const std::string& name);
void setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotspot);
void setXCursor(const std::string& name);
void setCursorFromName(const std::string& name);
void setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotspot);
void setXCursor(const std::string& name);
bool changeTheme(const std::string& name, const int size);
void updateTheme();
SCursorImageData dataFor(const std::string& name); // for xwayland
void setXWaylandCursor();
bool changeTheme(const std::string& name, const int size);
void updateTheme();
SCursorImageData dataFor(const std::string& name); // for xwayland
void setXWaylandCursor();
void tickAnimatedCursor();
void tickAnimatedCursor();
class CCursorBuffer {
class CCursorBuffer : public Aquamarine::IBuffer {
public:
CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot);
CCursorBuffer(uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot);
~CCursorBuffer();
struct SCursorWlrBuffer {
wlr_buffer base;
cairo_surface_t* surface = nullptr;
bool dropped = false;
CCursorBuffer* parent = nullptr;
uint8_t* pixelData = nullptr;
size_t stride = 0;
} wlrBuffer;
virtual Aquamarine::eBufferCapability caps();
virtual Aquamarine::eBufferType type();
virtual void update(const Hyprutils::Math::CRegion& damage);
virtual bool isSynchronous(); // whether the updates to this buffer are synchronous, aka happen over cpu
virtual bool good();
virtual Aquamarine::SSHMAttrs shm();
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
virtual void endDataPtr();
private:
Vector2D size;
Vector2D hotspot;
Vector2D hotspot;
cairo_surface_t* surface = nullptr;
uint8_t* pixelData = nullptr;
size_t stride = 0;
friend class CCursorManager;
};
@ -56,7 +60,7 @@ class CCursorManager {
bool m_bOurBufferConnected = false;
private:
std::vector<std::unique_ptr<CCursorBuffer>> m_vCursorBuffers;
std::vector<SP<CCursorBuffer>> m_vCursorBuffers;
std::unique_ptr<Hyprcursor::CHyprcursorManager> m_pHyprcursor;
@ -70,8 +74,24 @@ class CCursorManager {
int m_iCurrentAnimationFrame = 0;
Hyprcursor::SCursorShapeData m_sCurrentCursorShapeData;
// xcursor fallback
wlr_xcursor_manager* m_pWLRXCursorMgr = nullptr;
// gangsta bootleg XCursor impl. Whenever Hyprland has to use
// an xcursor, just use the pointer.
struct SXCursor {
Vector2D size;
Vector2D hotspot;
std::vector<uint32_t> pixels; // XPixel is a u32
};
struct SXCursorManager {
void loadTheme(const std::string& name, int size);
int lastLoadSize = 0;
bool themeLoaded = false;
std::string themeName = "";
SP<SXCursor> defaultCursor;
std::unordered_map<std::string, SP<SXCursor>> cursors;
} xcursor;
};
inline std::unique_ptr<CCursorManager> g_pCursorManager;

View file

@ -4,10 +4,12 @@
#include "../protocols/LayerShell.hpp"
#include "../protocols/ShortcutsInhibit.hpp"
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "../devices/IKeyboard.hpp"
#include "KeybindManager.hpp"
#include "PointerManager.hpp"
#include "Compositor.hpp"
#include "TokenManager.hpp"
#include "eventLoop/EventLoopManager.hpp"
#include "debug/Log.hpp"
#include "helpers/varlist/VarList.hpp"
@ -15,6 +17,7 @@
#include <iterator>
#include <string>
#include <string_view>
#include <cstring>
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
@ -157,37 +160,37 @@ uint32_t CKeybindManager::stringToModMask(std::string mods) {
uint32_t modMask = 0;
std::transform(mods.begin(), mods.end(), mods.begin(), ::toupper);
if (mods.contains("SHIFT"))
modMask |= WLR_MODIFIER_SHIFT;
modMask |= HL_MODIFIER_SHIFT;
if (mods.contains("CAPS"))
modMask |= WLR_MODIFIER_CAPS;
modMask |= HL_MODIFIER_CAPS;
if (mods.contains("CTRL") || mods.contains("CONTROL"))
modMask |= WLR_MODIFIER_CTRL;
modMask |= HL_MODIFIER_CTRL;
if (mods.contains("ALT") || mods.contains("MOD1"))
modMask |= WLR_MODIFIER_ALT;
modMask |= HL_MODIFIER_ALT;
if (mods.contains("MOD2"))
modMask |= WLR_MODIFIER_MOD2;
modMask |= HL_MODIFIER_MOD2;
if (mods.contains("MOD3"))
modMask |= WLR_MODIFIER_MOD3;
if (mods.contains("SUPER") || mods.contains("WIN") || mods.contains("LOGO") || mods.contains("MOD4"))
modMask |= WLR_MODIFIER_LOGO;
modMask |= HL_MODIFIER_MOD3;
if (mods.contains("SUPER") || mods.contains("WIN") || mods.contains("LOGO") || mods.contains("MOD4") || mods.contains("META"))
modMask |= HL_MODIFIER_META;
if (mods.contains("MOD5"))
modMask |= WLR_MODIFIER_MOD5;
modMask |= HL_MODIFIER_MOD5;
return modMask;
}
uint32_t CKeybindManager::keycodeToModifier(xkb_keycode_t keycode) {
switch (keycode - 8) {
case KEY_LEFTMETA: return WLR_MODIFIER_LOGO;
case KEY_RIGHTMETA: return WLR_MODIFIER_LOGO;
case KEY_LEFTSHIFT: return WLR_MODIFIER_SHIFT;
case KEY_RIGHTSHIFT: return WLR_MODIFIER_SHIFT;
case KEY_LEFTCTRL: return WLR_MODIFIER_CTRL;
case KEY_RIGHTCTRL: return WLR_MODIFIER_CTRL;
case KEY_LEFTALT: return WLR_MODIFIER_ALT;
case KEY_RIGHTALT: return WLR_MODIFIER_ALT;
case KEY_CAPSLOCK: return WLR_MODIFIER_CAPS;
case KEY_NUMLOCK: return WLR_MODIFIER_MOD2;
case KEY_LEFTMETA: return HL_MODIFIER_META;
case KEY_RIGHTMETA: return HL_MODIFIER_META;
case KEY_LEFTSHIFT: return HL_MODIFIER_SHIFT;
case KEY_RIGHTSHIFT: return HL_MODIFIER_SHIFT;
case KEY_LEFTCTRL: return HL_MODIFIER_CTRL;
case KEY_RIGHTCTRL: return HL_MODIFIER_CTRL;
case KEY_LEFTALT: return HL_MODIFIER_ALT;
case KEY_RIGHTALT: return HL_MODIFIER_ALT;
case KEY_CAPSLOCK: return HL_MODIFIER_CAPS;
case KEY_NUMLOCK: return HL_MODIFIER_MOD2;
default: return 0;
}
}
@ -366,8 +369,8 @@ bool CKeybindManager::onKeyEvent(std::any event, SP<IKeyboard> pKeyboard) {
const auto KEYCODE = e.keycode + 8; // Because to xkbcommon it's +8 from libinput
const xkb_keysym_t keysym = xkb_state_key_get_one_sym(pKeyboard->resolveBindsBySym ? pKeyboard->xkbTranslationState : m_pXKBTranslationState, KEYCODE);
const xkb_keysym_t internalKeysym = xkb_state_key_get_one_sym(pKeyboard->wlr()->xkb_state, KEYCODE);
const xkb_keysym_t keysym = xkb_state_key_get_one_sym(pKeyboard->resolveBindsBySym ? pKeyboard->xkbStaticState : m_pXKBTranslationState, KEYCODE);
const xkb_keysym_t internalKeysym = xkb_state_key_get_one_sym(pKeyboard->xkbState, KEYCODE);
if (handleInternalKeybinds(internalKeysym))
return true;
@ -554,7 +557,7 @@ 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_pSeatManager->keyboard->wlr()->repeat_info.rate);
wl_event_source_timer_update(g_pKeybindManager->m_pActiveKeybindEventSource, 1000 / g_pSeatManager->keyboard->repeatRate);
return 0;
}
@ -786,7 +789,7 @@ bool CKeybindManager::handleVT(xkb_keysym_t keysym) {
// beyond this point, return true to not handle anything else.
// we'll avoid printing shit to active windows.
if (g_pCompositor->m_sWLRSession) {
if (g_pCompositor->m_pAqBackend->hasSession()) {
const unsigned int TTY = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
// vtnr is bugged for some reason.
@ -810,8 +813,7 @@ bool CKeybindManager::handleVT(xkb_keysym_t keysym) {
Debug::log(LOG, "Switching from VT {} to VT {}", ttynum, TTY);
wlr_session_change_vt(g_pCompositor->m_sWLRSession, TTY);
return true;
g_pCompositor->m_pAqBackend->session->switchVT(TTY);
}
return true;
@ -893,6 +895,7 @@ uint64_t CKeybindManager::spawnRaw(std::string args) {
for (auto& e : HLENV) {
setenv(e.first.c_str(), e.second.c_str(), 1);
}
setenv("WAYLAND_DISPLAY", g_pCompositor->m_szWLDisplaySocket.c_str(), 1);
close(socket[0]);
close(socket[1]);
execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr);
@ -1659,7 +1662,7 @@ void CKeybindManager::renameWorkspace(std::string args) {
}
void CKeybindManager::exitHyprland(std::string argz) {
g_pCompositor->m_bExitTriggered = true;
g_pEventLoopManager->doLater([]() { g_pCompositor->cleanup(); });
}
void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) {
@ -2121,8 +2124,8 @@ void CKeybindManager::sendshortcut(std::string args) {
const auto KEYPAIRSTRING = std::format("{}{}", (uintptr_t)KB.get(), KEY);
if (!g_pKeybindManager->m_mKeyToCodeCache.contains(KEYPAIRSTRING)) {
xkb_keymap* km = KB->wlr()->keymap;
xkb_state* ks = KB->xkbTranslationState;
xkb_keymap* km = KB->xkbKeymap;
xkb_state* ks = KB->xkbState;
xkb_keycode_t keycode_min, keycode_max;
keycode_min = xkb_keymap_min_keycode(km);
@ -2259,7 +2262,7 @@ void CKeybindManager::dpms(std::string arg) {
if (!port.empty() && m->szName != port)
continue;
wlr_output_state_set_enabled(m->state.wlr(), enable);
m->output->state->setEnabled(enable);
m->dpmsStatus = enable;

View file

@ -6,6 +6,7 @@
#include "../Compositor.hpp"
#include <unordered_map>
#include <functional>
#include <xkbcommon/xkbcommon.h>
#include "../devices/IPointer.hpp"
class CInputManager;

View file

@ -6,133 +6,8 @@
#include "../protocols/core/Compositor.hpp"
#include "eventLoop/EventLoopManager.hpp"
#include "SeatManager.hpp"
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/interface.h>
#include <wlr/render/wlr_renderer.h>
// TODO: make nicer
// this will come with the eventual rewrite of wlr_drm, etc...
static bool wlr_drm_format_intersect(wlr_drm_format* dst, const wlr_drm_format* a, const wlr_drm_format* b) {
ASSERT(a->format == b->format);
size_t capacity = a->len < b->len ? a->len : b->len;
uint64_t* modifiers = (uint64_t*)malloc(sizeof(*modifiers) * capacity);
if (!modifiers)
return false;
struct wlr_drm_format fmt = {
.format = a->format,
.len = 0,
.capacity = capacity,
.modifiers = modifiers,
};
for (size_t i = 0; i < a->len; i++) {
for (size_t j = 0; j < b->len; j++) {
if (a->modifiers[i] == b->modifiers[j]) {
ASSERT(fmt.len < fmt.capacity);
fmt.modifiers[fmt.len++] = a->modifiers[i];
break;
}
}
}
wlr_drm_format_finish(dst);
*dst = fmt;
return true;
}
static bool wlr_drm_format_copy(wlr_drm_format* dst, const wlr_drm_format* src) {
ASSERT(src->len <= src->capacity);
uint64_t* modifiers = (uint64_t*)malloc(sizeof(*modifiers) * src->len);
if (!modifiers)
return false;
memcpy(modifiers, src->modifiers, sizeof(*modifiers) * src->len);
wlr_drm_format_finish(dst);
dst->capacity = src->len;
dst->len = src->len;
dst->format = src->format;
dst->modifiers = modifiers;
return true;
}
static const wlr_drm_format_set* wlr_renderer_get_render_formats(wlr_renderer* r) {
if (!r->impl->get_render_formats)
return nullptr;
return r->impl->get_render_formats(r);
}
static bool output_pick_format(wlr_output* output, const wlr_drm_format_set* display_formats, wlr_drm_format* format, uint32_t fmt) {
const wlr_drm_format_set* render_formats = wlr_renderer_get_render_formats(g_pCompositor->m_sWLRRenderer);
if (render_formats == NULL) {
wlr_log(WLR_ERROR, "Failed to get render formats");
return false;
}
const wlr_drm_format* render_format = wlr_drm_format_set_get(render_formats, fmt);
if (render_format == NULL) {
wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%" PRIX32, fmt);
return false;
}
if (display_formats != NULL) {
const wlr_drm_format* display_format = wlr_drm_format_set_get(display_formats, fmt);
if (display_format == NULL) {
wlr_log(WLR_DEBUG, "Output doesn't support format 0x%" PRIX32, fmt);
return false;
}
if (!wlr_drm_format_intersect(format, display_format, render_format)) {
wlr_log(WLR_DEBUG,
"Failed to intersect display and render "
"modifiers for format 0x%" PRIX32 " on output %s",
fmt, output->name);
return false;
}
} else {
// The output can display any format
if (!wlr_drm_format_copy(format, render_format))
return false;
}
if (format->len == 0) {
wlr_drm_format_finish(format);
wlr_log(WLR_DEBUG, "Failed to pick output format");
return false;
}
return true;
}
static bool output_pick_cursor_format(struct wlr_output* output, struct wlr_drm_format* format) {
struct wlr_allocator* allocator = output->allocator;
ASSERT(allocator != NULL);
const struct wlr_drm_format_set* display_formats = NULL;
if (output->impl->get_cursor_formats) {
display_formats = output->impl->get_cursor_formats(output, allocator->buffer_caps);
if (display_formats == NULL) {
wlr_log(WLR_DEBUG, "Failed to get cursor display formats");
return false;
}
}
// Note: taken from https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4596/diffs#diff-content-e3ea164da86650995728d70bd118f6aa8c386797
// If this fails to find a shared modifier try to use a linear
// modifier. This avoids a scenario where the hardware cannot render to
// linear textures but only linear textures are supported for cursors,
// as is the case with Nvidia and VmWare GPUs
if (!output_pick_format(output, display_formats, format, DRM_FORMAT_ARGB8888)) {
// Clear the format as output_pick_format doesn't zero it
memset(format, 0, sizeof(*format));
return output_pick_format(output, NULL, format, DRM_FORMAT_ARGB8888);
}
return true;
}
#include <cstring>
#include <gbm.h>
CPointerManager::CPointerManager() {
hooks.monitorAdded = g_pHookSystem->hookDynamic("newMonitor", [this](void* self, SCallbackInfo& info, std::any data) {
@ -201,6 +76,11 @@ void CPointerManager::unlockSoftwareForMonitor(SP<CMonitor> mon) {
updateCursorBackend();
}
bool CPointerManager::softwareLockedFor(SP<CMonitor> mon) {
auto state = stateFor(mon);
return state->softwareLocks > 0 || state->hardwareFailed;
}
Vector2D CPointerManager::position() {
return pointerPos;
}
@ -216,7 +96,7 @@ SP<CPointerManager::SMonitorPointerState> CPointerManager::stateFor(SP<CMonitor>
return *it;
}
void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale) {
void CPointerManager::setCursorBuffer(SP<Aquamarine::IBuffer> buf, const Vector2D& hotspot, const float& scale) {
damageIfSoftware();
if (buf == currentCursorImage.pBuffer) {
if (hotspot != currentCursorImage.hotspot || scale != currentCursorImage.scale) {
@ -232,10 +112,8 @@ void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot,
resetCursorImage(false);
if (buf) {
currentCursorImage.size = {buf->width, buf->height};
currentCursorImage.pBuffer = wlr_buffer_lock(buf);
currentCursorImage.hyprListener_destroyBuffer.initCallback(&buf->events.destroy, [this](void* owner, void* data) { resetCursorImage(); }, this, "CPointerManager");
currentCursorImage.size = buf->size;
currentCursorImage.pBuffer = buf;
}
currentCursorImage.hotspot = hotspot;
@ -317,8 +195,8 @@ void CPointerManager::recheckEnteredOutputs() {
// if we are using hw cursors, prevent
// the cursor from being stuck at the last point.
// if we are leaving it, move it to narnia.
if (!s->hardwareFailed && s->monitor->output->impl->move_cursor)
s->monitor->output->impl->move_cursor(s->monitor->output, -1337, -420);
if (!s->hardwareFailed && (s->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER))
s->monitor->output->moveCursor({-1337, -420});
if (!currentCursorImage.surface)
continue;
@ -339,16 +217,11 @@ void CPointerManager::resetCursorImage(bool apply) {
currentCursorImage.destroySurface.reset();
currentCursorImage.commitSurface.reset();
currentCursorImage.surface.reset();
} else if (currentCursorImage.pBuffer) {
wlr_buffer_unlock(currentCursorImage.pBuffer);
currentCursorImage.hyprListener_destroyBuffer.removeCallback();
} else if (currentCursorImage.pBuffer)
currentCursorImage.pBuffer = nullptr;
}
if (currentCursorImage.pBufferTexture) {
wlr_texture_destroy(currentCursorImage.pBufferTexture);
currentCursorImage.pBufferTexture = nullptr;
}
if (currentCursorImage.bufferTex)
currentCursorImage.bufferTex = nullptr;
currentCursorImage.scale = 1.F;
currentCursorImage.hotspot = {0, 0};
@ -370,9 +243,8 @@ void CPointerManager::resetCursorImage(bool apply) {
}
if (ms->cursorFrontBuffer) {
if (ms->monitor->output->impl->set_cursor)
ms->monitor->output->impl->set_cursor(ms->monitor->output, nullptr, 0, 0);
wlr_buffer_unlock(ms->cursorFrontBuffer);
if (ms->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER)
ms->monitor->output->setCursor(nullptr, {});
ms->cursorFrontBuffer = nullptr;
}
}
@ -418,18 +290,18 @@ void CPointerManager::onCursorMoved() {
continue;
const auto CURSORPOS = getCursorPosForMonitor(m);
m->output->impl->move_cursor(m->output, CURSORPOS.x, CURSORPOS.y);
m->output->moveCursor(CURSORPOS);
}
}
bool CPointerManager::attemptHardwareCursor(SP<CPointerManager::SMonitorPointerState> state) {
auto output = state->monitor->output;
if (!output->impl->set_cursor)
if (!(output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER))
return false;
const auto CURSORPOS = getCursorPosForMonitor(state->monitor.lock());
state->monitor->output->impl->move_cursor(state->monitor->output, CURSORPOS.x, CURSORPOS.y);
state->monitor->output->moveCursor(CURSORPOS);
auto texture = getCurrentCursorTexture();
@ -459,64 +331,62 @@ bool CPointerManager::attemptHardwareCursor(SP<CPointerManager::SMonitorPointerS
return success;
}
bool CPointerManager::setHWCursorBuffer(SP<SMonitorPointerState> state, wlr_buffer* buf) {
if (!state->monitor->output->impl->set_cursor)
bool CPointerManager::setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquamarine::IBuffer> buf) {
if (!(state->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER))
return false;
const auto HOTSPOT = transformedHotspot(state->monitor.lock());
Debug::log(TRACE, "[pointer] hw transformed hotspot for {}: {}", state->monitor->szName, HOTSPOT);
if (!state->monitor->output->impl->set_cursor(state->monitor->output, buf, HOTSPOT.x, HOTSPOT.y))
if (!state->monitor->output->setCursor(buf, HOTSPOT))
return false;
wlr_buffer_unlock(state->cursorFrontBuffer);
state->cursorFrontBuffer = buf;
g_pCompositor->scheduleFrameForMonitor(state->monitor.get());
if (buf)
wlr_buffer_lock(buf);
g_pCompositor->scheduleFrameForMonitor(state->monitor.get(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE);
return true;
}
wlr_buffer* CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) {
SP<Aquamarine::IBuffer> CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPointerState> state, SP<CTexture> texture) {
auto output = state->monitor->output;
int w = currentCursorImage.size.x, h = currentCursorImage.size.y;
if (output->impl->get_cursor_size) {
output->impl->get_cursor_size(output, &w, &h);
auto maxSize = output->cursorPlaneSize();
auto cursorSize = currentCursorImage.size;
if (w < currentCursorImage.size.x || h < currentCursorImage.size.y) {
Debug::log(TRACE, "hardware cursor too big! {} > {}x{}", currentCursorImage.size, w, h);
return nullptr;
}
}
if (w <= 0 || h <= 0) {
Debug::log(TRACE, "hw cursor for output {} failed the size checks ({}x{} is invalid)", state->monitor->szName, w, h);
if (maxSize == Vector2D{})
return nullptr;
}
if (!output->cursor_swapchain || Vector2D{w, h} != Vector2D{output->cursor_swapchain->width, output->cursor_swapchain->height}) {
wlr_drm_format fmt = {0};
if (!output_pick_cursor_format(output, &fmt)) {
Debug::log(TRACE, "Failed to pick cursor format");
if (maxSize != Vector2D{-1, -1}) {
if (cursorSize.x > maxSize.x || cursorSize.y > maxSize.y) {
Debug::log(TRACE, "hardware cursor too big! {} > {}", currentCursorImage.size, maxSize);
return nullptr;
}
} else
maxSize = cursorSize;
wlr_swapchain_destroy(output->cursor_swapchain);
output->cursor_swapchain = wlr_swapchain_create(output->allocator, w, h, &fmt);
wlr_drm_format_finish(&fmt);
if (!state->monitor->cursorSwapchain || maxSize != state->monitor->cursorSwapchain->currentOptions().size) {
if (!output->cursor_swapchain) {
Debug::log(TRACE, "Failed to create cursor swapchain");
if (!state->monitor->cursorSwapchain)
state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(state->monitor->output->getBackend()->preferredAllocator(), state->monitor->output->getBackend());
auto options = state->monitor->cursorSwapchain->currentOptions();
options.size = maxSize;
options.length = 2;
options.scanout = true;
options.cursor = true;
options.multigpu = state->monitor->output->getBackend()->preferredAllocator()->drmFD() != g_pCompositor->m_iDRMFD;
// We do not set the format. If it's unset (DRM_FORMAT_INVALID) then the swapchain will pick for us,
// but if it's set, we don't wanna change it.
if (!state->monitor->cursorSwapchain->reconfigure(options)) {
Debug::log(TRACE, "Failed to reconfigure cursor swapchain");
return nullptr;
}
}
wlr_buffer* buf = wlr_swapchain_acquire(output->cursor_swapchain, nullptr);
auto buf = state->monitor->cursorSwapchain->next(nullptr);
if (!buf) {
Debug::log(TRACE, "Failed to acquire a buffer from the cursor swapchain");
return nullptr;
@ -525,16 +395,45 @@ wlr_buffer* CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPo
CRegion damage = {0, 0, INT16_MAX, INT16_MAX};
g_pHyprRenderer->makeEGLCurrent();
g_pHyprOpenGL->m_RenderData.pMonitor = state->monitor.get(); // has to be set cuz allocs
g_pHyprOpenGL->m_RenderData.pMonitor = state->monitor.get();
auto RBO = g_pHyprRenderer->getOrCreateRenderbuffer(buf, state->monitor->cursorSwapchain->currentOptions().format);
if (!RBO) {
Debug::log(TRACE, "Failed to create cursor RB with format {}, mod {}", buf->dmabuf().format, buf->dmabuf().modifier);
static auto PDUMB = CConfigValue<Hyprlang::INT>("cursor:allow_dumb_copy");
if (!*PDUMB)
return nullptr;
auto bufData = buf->beginDataPtr(0);
auto bufPtr = std::get<0>(bufData);
// clear buffer
memset(bufPtr, 0, std::get<2>(bufData));
auto texBuffer = currentCursorImage.pBuffer ? currentCursorImage.pBuffer : currentCursorImage.surface->resource()->current.buffer;
if (texBuffer) {
auto textAttrs = texBuffer->shm();
auto texData = texBuffer->beginDataPtr(GBM_BO_TRANSFER_WRITE);
auto texPtr = std::get<0>(texData);
Debug::log(TRACE, "cursor texture {}x{} {} {} {}", textAttrs.size.x, textAttrs.size.y, (void*)texPtr, textAttrs.format, textAttrs.stride);
// copy cursor texture
for (int i = 0; i < texBuffer->shm().size.y; i++)
memcpy(bufPtr + i * buf->dmabuf().strides[0], texPtr + i * textAttrs.stride, textAttrs.stride);
}
buf->endDataPtr();
return buf;
}
const auto RBO = g_pHyprRenderer->getOrCreateRenderbuffer(buf, DRM_FORMAT_ARGB8888);
RBO->bind();
g_pHyprOpenGL->beginSimple(state->monitor.get(), damage, RBO);
g_pHyprOpenGL->clear(CColor{0.F, 0.F, 0.F, 0.F});
CBox xbox = {{}, Vector2D{currentCursorImage.size / currentCursorImage.scale * state->monitor->scale}.round()};
Debug::log(TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->szName, currentCursorImage.size, Vector2D{w, h},
Debug::log(TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->szName, currentCursorImage.size, cursorSize,
currentCursorImage.scale, state->monitor->scale, xbox.size());
g_pHyprOpenGL->renderTexture(texture, &xbox, 1.F);
@ -543,9 +442,7 @@ wlr_buffer* CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPo
glFlush();
g_pHyprOpenGL->m_RenderData.pMonitor = nullptr;
g_pHyprRenderer->onRenderbufferDestroy(RBO);
wlr_buffer_unlock(buf);
g_pHyprRenderer->onRenderbufferDestroy(RBO.get());
return buf;
}
@ -579,7 +476,7 @@ void CPointerManager::renderSoftwareCursorsFor(SP<CMonitor> pMonitor, timespec*
box.x = std::round(box.x);
box.y = std::round(box.y);
g_pHyprOpenGL->renderTextureWithDamage(texture, &box, &damage, 1.F);
g_pHyprOpenGL->renderTextureWithDamage(texture, &box, &damage, 1.F, 0, false, false, currentCursorImage.waitTimeline, currentCursorImage.waitPoint);
if (currentCursorImage.surface)
currentCursorImage.surface->resource()->frame(now);
@ -587,17 +484,19 @@ void CPointerManager::renderSoftwareCursorsFor(SP<CMonitor> pMonitor, timespec*
Vector2D CPointerManager::getCursorPosForMonitor(SP<CMonitor> pMonitor) {
return CBox{pointerPos - pMonitor->vecPosition, {0, 0}}
//.transform(pMonitor->transform, pMonitor->vecTransformedSize.x / pMonitor->scale, pMonitor->vecTransformedSize.y / pMonitor->scale)
.transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->vecTransformedSize.x / pMonitor->scale,
pMonitor->vecTransformedSize.y / pMonitor->scale)
.pos() *
pMonitor->scale;
}
Vector2D CPointerManager::transformedHotspot(SP<CMonitor> pMonitor) {
if (!pMonitor->output->cursor_swapchain)
if (!pMonitor->cursorSwapchain)
return {}; // doesn't matter, we have no hw cursor, and this is only for hw cursors
return CBox{currentCursorImage.hotspot * pMonitor->scale, {0, 0}}
.transform(wlTransformToHyprutils(wlr_output_transform_invert(pMonitor->transform)), pMonitor->output->cursor_swapchain->width, pMonitor->output->cursor_swapchain->height)
.transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->cursorSwapchain->currentOptions().size.x,
pMonitor->cursorSwapchain->currentOptions().size.y)
.pos();
}
@ -799,10 +698,8 @@ SP<CTexture> CPointerManager::getCurrentCursorTexture() {
return nullptr;
if (currentCursorImage.pBuffer) {
if (!currentCursorImage.pBufferTexture) {
currentCursorImage.pBufferTexture = wlr_texture_from_buffer(g_pCompositor->m_sWLRRenderer, currentCursorImage.pBuffer);
currentCursorImage.bufferTex = makeShared<CTexture>(currentCursorImage.pBufferTexture);
}
if (!currentCursorImage.bufferTex)
currentCursorImage.bufferTex = makeShared<CTexture>(currentCursorImage.pBuffer);
return currentCursorImage.bufferTex;
}

View file

@ -6,13 +6,15 @@
#include "../helpers/math/Math.hpp"
#include "../helpers/math/Math.hpp"
#include "../desktop/WLSurface.hpp"
#include "../helpers/sync/SyncTimeline.hpp"
#include <tuple>
class CMonitor;
struct wlr_input_device;
class IHID;
class CTexture;
AQUAMARINE_FORWARD(IBuffer);
/*
The naming here is a bit confusing.
CPointerManager manages the _position_ and _displaying_ of the cursor,
@ -37,7 +39,7 @@ class CPointerManager {
void move(const Vector2D& deltaLogical);
void warpAbsolute(Vector2D abs, SP<IHID> dev);
void setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale);
void setCursorBuffer(SP<Aquamarine::IBuffer> buf, const Vector2D& hotspot, const float& scale);
void setCursorSurface(SP<CWLSurface> buf, const Vector2D& hotspot);
void resetCursorImage(bool apply = true);
@ -47,6 +49,7 @@ class CPointerManager {
void unlockSoftwareForMonitor(CMonitor* pMonitor);
void lockSoftwareAll();
void unlockSoftwareAll();
bool softwareLockedFor(SP<CMonitor> pMonitor);
void renderSoftwareCursorsFor(SP<CMonitor> pMonitor, timespec* now, CRegion& damage /* logical */, std::optional<Vector2D> overridePos = {} /* monitor-local */);
@ -135,45 +138,42 @@ class CPointerManager {
} currentMonitorLayout;
struct {
wlr_buffer* pBuffer = nullptr;
SP<CTexture> bufferTex;
WP<CWLSurface> surface;
wlr_texture* pBufferTexture = nullptr;
SP<Aquamarine::IBuffer> pBuffer;
SP<CTexture> bufferTex;
WP<CWLSurface> surface;
Vector2D hotspot;
Vector2D size;
float scale = 1.F;
Vector2D hotspot;
Vector2D size;
float scale = 1.F;
CHyprSignalListener destroySurface;
CHyprSignalListener commitSurface;
DYNLISTENER(destroyBuffer);
CHyprSignalListener destroySurface;
CHyprSignalListener commitSurface;
SP<CSyncTimeline> waitTimeline = nullptr;
uint64_t waitPoint = 0;
} currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors
Vector2D pointerPos = {0, 0};
struct SMonitorPointerState {
SMonitorPointerState(SP<CMonitor> m) : monitor(m) {}
~SMonitorPointerState() {
if (cursorFrontBuffer)
wlr_buffer_unlock(cursorFrontBuffer);
}
~SMonitorPointerState() {}
WP<CMonitor> monitor;
WP<CMonitor> monitor;
int softwareLocks = 0;
bool hardwareFailed = false;
CBox box; // logical
bool entered = false;
bool hwApplied = false;
int softwareLocks = 0;
bool hardwareFailed = false;
CBox box; // logical
bool entered = false;
bool hwApplied = false;
wlr_buffer* cursorFrontBuffer = nullptr;
SP<Aquamarine::IBuffer> cursorFrontBuffer;
};
std::vector<SP<SMonitorPointerState>> monitorStates;
SP<SMonitorPointerState> stateFor(SP<CMonitor> mon);
bool attemptHardwareCursor(SP<SMonitorPointerState> state);
wlr_buffer* renderHWCursorBuffer(SP<SMonitorPointerState> state, SP<CTexture> texture);
bool setHWCursorBuffer(SP<SMonitorPointerState> state, wlr_buffer* buf);
SP<Aquamarine::IBuffer> renderHWCursorBuffer(SP<SMonitorPointerState> state, SP<CTexture> texture);
bool setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquamarine::IBuffer> buf);
struct {
SP<HOOK_CALLBACK_FN> monitorAdded;

View file

@ -1,5 +1,7 @@
#include "ProtocolManager.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/TearingControl.hpp"
#include "../protocols/FractionalScale.hpp"
#include "../protocols/XDGOutput.hpp"
@ -35,6 +37,8 @@
#include "../protocols/Viewporter.hpp"
#include "../protocols/MesaDRM.hpp"
#include "../protocols/LinuxDMABUF.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/core/Seat.hpp"
#include "../protocols/core/DataDevice.hpp"
@ -45,6 +49,10 @@
#include "../helpers/Monitor.hpp"
#include "../render/Renderer.hpp"
#include "../Compositor.hpp"
#include <aquamarine/buffer/Buffer.hpp>
#include <aquamarine/backend/Backend.hpp>
void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) {
const bool ISMIRROR = pMonitor->isMirror();
@ -65,6 +73,8 @@ void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) {
CProtocolManager::CProtocolManager() {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("experimental:explicit_sync");
// Outputs are a bit dumb, we have to agree.
static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
auto M = std::any_cast<CMonitor*>(param);
@ -131,6 +141,16 @@ CProtocolManager::CProtocolManager() {
PROTO::primarySelection = std::make_unique<CPrimarySelectionProtocol>(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection");
PROTO::xwaylandShell = std::make_unique<CXWaylandShellProtocol>(&xwayland_shell_v1_interface, 1, "XWaylandShell");
for (auto& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
continue;
PROTO::lease = std::make_unique<CDRMLeaseProtocol>(&wp_drm_lease_device_v1_interface, 1, "DRMLease");
if (*PENABLEEXPLICIT)
PROTO::sync = std::make_unique<CDRMSyncobjProtocol>(&wp_linux_drm_syncobj_manager_v1_interface, 1, "DRMSyncobj");
break;
}
if (g_pHyprOpenGL->getDRMFormats().size() > 0) {
PROTO::mesaDRM = std::make_unique<CMesaDRMProtocol>(&wl_drm_interface, 2, "MesaDRM");
PROTO::linuxDma = std::make_unique<CLinuxDMABufV1Protocol>(&zwp_linux_dmabuf_v1_interface, 5, "LinuxDMABUF");

View file

@ -94,8 +94,8 @@ void CSeatManager::setKeyboard(SP<IKeyboard> KEEB) {
}
void CSeatManager::updateActiveKeyboardData() {
if (keyboard && keyboard->wlr())
PROTO::seat->updateRepeatInfo(keyboard->wlr()->repeat_info.rate, keyboard->wlr()->repeat_info.delay);
if (keyboard)
PROTO::seat->updateRepeatInfo(keyboard->repeatRate, keyboard->repeatDelay);
PROTO::seat->updateKeymap();
}
@ -103,7 +103,7 @@ void CSeatManager::setKeyboardFocus(SP<CWLSurfaceResource> surf) {
if (state.keyboardFocus == surf)
return;
if (!keyboard || !keyboard->wlr()) {
if (!keyboard) {
Debug::log(ERR, "BUG THIS: setKeyboardFocus without a valid keyboard set");
return;
}
@ -144,7 +144,7 @@ void CSeatManager::setKeyboardFocus(SP<CWLSurfaceResource> surf) {
continue;
k->sendEnter(surf);
k->sendMods(keyboard->wlr()->modifiers.depressed, keyboard->wlr()->modifiers.latched, keyboard->wlr()->modifiers.locked, keyboard->wlr()->modifiers.group);
k->sendMods(keyboard->modifiersState.depressed, keyboard->modifiersState.latched, keyboard->modifiersState.locked, keyboard->modifiersState.group);
}
}
@ -196,7 +196,7 @@ void CSeatManager::setPointerFocus(SP<CWLSurfaceResource> surf, const Vector2D&
return;
}
if (!mouse || !mouse->wlr()) {
if (!mouse) {
Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set");
return;
}
@ -333,9 +333,7 @@ void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double
if (source == 0) {
p->sendAxisValue120(axis, value120);
p->sendAxisDiscrete(axis, discrete);
}
if (value == 0)
} else if (value == 0)
p->sendAxisStop(timeMs, axis);
}
}

View file

@ -76,13 +76,21 @@ void CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow, CBox* pbox) {
const auto SIZEHINTS = pWindow->m_pXWaylandSurface->sizeHints.get();
if (SIZEHINTS && pWindow->m_iX11Type != 2) {
pbox->x = SIZEHINTS->x;
pbox->y = SIZEHINTS->y;
pbox->width = SIZEHINTS->width;
pbox->height = SIZEHINTS->height;
} else {
// WM_SIZE_HINTS' x,y,w,h is deprecated it seems.
// Source: https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#wm_normal_hints_property
pbox->x = pWindow->m_pXWaylandSurface->geometry.x;
pbox->y = pWindow->m_pXWaylandSurface->geometry.y;
if ((SIZEHINTS->flags & 0x2 /* ICCCM USSize */) || (SIZEHINTS->flags & 0x8 /* ICCCM PSize */)) {
pbox->w = SIZEHINTS->base_width;
pbox->h = SIZEHINTS->base_height;
} else {
pbox->w = pWindow->m_pXWaylandSurface->geometry.w;
pbox->h = pWindow->m_pXWaylandSurface->geometry.h;
}
} else
*pbox = pWindow->m_pXWaylandSurface->geometry;
}
} else if (pWindow->m_pXDGSurface)
*pbox = pWindow->m_pXDGSurface->current.geometry;
}

View file

@ -1,5 +1,6 @@
#include "EventLoopManager.hpp"
#include "../../debug/Log.hpp"
#include "../../Compositor.hpp"
#include <algorithm>
#include <limits>
@ -7,6 +8,8 @@
#include <sys/timerfd.h>
#include <time.h>
#include <aquamarine/backend/Backend.hpp>
#define TIMESPEC_NSEC_PER_SEC 1000000000L
CEventLoopManager::CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop) {
@ -25,9 +28,21 @@ static int timerWrite(int fd, uint32_t mask, void* data) {
return 1;
}
static int aquamarineFDWrite(int fd, uint32_t mask, void* data) {
auto POLLFD = (Aquamarine::SPollFD*)data;
POLLFD->onSignal();
return 1;
}
void CEventLoopManager::enterLoop() {
m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd, WL_EVENT_READABLE, timerWrite, nullptr);
aqPollFDs = g_pCompositor->m_pAqBackend->getPollFDs();
for (auto& fd : aqPollFDs) {
m_sWayland.aqEventSources.emplace_back(wl_event_loop_add_fd(m_sWayland.loop, fd->fd, WL_EVENT_READABLE, aquamarineFDWrite, fd.get()));
fd->onSignal(); // dispatch outstanding
}
wl_display_run(m_sWayland.display);
Debug::log(LOG, "Kicked off the event loop! :(");

View file

@ -7,6 +7,10 @@
#include "EventLoopTimer.hpp"
namespace Aquamarine {
struct SPollFD;
};
class CEventLoopManager {
public:
CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop);
@ -33,9 +37,10 @@ class CEventLoopManager {
private:
struct {
wl_event_loop* loop = nullptr;
wl_display* display = nullptr;
wl_event_source* eventSource = nullptr;
wl_event_loop* loop = nullptr;
wl_display* display = nullptr;
wl_event_source* eventSource = nullptr;
std::vector<wl_event_source*> aqEventSources;
} m_sWayland;
struct {
@ -43,7 +48,10 @@ class CEventLoopManager {
int timerfd = -1;
} m_sTimers;
SIdleData m_sIdle;
SIdleData m_sIdle;
std::vector<SP<Aquamarine::SPollFD>> aqPollFDs;
friend class CSyncTimeline;
};
inline std::unique_ptr<CEventLoopManager> g_pEventLoopManager;

View file

@ -1,6 +1,6 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "wlr/types/wlr_switch.h"
#include <aquamarine/output/Output.hpp>
#include <cstdint>
#include <ranges>
#include "../../config/ConfigValue.hpp"
@ -28,6 +28,8 @@
#include "../../managers/PointerManager.hpp"
#include "../../managers/SeatManager.hpp"
#include <aquamarine/input/Input.hpp>
CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
if (!cursorImageUnlocked())
@ -189,8 +191,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
bool skipFrameSchedule = PMONITOR->shouldSkipScheduleFrameOnMouseEvent();
if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && PMONITOR->output->software_cursor_locks > 0 && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && g_pPointerManager->softwareLockedFor(PMONITOR->self.lock()) && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE);
PHLWINDOW forcedFocus = m_pForcedFocus.lock();
@ -372,8 +374,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
foundSurface =
g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface);
if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0 && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get());
if (g_pPointerManager->softwareLockedFor(PMONITOR->self.lock()) > 0 && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE);
// grabs
if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(foundSurface)) {
@ -760,6 +762,7 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) {
static auto POFFWINDOWAXIS = CConfigValue<Hyprlang::INT>("input:off_window_axis_events");
static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor");
static auto PTOUCHPADSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:touchpad:scroll_factor");
static auto PEMULATEDISCRETE = CConfigValue<Hyprlang::INT>("input:emulate_discrete_scroll");
auto factor = (*PTOUCHPADSCROLLFACTOR <= 0.f || e.source == WL_POINTER_AXIS_SOURCE_FINGER ? *PTOUCHPADSCROLLFACTOR : *PINPUTSCROLLFACTOR);
@ -798,17 +801,52 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) {
}
}
}
double deltaDiscrete = (e.deltaDiscrete != 0) ? (factor * e.deltaDiscrete / std::abs(e.deltaDiscrete)) : 0;
g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, factor * e.delta, deltaDiscrete > 0 ? std::ceil(deltaDiscrete) : std::floor(deltaDiscrete),
std::round(factor * e.deltaDiscrete), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL);
double discrete = (e.deltaDiscrete != 0) ? (factor * e.deltaDiscrete / std::abs(e.deltaDiscrete)) : 0;
double delta = e.delta * factor;
if (e.source == 0) {
// if an application supports v120, it should ignore discrete anyways
if ((*PEMULATEDISCRETE >= 1 && std::abs(e.deltaDiscrete) != 120) || *PEMULATEDISCRETE >= 2) {
const int interval = factor != 0 ? std::round(120 * (1 / factor)) : 120;
// reset the accumulator when timeout is reached or direction/axis has changed
if (std::signbit(e.deltaDiscrete) != m_ScrollWheelState.lastEventSign || e.axis != m_ScrollWheelState.lastEventAxis ||
e.timeMs - m_ScrollWheelState.lastEventTime > 500 /* 500ms taken from libinput default timeout */) {
m_ScrollWheelState.accumulatedScroll = 0;
// send 1 discrete on first event for responsiveness
discrete = std::copysign(1, e.deltaDiscrete);
} else
discrete = 0;
for (int ac = m_ScrollWheelState.accumulatedScroll; ac >= interval; ac -= interval) {
discrete += std::copysign(1, e.deltaDiscrete);
m_ScrollWheelState.accumulatedScroll -= interval;
}
m_ScrollWheelState.lastEventSign = std::signbit(e.deltaDiscrete);
m_ScrollWheelState.lastEventAxis = e.axis;
m_ScrollWheelState.lastEventTime = e.timeMs;
m_ScrollWheelState.accumulatedScroll += std::abs(e.deltaDiscrete);
delta = 15.0 * discrete * factor;
}
}
int32_t value120 = std::round(factor * e.deltaDiscrete);
int32_t deltaDiscrete = std::abs(discrete) != 0 && std::abs(discrete) < 1 ? std::copysign(1, discrete) : std::round(discrete);
g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, delta, deltaDiscrete, value120, e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL);
}
Vector2D CInputManager::getMouseCoordsInternal() {
return g_pPointerManager->position();
}
void CInputManager::newKeyboard(wlr_input_device* keyboard) {
const auto PNEWKEYBOARD = m_vKeyboards.emplace_back(CKeyboard::create(wlr_keyboard_from_input_device(keyboard)));
void CInputManager::newKeyboard(SP<Aquamarine::IKeyboard> keyboard) {
const auto PNEWKEYBOARD = m_vKeyboards.emplace_back(CKeyboard::create(keyboard));
setupKeyboard(PNEWKEYBOARD);
@ -820,14 +858,14 @@ void CInputManager::newVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keyboard)
setupKeyboard(PNEWKEYBOARD);
Debug::log(LOG, "New virtual keyboard created, pointers Hypr: {:x} and WLR: {:x}", (uintptr_t)PNEWKEYBOARD.get(), (uintptr_t)keyboard->wlr());
Debug::log(LOG, "New virtual keyboard created at {:x}", (uintptr_t)PNEWKEYBOARD.get());
}
void CInputManager::setupKeyboard(SP<IKeyboard> keeb) {
m_vHIDs.push_back(keeb);
try {
keeb->hlName = getNameForNewDevice(keeb->wlr()->base.name);
keeb->hlName = getNameForNewDevice(keeb->deviceName);
} catch (std::exception& e) {
Debug::log(ERR, "Keyboard had no name???"); // logic error
}
@ -926,83 +964,12 @@ void CInputManager::applyConfigToKeyboard(SP<IKeyboard> pKeyboard) {
// we can ignore those and just apply
}
wlr_keyboard_set_repeat_info(pKeyboard->wlr(), std::max(0, REPEATRATE), std::max(0, REPEATDELAY));
pKeyboard->repeatDelay = REPEATDELAY;
pKeyboard->repeatRate = REPEATRATE;
pKeyboard->repeatRate = std::max(0, REPEATRATE);
pKeyboard->repeatDelay = std::max(0, REPEATDELAY);
pKeyboard->numlockOn = NUMLOCKON;
pKeyboard->xkbFilePath = FILEPATH;
xkb_rule_names rules = {.rules = RULES.c_str(), .model = MODEL.c_str(), .layout = LAYOUT.c_str(), .variant = VARIANT.c_str(), .options = OPTIONS.c_str()};
pKeyboard->currentRules.rules = RULES;
pKeyboard->currentRules.model = MODEL;
pKeyboard->currentRules.variant = VARIANT;
pKeyboard->currentRules.options = OPTIONS;
pKeyboard->currentRules.layout = LAYOUT;
const auto CONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!CONTEXT) {
Debug::log(ERR, "applyConfigToKeyboard: CONTEXT null??");
return;
}
Debug::log(LOG, "Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {})", rules.layout, rules.variant, rules.rules, rules.model,
rules.options);
xkb_keymap* KEYMAP = NULL;
if (!FILEPATH.empty()) {
auto path = absolutePath(FILEPATH, g_pConfigManager->configCurrentPath);
if (FILE* const KEYMAPFILE = fopen(path.c_str(), "r"); !KEYMAPFILE)
Debug::log(ERR, "Cannot open input:kb_file= file for reading");
else {
KEYMAP = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
fclose(KEYMAPFILE);
}
}
if (!KEYMAP)
KEYMAP = xkb_keymap_new_from_names(CONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!KEYMAP) {
g_pConfigManager->addParseError("Invalid keyboard layout passed. ( rules: " + RULES + ", model: " + MODEL + ", variant: " + VARIANT + ", options: " + OPTIONS +
", layout: " + LAYOUT + " )");
Debug::log(ERR, "Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, rules.variant, rules.rules, rules.model,
rules.options);
memset(&rules, 0, sizeof(rules));
pKeyboard->currentRules.rules = "";
pKeyboard->currentRules.model = "";
pKeyboard->currentRules.variant = "";
pKeyboard->currentRules.options = "";
pKeyboard->currentRules.layout = "us";
KEYMAP = xkb_keymap_new_from_names(CONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
}
wlr_keyboard_set_keymap(pKeyboard->wlr(), KEYMAP);
pKeyboard->updateXKBTranslationState();
wlr_keyboard_modifiers wlrMods = {0};
if (NUMLOCKON == 1) {
// lock numlock
const auto IDX = xkb_map_mod_get_index(KEYMAP, XKB_MOD_NAME_NUM);
if (IDX != XKB_MOD_INVALID)
wlrMods.locked |= (uint32_t)1 << IDX;
}
if (wlrMods.locked != 0)
wlr_keyboard_notify_modifiers(pKeyboard->wlr(), 0, 0, wlrMods.locked, 0);
xkb_keymap_unref(KEYMAP);
xkb_context_unref(CONTEXT);
pKeyboard->setKeymap(IKeyboard::SStringRuleNames{LAYOUT, MODEL, VARIANT, OPTIONS, RULES});
const auto LAYOUTSTR = pKeyboard->getActiveLayout();
@ -1017,11 +984,11 @@ void CInputManager::newVirtualMouse(SP<CVirtualPointerV1Resource> mouse) {
setupMouse(PMOUSE);
Debug::log(LOG, "New virtual mouse created, pointer WLR: {:x}", (uintptr_t)mouse->wlr());
Debug::log(LOG, "New virtual mouse created");
}
void CInputManager::newMouse(wlr_input_device* mouse) {
const auto PMOUSE = m_vPointers.emplace_back(CMouse::create(wlr_pointer_from_input_device(mouse)));
void CInputManager::newMouse(SP<Aquamarine::IPointer> mouse) {
const auto PMOUSE = m_vPointers.emplace_back(CMouse::create(mouse));
setupMouse(PMOUSE);
@ -1032,13 +999,13 @@ void CInputManager::setupMouse(SP<IPointer> mauz) {
m_vHIDs.push_back(mauz);
try {
mauz->hlName = getNameForNewDevice(mauz->wlr()->base.name);
mauz->hlName = getNameForNewDevice(mauz->deviceName);
} catch (std::exception& e) {
Debug::log(ERR, "Mouse had no name???"); // logic error
}
if (wlr_input_device_is_libinput(&mauz->wlr()->base)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&mauz->wlr()->base);
if (mauz->aq() && mauz->aq()->getLibinputHandle()) {
const auto LIBINPUTDEV = mauz->aq()->getLibinputHandle();
Debug::log(LOG, "New mouse has libinput sens {:.2f} ({:.2f}) with accel profile {} ({})", libinput_device_config_accel_get_speed(LIBINPUTDEV),
libinput_device_config_accel_get_default_speed(LIBINPUTDEV), (int)libinput_device_config_accel_get_profile(LIBINPUTDEV),
@ -1084,8 +1051,8 @@ void CInputManager::setPointerConfigs() {
}
}
if (wlr_input_device_is_libinput(&m->wlr()->base)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base);
if (m->aq() && m->aq()->getLibinputHandle()) {
const auto LIBINPUTDEV = m->aq()->getLibinputHandle();
double touchw = 0, touchh = 0;
const auto ISTOUCHPAD = libinput_device_has_capability(LIBINPUTDEV, LIBINPUT_DEVICE_CAP_POINTER) &&
@ -1225,16 +1192,14 @@ static void removeFromHIDs(WP<IHID> hid) {
}
void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
if (pKeyboard->xkbTranslationState)
xkb_state_unref(pKeyboard->xkbTranslationState);
pKeyboard->xkbTranslationState = nullptr;
Debug::log(LOG, "Keyboard at {:x} removed", (uintptr_t)pKeyboard.get());
std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; });
if (m_vKeyboards.size() > 0) {
bool found = false;
for (auto& k : m_vKeyboards | std::views::reverse) {
if (!k->wlr())
if (!k)
continue;
g_pSeatManager->setKeyboard(k);
@ -1251,6 +1216,8 @@ void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
}
void CInputManager::destroyPointer(SP<IPointer> mouse) {
Debug::log(LOG, "Pointer at {:x} removed", (uintptr_t)mouse.get());
std::erase_if(m_vPointers, [mouse](const auto& other) { return other == mouse; });
g_pSeatManager->setMouse(m_vPointers.size() > 0 ? m_vPointers.front() : nullptr);
@ -1297,20 +1264,7 @@ 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);
}
pKeyboard->updateLEDs();
}
void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
@ -1338,7 +1292,7 @@ void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) {
const auto IME = m_sIMERelay.m_pIME.lock();
if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(pKeyboard->wlr());
IME->setKeyboard(pKeyboard);
IME->sendKey(e.timeMs, e.keycode, e.state);
} else {
g_pSeatManager->setKeyboard(pKeyboard);
@ -1356,15 +1310,14 @@ void CInputManager::onKeyboardMod(SP<IKeyboard> pKeyboard) {
const bool DISALLOWACTION = pKeyboard->isVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard);
const auto ALLMODS = accumulateModsFromAllKBs();
const auto PWLRKB = pKeyboard->wlr();
auto MODS = PWLRKB->modifiers;
auto MODS = pKeyboard->modifiersState;
MODS.depressed = ALLMODS;
const auto IME = m_sIMERelay.m_pIME.lock();
if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(PWLRKB);
IME->setKeyboard(pKeyboard);
IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
} else {
g_pSeatManager->setKeyboard(pKeyboard);
@ -1373,12 +1326,12 @@ void CInputManager::onKeyboardMod(SP<IKeyboard> pKeyboard) {
updateKeyboardsLeds(pKeyboard);
if (PWLRKB->modifiers.group != pKeyboard->activeLayout) {
pKeyboard->activeLayout = PWLRKB->modifiers.group;
if (pKeyboard->modifiersState.group != pKeyboard->activeLayout) {
pKeyboard->activeLayout = pKeyboard->modifiersState.group;
const auto LAYOUT = pKeyboard->getActiveLayout();
pKeyboard->updateXKBTranslationState();
Debug::log(LOG, "LAYOUT CHANGED TO {} GROUP {}", LAYOUT, MODS.group);
g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->hlName + "," + LAYOUT});
EMIT_HOOK_EVENT("activeLayout", (std::vector<std::any>{pKeyboard, LAYOUT}));
@ -1488,10 +1441,10 @@ uint32_t CInputManager::accumulateModsFromAllKBs() {
if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb))
continue;
if (!kb->enabled || !kb->wlr())
if (!kb->enabled)
continue;
finalMask |= wlr_keyboard_get_modifiers(kb->wlr());
finalMask |= kb->getModifiers();
}
return finalMask;
@ -1507,12 +1460,12 @@ void CInputManager::disableAllKeyboards(bool virt) {
}
}
void CInputManager::newTouchDevice(wlr_input_device* pDevice) {
const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(wlr_touch_from_input_device(pDevice)));
void CInputManager::newTouchDevice(SP<Aquamarine::ITouch> pDevice) {
const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(pDevice));
m_vHIDs.push_back(PNEWDEV);
try {
PNEWDEV->hlName = getNameForNewDevice(pDevice->name);
PNEWDEV->hlName = getNameForNewDevice(PNEWDEV->deviceName);
} catch (std::exception& e) {
Debug::log(ERR, "Touch Device had no name???"); // logic error
}
@ -1535,9 +1488,9 @@ void CInputManager::newTouchDevice(wlr_input_device* pDevice) {
}
void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
auto setConfig = [&](SP<ITouch> PTOUCHDEV) -> void {
if (wlr_input_device_is_libinput(&PTOUCHDEV->wlr()->base)) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&PTOUCHDEV->wlr()->base);
auto setConfig = [](SP<ITouch> PTOUCHDEV) -> void {
if (PTOUCHDEV->aq() && PTOUCHDEV->aq()->getLibinputHandle()) {
const auto LIBINPUTDEV = PTOUCHDEV->aq()->getLibinputHandle();
const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->hlName, "enabled", "input:touchdevice:enabled");
const auto mode = ENABLED ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
@ -1553,11 +1506,12 @@ void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
bool bound = !output.empty() && output != STRVAL_EMPTY;
const bool AUTODETECT = output == "[[Auto]]";
if (!bound && AUTODETECT) {
const auto DEFAULTOUTPUT = PTOUCHDEV->wlr()->output_name;
if (DEFAULTOUTPUT) {
output = DEFAULTOUTPUT;
bound = true;
}
// FIXME:
// const auto DEFAULTOUTPUT = PTOUCHDEV->wlr()->output_name;
// if (DEFAULTOUTPUT) {
// output = DEFAULTOUTPUT;
// bound = true;
// }
}
PTOUCHDEV->boundOutput = bound ? output : "";
const auto PMONITOR = bound ? g_pCompositor->getMonitorFromName(output) : nullptr;
@ -1581,9 +1535,9 @@ void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
void CInputManager::setTabletConfigs() {
for (auto& t : m_vTablets) {
if (wlr_input_device_is_libinput(&t->wlr()->base)) {
if (t->aq()->getLibinputHandle()) {
const auto NAME = t->hlName;
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&t->wlr()->base);
const auto LIBINPUTDEV = t->aq()->getLibinputHandle();
const auto RELINPUT = g_pConfigManager->getDeviceInt(NAME, "relative_input", "input:tablet:relative_input");
t->relativeInput = RELINPUT;
@ -1611,51 +1565,37 @@ void CInputManager::setTabletConfigs() {
const auto ACTIVE_AREA_SIZE = g_pConfigManager->getDeviceVec(NAME, "active_area_size", "input:tablet:active_area_size");
const auto ACTIVE_AREA_POS = g_pConfigManager->getDeviceVec(NAME, "active_area_position", "input:tablet:active_area_position");
if (ACTIVE_AREA_SIZE.x != 0 || ACTIVE_AREA_SIZE.y != 0) {
t->activeArea = CBox{ACTIVE_AREA_POS.x / t->wlr()->width_mm, ACTIVE_AREA_POS.y / t->wlr()->height_mm, (ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t->wlr()->width_mm,
(ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t->wlr()->height_mm};
t->activeArea = CBox{ACTIVE_AREA_POS.x / t->aq()->physicalSize.x, ACTIVE_AREA_POS.y / t->aq()->physicalSize.y,
(ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t->aq()->physicalSize.x, (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t->aq()->physicalSize.y};
}
}
}
}
void CInputManager::newSwitch(wlr_input_device* pDevice) {
const auto PNEWDEV = &m_lSwitches.emplace_back();
PNEWDEV->pWlrDevice = pDevice;
void CInputManager::newSwitch(SP<Aquamarine::ISwitch> pDevice) {
const auto PNEWDEV = &m_lSwitches.emplace_back();
PNEWDEV->pDevice = pDevice;
Debug::log(LOG, "New switch with name \"{}\" added", pDevice->name);
Debug::log(LOG, "New switch with name \"{}\" added", pDevice->getName());
PNEWDEV->hyprListener_destroy.initCallback(&pDevice->events.destroy, [&](void* owner, void* data) { destroySwitch((SSwitchDevice*)owner); }, PNEWDEV, "SwitchDevice");
PNEWDEV->listeners.destroy = pDevice->events.destroy.registerListener([this, PNEWDEV](std::any d) { destroySwitch(PNEWDEV); });
const auto PSWITCH = wlr_switch_from_input_device(pDevice);
PNEWDEV->listeners.fire = pDevice->events.fire.registerListener([PNEWDEV](std::any d) {
const auto NAME = PNEWDEV->pDevice->getName();
const auto E = std::any_cast<Aquamarine::ISwitch::SFireEvent>(d);
PNEWDEV->hyprListener_toggle.initCallback(
&PSWITCH->events.toggle,
[&](void* owner, void* data) {
const auto PDEVICE = (SSwitchDevice*)owner;
const auto NAME = std::string(PDEVICE->pWlrDevice->name);
const auto E = (wlr_switch_toggle_event*)data;
Debug::log(LOG, "Switch {} fired, triggering binds.", NAME);
if (PDEVICE->status != -1 && PDEVICE->status == E->switch_state)
return;
g_pKeybindManager->onSwitchEvent(NAME);
Debug::log(LOG, "Switch {} fired, triggering binds.", NAME);
g_pKeybindManager->onSwitchEvent(NAME);
switch (E->switch_state) {
case WLR_SWITCH_STATE_ON:
Debug::log(LOG, "Switch {} turn on, triggering binds.", NAME);
g_pKeybindManager->onSwitchOnEvent(NAME);
break;
case WLR_SWITCH_STATE_OFF:
Debug::log(LOG, "Switch {} turn off, triggering binds.", NAME);
g_pKeybindManager->onSwitchOffEvent(NAME);
break;
}
PDEVICE->status = E->switch_state;
},
PNEWDEV, "SwitchDevice");
if (E.enable) {
Debug::log(LOG, "Switch {} turn on, triggering binds.", NAME);
g_pKeybindManager->onSwitchOnEvent(NAME);
} else {
Debug::log(LOG, "Switch {} turn off, triggering binds.", NAME);
g_pKeybindManager->onSwitchOffEvent(NAME);
}
});
}
void CInputManager::destroySwitch(SSwitchDevice* pDevice) {

View file

@ -18,6 +18,14 @@ class CVirtualKeyboardV1Resource;
class CVirtualPointerV1Resource;
class IKeyboard;
AQUAMARINE_FORWARD(IPointer);
AQUAMARINE_FORWARD(IKeyboard);
AQUAMARINE_FORWARD(ITouch);
AQUAMARINE_FORWARD(ISwitch);
AQUAMARINE_FORWARD(ITablet);
AQUAMARINE_FORWARD(ITabletTool);
AQUAMARINE_FORWARD(ITabletPad);
enum eClickBehaviorMode {
CLICKMODE_DEFAULT = 0,
CLICKMODE_KILL
@ -82,15 +90,14 @@ class CInputManager {
void onKeyboardKey(std::any, SP<IKeyboard>);
void onKeyboardMod(SP<IKeyboard>);
void newKeyboard(wlr_input_device*);
void newKeyboard(SP<Aquamarine::IKeyboard>);
void newVirtualKeyboard(SP<CVirtualKeyboardV1Resource>);
void newMouse(wlr_input_device*);
void newMouse(SP<Aquamarine::IPointer>);
void newVirtualMouse(SP<CVirtualPointerV1Resource>);
void newTouchDevice(wlr_input_device*);
void newSwitch(wlr_input_device*);
void newTabletTool(wlr_tablet_tool*);
void newTabletPad(wlr_input_device*);
void newTablet(wlr_input_device*);
void newTouchDevice(SP<Aquamarine::ITouch>);
void newSwitch(SP<Aquamarine::ISwitch>);
void newTabletPad(SP<Aquamarine::ITabletPad>);
void newTablet(SP<Aquamarine::ITablet>);
void destroyTouchDevice(SP<ITouch>);
void destroyKeyboard(SP<IKeyboard>);
void destroyPointer(SP<IPointer>);
@ -232,7 +239,7 @@ class CInputManager {
void mouseMoveUnified(uint32_t, bool refocus = false);
SP<CTabletTool> ensureTabletToolPresent(wlr_tablet_tool*);
SP<CTabletTool> ensureTabletToolPresent(SP<Aquamarine::ITabletTool>);
void applyConfigToKeyboard(SP<IKeyboard>);
@ -278,6 +285,14 @@ class CInputManager {
void restoreCursorIconToApp(); // no-op if restored
// discrete scrolling emulation using v120 data
struct {
bool lastEventSign = 0;
bool lastEventAxis = 0;
uint32_t lastEventTime = 0;
uint32_t accumulatedScroll = 0;
} m_ScrollWheelState;
friend class CKeybindManager;
friend class CWLSurface;
};

View file

@ -62,7 +62,7 @@ static void refocusTablet(SP<CTablet> tab, SP<CTabletTool> tool, bool motion = f
if (!motion)
return;
if (LASTHLSURFACE->constraint() && tool->wlr()->type != WLR_TABLET_TOOL_TYPE_MOUSE) {
if (LASTHLSURFACE->constraint() && tool->aq()->type != Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_MOUSE) {
// cursor logic will completely break here as the cursor will be locked.
// let's just "map" the desired position to the constraint area.
@ -102,7 +102,7 @@ void CInputManager::onTabletAxis(CTablet::SAxisEvent e) {
Vector2D delta = {std::isnan(dx) ? 0.0 : dx, std::isnan(dy) ? 0.0 : dy};
switch (e.tool->type) {
case WLR_TABLET_TOOL_TYPE_MOUSE: {
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_MOUSE: {
g_pPointerManager->move(delta);
break;
}
@ -205,12 +205,12 @@ void CInputManager::onTabletProximity(CTablet::SProximityEvent e) {
PROTO::idle->onActivity();
}
void CInputManager::newTablet(wlr_input_device* pDevice) {
const auto PNEWTABLET = m_vTablets.emplace_back(CTablet::create(wlr_tablet_from_input_device(pDevice)));
void CInputManager::newTablet(SP<Aquamarine::ITablet> pDevice) {
const auto PNEWTABLET = m_vTablets.emplace_back(CTablet::create(pDevice));
m_vHIDs.push_back(PNEWTABLET);
try {
PNEWTABLET->hlName = deviceNameToInternalString(pDevice->name);
PNEWTABLET->hlName = deviceNameToInternalString(pDevice->getName());
} catch (std::exception& e) {
Debug::log(ERR, "Tablet had no name???"); // logic error
}
@ -227,28 +227,32 @@ void CInputManager::newTablet(wlr_input_device* pDevice) {
setTabletConfigs();
}
SP<CTabletTool> CInputManager::ensureTabletToolPresent(wlr_tablet_tool* pTool) {
if (pTool->data == nullptr) {
const auto PTOOL = m_vTabletTools.emplace_back(CTabletTool::create(pTool));
m_vHIDs.push_back(PTOOL);
SP<CTabletTool> CInputManager::ensureTabletToolPresent(SP<Aquamarine::ITabletTool> pTool) {
PTOOL->events.destroy.registerStaticListener(
[this](void* owner, std::any d) {
auto TOOL = ((CTabletTool*)owner)->self;
destroyTabletTool(TOOL.lock());
},
PTOOL.get());
for (auto& t : m_vTabletTools) {
if (t->aq() == pTool)
return t;
}
return CTabletTool::fromWlr(pTool);
const auto PTOOL = m_vTabletTools.emplace_back(CTabletTool::create(pTool));
m_vHIDs.push_back(PTOOL);
PTOOL->events.destroy.registerStaticListener(
[this](void* owner, std::any d) {
auto TOOL = ((CTabletTool*)owner)->self;
destroyTabletTool(TOOL.lock());
},
PTOOL.get());
return PTOOL;
}
void CInputManager::newTabletPad(wlr_input_device* pDevice) {
const auto PNEWPAD = m_vTabletPads.emplace_back(CTabletPad::create(wlr_tablet_pad_from_input_device(pDevice)));
void CInputManager::newTabletPad(SP<Aquamarine::ITabletPad> pDevice) {
const auto PNEWPAD = m_vTabletPads.emplace_back(CTabletPad::create(pDevice));
m_vHIDs.push_back(PNEWPAD);
try {
PNEWPAD->hlName = deviceNameToInternalString(pDevice->name);
PNEWPAD->hlName = deviceNameToInternalString(pDevice->getName());
} catch (std::exception& e) {
Debug::log(ERR, "Pad had no name???"); // logic error
}
@ -259,7 +263,7 @@ void CInputManager::newTabletPad(wlr_input_device* pDevice) {
destroyTabletPad(PAD.lock());
}, PNEWPAD.get());
PNEWPAD->padEvents.button.registerStaticListener([this](void* owner, std::any e) {
PNEWPAD->padEvents.button.registerStaticListener([](void* owner, std::any e) {
const auto E = std::any_cast<CTabletPad::SButtonEvent>(e);
const auto PPAD = ((CTabletPad*)owner)->self.lock();
@ -267,26 +271,25 @@ void CInputManager::newTabletPad(wlr_input_device* pDevice) {
PROTO::tablet->buttonPad(PPAD, E.button, E.timeMs, E.down);
}, PNEWPAD.get());
PNEWPAD->padEvents.strip.registerStaticListener([this](void* owner, std::any e) {
PNEWPAD->padEvents.strip.registerStaticListener([](void* owner, std::any e) {
const auto E = std::any_cast<CTabletPad::SStripEvent>(e);
const auto PPAD = ((CTabletPad*)owner)->self.lock();
PROTO::tablet->strip(PPAD, E.strip, E.position, E.finger, E.timeMs);
}, PNEWPAD.get());
PNEWPAD->padEvents.ring.registerStaticListener([this](void* owner, std::any e) {
PNEWPAD->padEvents.ring.registerStaticListener([](void* owner, std::any e) {
const auto E = std::any_cast<CTabletPad::SRingEvent>(e);
const auto PPAD = ((CTabletPad*)owner)->self.lock();
PROTO::tablet->ring(PPAD, E.ring, E.position, E.finger, E.timeMs);
}, PNEWPAD.get());
PNEWPAD->padEvents.attach.registerStaticListener([this](void* owner, std::any e) {
PNEWPAD->padEvents.attach.registerStaticListener([](void* owner, std::any e) {
const auto PPAD = ((CTabletPad*)owner)->self.lock();
const auto TOOL = std::any_cast<SP<CTabletTool>>(e);
PPAD->parent = TOOL;
}, PNEWPAD.get());
// clang-format on
}

View file

@ -103,7 +103,7 @@ void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
return;
const bool VERTANIMS = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle == "slidevert" ||
m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle.starts_with("slidefadevert");
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_invert");
static auto PSWIPEINVR = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_touch_invert");
static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, (int64_t)1LL, (int64_t)UINT32_MAX);
// Handle the workspace swipe if there is one

View file

@ -2,14 +2,15 @@ globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
src = globber.stdout().strip().split('\n')
executable('Hyprland', src,
cpp_args: ['-DWLR_USE_UNSTABLE'],
link_args: '-rdynamic',
cpp_pch: 'pch/pch.hpp',
dependencies: [
server_protos,
dependency('aquamarine'),
dependency('gbm'),
dependency('xcursor'),
dependency('wayland-server'),
dependency('wayland-client'),
wlroots.get_variable('wlroots'),
dependency('cairo'),
dependency('hyprcursor', version: '>=0.1.7'),
dependency('hyprlang', version: '>= 0.3.2'),

View file

@ -2,6 +2,7 @@
#include "../Compositor.hpp"
#include "../debug/HyprCtl.hpp"
#include <dlfcn.h>
#include <filesystem>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/sysctl.h>

View file

@ -1,48 +1,9 @@
#include "CursorShape.hpp"
#include <algorithm>
#include "../helpers/CursorShapes.hpp"
#define LOGM PROTO::cursorShape->protoLog
// clang-format off
constexpr const char* SHAPE_NAMES[] = {
"invalid",
"default",
"context-menu",
"help",
"pointer",
"progress",
"wait",
"cell",
"crosshair",
"text",
"vertical-text",
"alias",
"copy",
"move",
"no-drop",
"not-allowed",
"grab",
"grabbing",
"e-resize",
"n-resize",
"ne-resize",
"nw-resize",
"s-resize",
"se-resize",
"sw-resize",
"w-resize",
"ew-resize",
"ns-resize",
"nesw-resize",
"nwse-resize",
"col-resize",
"row-resize",
"all-scroll",
"zoom-in",
"zoom-out",
};
// clang-format on
CCursorShapeProtocol::CCursorShapeProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
;
}
@ -82,7 +43,7 @@ void CCursorShapeProtocol::createCursorShapeDevice(CWpCursorShapeManagerV1* pMgr
}
void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t serial, wpCursorShapeDeviceV1Shape shape) {
if ((uint32_t)shape == 0 || (uint32_t)shape > sizeof(SHAPE_NAMES)) {
if ((uint32_t)shape == 0 || (uint32_t)shape > CURSOR_SHAPE_NAMES.size()) {
pMgr->error(WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE, "The shape is invalid");
return;
}
@ -90,7 +51,7 @@ void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t ser
SSetShapeEvent event;
event.pMgr = pMgr;
event.shape = shape;
event.shapeName = SHAPE_NAMES[shape];
event.shapeName = CURSOR_SHAPE_NAMES.at(shape);
events.setShape.emit(event);
}

302
src/protocols/DRMLease.cpp Normal file
View file

@ -0,0 +1,302 @@
#include "DRMLease.hpp"
#include "../Compositor.hpp"
#include <aquamarine/backend/DRM.hpp>
#include <fcntl.h>
#define LOGM PROTO::lease->protoLog
CDRMLeaseResource::CDRMLeaseResource(SP<CWpDrmLeaseV1> resource_, SP<CDRMLeaseRequestResource> request) : resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CWpDrmLeaseV1* r) { PROTO::lease->destroyResource(this); });
resource->setDestroy([this](CWpDrmLeaseV1* r) { PROTO::lease->destroyResource(this); });
parent = request->parent;
requested = request->requested;
for (auto& m : requested) {
if (!m->monitor || m->monitor->isBeingLeased) {
LOGM(ERR, "Rejecting lease: no monitor or monitor is being leased for {}", (m->monitor ? m->monitor->szName : "null"));
resource->sendFinished();
return;
}
}
// grant the lease if it is seemingly valid
LOGM(LOG, "Leasing outputs: {}", [this]() {
std::string roll;
for (auto& o : requested) {
roll += std::format("{} ", o->monitor->szName);
}
return roll;
}());
std::vector<SP<Aquamarine::IOutput>> outputs;
for (auto& m : requested) {
outputs.emplace_back(m->monitor->output);
}
auto aqlease = Aquamarine::CDRMLease::create(outputs);
if (!aqlease) {
LOGM(ERR, "Rejecting lease: backend failed to alloc a lease");
resource->sendFinished();
return;
}
LOGM(LOG, "Granting lease, sending fd {}", aqlease->leaseFD);
resource->sendLeaseFd(aqlease->leaseFD);
lease = aqlease;
for (auto& m : requested) {
m->monitor->isBeingLeased = true;
}
listeners.destroyLease = lease->events.destroy.registerListener([this](std::any d) {
for (auto& m : requested) {
if (m && m->monitor)
m->monitor->isBeingLeased = false;
}
resource->sendFinished();
});
close(lease->leaseFD);
}
bool CDRMLeaseResource::good() {
return resource->resource();
}
CDRMLeaseRequestResource::CDRMLeaseRequestResource(SP<CWpDrmLeaseRequestV1> resource_) : resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CWpDrmLeaseRequestV1* r) { PROTO::lease->destroyResource(this); });
resource->setRequestConnector([this](CWpDrmLeaseRequestV1* r, wl_resource* conn) {
if (!conn) {
resource->error(-1, "Null connector");
return;
}
auto CONNECTOR = CDRMLeaseConnectorResource::fromResource(conn);
if (std::find(requested.begin(), requested.end(), CONNECTOR) != requested.end()) {
resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Connector already requested");
return;
}
// TODO: when (if) we add multi, make sure this is from the correct device.
requested.emplace_back(CONNECTOR);
});
resource->setSubmit([this](CWpDrmLeaseRequestV1* r, uint32_t id) {
if (requested.empty()) {
resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "No connectors added");
return;
}
auto RESOURCE = makeShared<CDRMLeaseResource>(makeShared<CWpDrmLeaseV1>(resource->client(), resource->version(), -1), self.lock());
if (!RESOURCE) {
resource->noMemory();
return;
}
PROTO::lease->m_vLeases.emplace_back(RESOURCE);
// per protcol, after submit, this is dead.
PROTO::lease->destroyResource(this);
});
}
bool CDRMLeaseRequestResource::good() {
return resource->resource();
}
SP<CDRMLeaseConnectorResource> CDRMLeaseConnectorResource::fromResource(wl_resource* res) {
auto data = (CDRMLeaseConnectorResource*)(((CWpDrmLeaseConnectorV1*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
CDRMLeaseConnectorResource::CDRMLeaseConnectorResource(SP<CWpDrmLeaseConnectorV1> resource_, SP<CMonitor> monitor_) : monitor(monitor_), resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CWpDrmLeaseConnectorV1* r) { PROTO::lease->destroyResource(this); });
resource->setDestroy([this](CWpDrmLeaseConnectorV1* r) { PROTO::lease->destroyResource(this); });
resource->setData(this);
listeners.destroyMonitor = monitor->events.destroy.registerListener([this](std::any d) {
resource->sendWithdrawn();
dead = true;
});
}
bool CDRMLeaseConnectorResource::good() {
return resource->resource();
}
void CDRMLeaseConnectorResource::sendData() {
resource->sendName(monitor->szName.c_str());
resource->sendDescription(monitor->szDescription.c_str());
auto AQDRMOutput = (Aquamarine::CDRMOutput*)monitor->output.get();
resource->sendConnectorId(AQDRMOutput->getConnectorID());
resource->sendDone();
}
CDRMLeaseDeviceResource::CDRMLeaseDeviceResource(SP<CWpDrmLeaseDeviceV1> resource_) : resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CWpDrmLeaseDeviceV1* r) { PROTO::lease->destroyResource(this); });
resource->setRelease([this](CWpDrmLeaseDeviceV1* r) { PROTO::lease->destroyResource(this); });
resource->setCreateLeaseRequest([this](CWpDrmLeaseDeviceV1* r, uint32_t id) {
auto RESOURCE = makeShared<CDRMLeaseRequestResource>(makeShared<CWpDrmLeaseRequestV1>(resource->client(), resource->version(), id));
if (!RESOURCE) {
resource->noMemory();
return;
}
RESOURCE->self = RESOURCE;
PROTO::lease->m_vRequests.emplace_back(RESOURCE);
LOGM(LOG, "New lease request {}", id);
RESOURCE->parent = self;
});
int fd = ((Aquamarine::CDRMBackend*)PROTO::lease->primaryDevice->backend.get())->getNonMasterFD();
if (fd < 0) {
LOGM(ERR, "Failed to dup fd in lease");
return;
}
LOGM(LOG, "Sending DRMFD {} to new lease device", fd);
resource->sendDrmFd(fd);
close(fd);
for (auto& m : PROTO::lease->primaryDevice->offeredOutputs) {
sendConnector(m.lock());
}
resource->sendDone();
}
bool CDRMLeaseDeviceResource::good() {
return resource->resource();
}
void CDRMLeaseDeviceResource::sendConnector(SP<CMonitor> monitor) {
if (std::find_if(connectorsSent.begin(), connectorsSent.end(), [monitor](const auto& e) { return e->monitor == monitor; }) != connectorsSent.end())
return;
auto RESOURCE = makeShared<CDRMLeaseConnectorResource>(makeShared<CWpDrmLeaseConnectorV1>(resource->client(), resource->version(), -1), monitor);
if (!RESOURCE) {
resource->noMemory();
return;
}
RESOURCE->parent = self;
RESOURCE->self = RESOURCE;
LOGM(LOG, "Sending new connector {}", monitor->szName);
connectorsSent.emplace_back(RESOURCE);
PROTO::lease->m_vConnectors.emplace_back(RESOURCE);
resource->sendConnector(RESOURCE->resource.get());
RESOURCE->sendData();
}
CDRMLeaseDevice::CDRMLeaseDevice(SP<Aquamarine::CDRMBackend> drmBackend) : backend(drmBackend) {
auto drm = (Aquamarine::CDRMBackend*)drmBackend.get();
auto fd = drm->getNonMasterFD();
if (fd < 0) {
LOGM(ERR, "Failed to dup fd for drm node {}", drm->gpuName);
return;
}
close(fd);
success = true;
name = drm->gpuName;
}
CDRMLeaseProtocol::CDRMLeaseProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
for (auto& b : g_pCompositor->m_pAqBackend->getImplementations()) {
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
continue;
auto drm = ((Aquamarine::CDRMBackend*)b.get())->self.lock();
primaryDevice = makeShared<CDRMLeaseDevice>(drm);
if (primaryDevice->success)
break;
}
if (!primaryDevice || primaryDevice->success) {
PROTO::lease.reset();
return;
}
}
void CDRMLeaseProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CDRMLeaseDeviceResource>(makeShared<CWpDrmLeaseDeviceV1>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
RESOURCE->self = RESOURCE;
}
void CDRMLeaseProtocol::destroyResource(CDRMLeaseDeviceResource* resource) {
std::erase_if(m_vManagers, [resource](const auto& e) { return e.get() == resource; });
}
void CDRMLeaseProtocol::destroyResource(CDRMLeaseConnectorResource* resource) {
std::erase_if(m_vConnectors, [resource](const auto& e) { return e.get() == resource; });
}
void CDRMLeaseProtocol::destroyResource(CDRMLeaseRequestResource* resource) {
std::erase_if(m_vRequests, [resource](const auto& e) { return e.get() == resource; });
}
void CDRMLeaseProtocol::destroyResource(CDRMLeaseResource* resource) {
std::erase_if(m_vLeases, [resource](const auto& e) { return e.get() == resource; });
}
void CDRMLeaseProtocol::offer(SP<CMonitor> monitor) {
if (std::find(primaryDevice->offeredOutputs.begin(), primaryDevice->offeredOutputs.end(), monitor) != primaryDevice->offeredOutputs.end())
return;
if (monitor->output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM)
return;
if (monitor->output->getBackend() != primaryDevice->backend) {
LOGM(ERR, "Monitor {} cannot be leased: primaryDevice lease is for a different device", monitor->szName);
return;
}
primaryDevice->offeredOutputs.emplace_back(monitor);
for (auto& m : m_vManagers) {
m->sendConnector(monitor);
m->resource->sendDone();
}
}

137
src/protocols/DRMLease.hpp Normal file
View file

@ -0,0 +1,137 @@
#pragma once
#include <memory>
#include <vector>
#include <unordered_map>
#include "WaylandProtocol.hpp"
#include "drm-lease-v1.hpp"
#include "../helpers/signal/Signal.hpp"
/*
TODO: this protocol is not made for systems with multiple DRM nodes (e.g. multigpu)
*/
AQUAMARINE_FORWARD(CDRMBackend);
AQUAMARINE_FORWARD(CDRMLease);
class CDRMLeaseDeviceResource;
class CMonitor;
class CDRMLeaseProtocol;
class CDRMLeaseConnectorResource;
class CDRMLeaseRequestResource;
class CDRMLeaseResource {
public:
CDRMLeaseResource(SP<CWpDrmLeaseV1> resource_, SP<CDRMLeaseRequestResource> request);
bool good();
WP<CDRMLeaseDeviceResource> parent;
std::vector<WP<CDRMLeaseConnectorResource>> requested;
WP<Aquamarine::CDRMLease> lease;
int leaseFD = -1;
struct {
CHyprSignalListener destroyLease;
} listeners;
private:
SP<CWpDrmLeaseV1> resource;
};
class CDRMLeaseRequestResource {
public:
CDRMLeaseRequestResource(SP<CWpDrmLeaseRequestV1> resource_);
bool good();
WP<CDRMLeaseDeviceResource> parent;
WP<CDRMLeaseRequestResource> self;
std::vector<WP<CDRMLeaseConnectorResource>> requested;
private:
SP<CWpDrmLeaseRequestV1> resource;
};
class CDRMLeaseConnectorResource {
public:
CDRMLeaseConnectorResource(SP<CWpDrmLeaseConnectorV1> resource_, SP<CMonitor> monitor_);
static SP<CDRMLeaseConnectorResource> fromResource(wl_resource*);
bool good();
void sendData();
WP<CDRMLeaseConnectorResource> self;
WP<CDRMLeaseDeviceResource> parent;
WP<CMonitor> monitor;
bool dead = false;
private:
SP<CWpDrmLeaseConnectorV1> resource;
struct {
CHyprSignalListener destroyMonitor;
} listeners;
friend class CDRMLeaseDeviceResource;
};
class CDRMLeaseDeviceResource {
public:
CDRMLeaseDeviceResource(SP<CWpDrmLeaseDeviceV1> resource_);
bool good();
void sendConnector(SP<CMonitor> monitor);
std::vector<WP<CDRMLeaseConnectorResource>> connectorsSent;
WP<CDRMLeaseDeviceResource> self;
private:
SP<CWpDrmLeaseDeviceV1> resource;
friend class CDRMLeaseProtocol;
};
class CDRMLeaseDevice {
public:
CDRMLeaseDevice(SP<Aquamarine::CDRMBackend> drmBackend);
std::string name = "";
bool success = false;
SP<Aquamarine::CDRMBackend> backend;
std::vector<WP<CMonitor>> offeredOutputs;
};
class CDRMLeaseProtocol : public IWaylandProtocol {
public:
CDRMLeaseProtocol(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);
void offer(SP<CMonitor> monitor);
private:
void destroyResource(CDRMLeaseDeviceResource* resource);
void destroyResource(CDRMLeaseConnectorResource* resource);
void destroyResource(CDRMLeaseRequestResource* resource);
void destroyResource(CDRMLeaseResource* resource);
//
std::vector<SP<CDRMLeaseDeviceResource>> m_vManagers;
std::vector<SP<CDRMLeaseConnectorResource>> m_vConnectors;
std::vector<SP<CDRMLeaseRequestResource>> m_vRequests;
std::vector<SP<CDRMLeaseResource>> m_vLeases;
SP<CDRMLeaseDevice> primaryDevice;
friend class CDRMLeaseDeviceResource;
friend class CDRMLeaseConnectorResource;
friend class CDRMLeaseRequestResource;
friend class CDRMLeaseResource;
};
namespace PROTO {
inline UP<CDRMLeaseProtocol> lease;
};

View file

@ -0,0 +1,183 @@
#include "DRMSyncobj.hpp"
#include <algorithm>
#include "core/Compositor.hpp"
#include "../helpers/sync/SyncTimeline.hpp"
#include "../Compositor.hpp"
#include <fcntl.h>
#define LOGM PROTO::sync->protoLog
CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurfaceV1> resource_, SP<CWLSurfaceResource> surface_) : surface(surface_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setOnDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); });
resource->setDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); });
resource->setSetAcquirePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
if (!surface) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone");
return;
}
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
acquireTimeline = timeline;
acquirePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
});
resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
if (!surface) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone");
return;
}
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
releaseTimeline = timeline;
releasePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
});
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
if (!!acquireTimeline != !!releaseTimeline) {
resource->error(acquireTimeline ? WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT : WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing timeline");
surface->pending.rejected = true;
return;
}
if ((acquireTimeline || releaseTimeline) && !surface->pending.buffer) {
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
surface->pending.rejected = true;
return;
}
if (!acquireTimeline)
return;
// wait for the acquire timeline to materialize
auto materialized = acquireTimeline->timeline->check(acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
if (!materialized.has_value()) {
LOGM(ERR, "Failed to check the acquire timeline");
resource->noMemory();
return;
}
if (materialized)
return;
surface->lockPendingState();
acquireTimeline->timeline->addWaiter([this]() { surface->unlockPendingState(); }, acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
});
}
bool CDRMSyncobjSurfaceResource::good() {
return resource->resource();
}
CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(SP<CWpLinuxDrmSyncobjTimelineV1> resource_, int fd_) : fd(fd_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setOnDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); });
resource->setDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); });
timeline = CSyncTimeline::create(PROTO::sync->drmFD, fd);
if (!timeline) {
resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Timeline failed importing");
return;
}
}
SP<CDRMSyncobjTimelineResource> CDRMSyncobjTimelineResource::fromResource(wl_resource* res) {
auto data = (CDRMSyncobjTimelineResource*)(((CWpLinuxDrmSyncobjTimelineV1*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CDRMSyncobjTimelineResource::good() {
return resource->resource();
}
CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(SP<CWpLinuxDrmSyncobjManagerV1> resource_) : resource(resource_) {
if (!good())
return;
resource->setOnDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); });
resource->setDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); });
resource->setGetSurface([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, wl_resource* surf) {
if (!surf) {
resource->error(-1, "Invalid surface");
return;
}
auto SURF = CWLSurfaceResource::fromResource(surf);
if (!SURF) {
resource->error(-1, "Invalid surface (2)");
return;
}
if (SURF->syncobj) {
resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS, "Surface already has a syncobj attached");
return;
}
auto RESOURCE = makeShared<CDRMSyncobjSurfaceResource>(makeShared<CWpLinuxDrmSyncobjSurfaceV1>(resource->client(), resource->version(), id), SURF);
if (!RESOURCE->good()) {
resource->noMemory();
return;
}
PROTO::sync->m_vSurfaces.emplace_back(RESOURCE);
SURF->syncobj = RESOURCE;
LOGM(LOG, "New linux_syncobj at {:x} for surface {:x}", (uintptr_t)RESOURCE.get(), (uintptr_t)SURF.get());
});
resource->setImportTimeline([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, int32_t fd) {
auto RESOURCE = makeShared<CDRMSyncobjTimelineResource>(makeShared<CWpLinuxDrmSyncobjTimelineV1>(resource->client(), resource->version(), id), fd);
if (!RESOURCE->good()) {
resource->noMemory();
return;
}
PROTO::sync->m_vTimelines.emplace_back(RESOURCE);
RESOURCE->self = RESOURCE;
LOGM(LOG, "New linux_drm_timeline at {:x}", (uintptr_t)RESOURCE.get());
});
}
bool CDRMSyncobjManagerResource::good() {
return resource->resource();
}
CDRMSyncobjProtocol::CDRMSyncobjProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
drmFD = g_pCompositor->m_iDRMFD;
}
void CDRMSyncobjProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CDRMSyncobjManagerResource>(makeShared<CWpLinuxDrmSyncobjManagerV1>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vManagers.pop_back();
return;
}
}
void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjManagerResource* resource) {
std::erase_if(m_vManagers, [resource](const auto& e) { return e.get() == resource; });
}
void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjTimelineResource* resource) {
std::erase_if(m_vTimelines, [resource](const auto& e) { return e.get() == resource; });
}
void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjSurfaceResource* resource) {
std::erase_if(m_vSurfaces, [resource](const auto& e) { return e.get() == resource; });
}

View file

@ -0,0 +1,82 @@
#pragma once
#include <memory>
#include <vector>
#include "WaylandProtocol.hpp"
#include "linux-drm-syncobj-v1.hpp"
#include "../helpers/signal/Signal.hpp"
class CWLSurfaceResource;
class CDRMSyncobjTimelineResource;
class CSyncTimeline;
class CDRMSyncobjSurfaceResource {
public:
CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurfaceV1> resource_, SP<CWLSurfaceResource> surface_);
bool good();
WP<CWLSurfaceResource> surface;
WP<CDRMSyncobjTimelineResource> acquireTimeline, releaseTimeline;
uint64_t acquirePoint = 0, releasePoint = 0;
private:
SP<CWpLinuxDrmSyncobjSurfaceV1> resource;
struct {
CHyprSignalListener surfacePrecommit;
} listeners;
};
class CDRMSyncobjTimelineResource {
public:
CDRMSyncobjTimelineResource(SP<CWpLinuxDrmSyncobjTimelineV1> resource_, int fd_);
static SP<CDRMSyncobjTimelineResource> fromResource(wl_resource*);
bool good();
WP<CDRMSyncobjTimelineResource> self;
int fd = -1;
SP<CSyncTimeline> timeline;
private:
SP<CWpLinuxDrmSyncobjTimelineV1> resource;
};
class CDRMSyncobjManagerResource {
public:
CDRMSyncobjManagerResource(SP<CWpLinuxDrmSyncobjManagerV1> resource_);
bool good();
private:
SP<CWpLinuxDrmSyncobjManagerV1> resource;
};
class CDRMSyncobjProtocol : public IWaylandProtocol {
public:
CDRMSyncobjProtocol(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(CDRMSyncobjManagerResource* resource);
void destroyResource(CDRMSyncobjTimelineResource* resource);
void destroyResource(CDRMSyncobjSurfaceResource* resource);
//
std::vector<SP<CDRMSyncobjManagerResource>> m_vManagers;
std::vector<SP<CDRMSyncobjTimelineResource>> m_vTimelines;
std::vector<SP<CDRMSyncobjSurfaceResource>> m_vSurfaces;
//
int drmFD = -1;
friend class CDRMSyncobjManagerResource;
friend class CDRMSyncobjTimelineResource;
friend class CDRMSyncobjSurfaceResource;
};
namespace PROTO {
inline UP<CDRMSyncobjProtocol> sync;
};

View file

@ -34,7 +34,7 @@ CGammaControl::CGammaControl(SP<CZwlrGammaControlV1> resource_, wl_resource* out
}
}
gammaSize = wlr_output_get_gamma_size(pMonitor->output);
gammaSize = pMonitor->output->getGammaSize();
if (gammaSize <= 0) {
LOGM(ERR, "Output {} doesn't support gamma", pMonitor->szName);
@ -81,6 +81,24 @@ CGammaControl::CGammaControl(SP<CZwlrGammaControlV1> resource_, wl_resource* out
gammaTableSet = true;
close(fd);
// translate the table to AQ format
std::vector<uint16_t> red, green, blue;
red.resize(gammaTable.size() / 3);
green.resize(gammaTable.size() / 3);
blue.resize(gammaTable.size() / 3);
for (size_t i = 0; i < gammaTable.size() / 3; ++i) {
red.at(i) = gammaTable.at(i);
green.at(i) = gammaTable.at(gammaTable.size() / 3 + i);
blue.at(i) = gammaTable.at((gammaTable.size() / 3) * 2 + i);
}
for (size_t i = 0; i < gammaTable.size() / 3; ++i) {
gammaTable.at(i * 3) = red.at(i);
gammaTable.at(i * 3 + 1) = green.at(i);
gammaTable.at(i * 3 + 2) = blue.at(i);
}
applyToMonitor();
});
@ -95,7 +113,7 @@ CGammaControl::~CGammaControl() {
return;
// reset the LUT if the client dies for whatever reason and doesn't unset the gamma
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr);
pMonitor->output->state->setGammaLut({});
}
bool CGammaControl::good() {
@ -109,19 +127,15 @@ void CGammaControl::applyToMonitor() {
LOGM(LOG, "setting to monitor {}", pMonitor->szName);
if (!gammaTableSet) {
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr);
pMonitor->output->state->setGammaLut({});
return;
}
uint16_t* red = &gammaTable.at(0);
uint16_t* green = &gammaTable.at(gammaSize);
uint16_t* blue = &gammaTable.at(gammaSize * 2);
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), gammaSize, red, green, blue);
pMonitor->output->state->setGammaLut(gammaTable);
if (!pMonitor->state.test()) {
LOGM(LOG, "setting to monitor {} failed", pMonitor->szName);
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr);
pMonitor->output->state->setGammaLut({});
}
g_pHyprRenderer->damageMonitor(pMonitor);

View file

@ -23,7 +23,7 @@ class CGammaControl {
CMonitor* pMonitor = nullptr;
size_t gammaSize = 0;
bool gammaTableSet = false;
std::vector<uint16_t> gammaTable;
std::vector<uint16_t> gammaTable; // [r,g,b]+
void onMonitorDestroy();

View file

@ -4,6 +4,7 @@
#include "../devices/IKeyboard.hpp"
#include <sys/mman.h>
#include "core/Compositor.hpp"
#include <cstring>
#define LOGM PROTO::ime->protoLog
@ -19,7 +20,7 @@ CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboar
return;
}
sendKeyboardData(g_pSeatManager->keyboard->wlr());
sendKeyboardData(g_pSeatManager->keyboard.lock());
}
CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
@ -27,37 +28,36 @@ CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
std::erase_if(owner->grabs, [](const auto& g) { return g.expired(); });
}
void CInputMethodKeyboardGrabV2::sendKeyboardData(wlr_keyboard* keyboard) {
void CInputMethodKeyboardGrabV2::sendKeyboardData(SP<IKeyboard> keyboard) {
if (keyboard == pLastKeyboard)
return;
pLastKeyboard = keyboard;
int keymapFD = allocateSHMFile(keyboard->keymap_size);
int keymapFD = allocateSHMFile(keyboard->xkbKeymapString.length() + 1);
if (keymapFD < 0) {
LOGM(ERR, "Failed to create a keymap file for keyboard grab");
return;
}
void* data = mmap(nullptr, keyboard->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD, 0);
void* data = mmap(nullptr, keyboard->xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD, 0);
if (data == MAP_FAILED) {
LOGM(ERR, "Failed to mmap a keymap file for keyboard grab");
close(keymapFD);
return;
}
memcpy(data, keyboard->keymap_string, keyboard->keymap_size);
munmap(data, keyboard->keymap_size);
memcpy(data, keyboard->xkbKeymapString.c_str(), keyboard->xkbKeymapString.length());
munmap(data, keyboard->xkbKeymapString.length() + 1);
resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD, keyboard->keymap_size);
resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD, keyboard->xkbKeymapString.length() + 1);
close(keymapFD);
const auto MODS = keyboard->modifiers;
sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
sendMods(keyboard->modifiersState.depressed, keyboard->modifiersState.latched, keyboard->modifiersState.locked, keyboard->modifiersState.group);
resource->sendRepeatInfo(keyboard->repeat_info.rate, keyboard->repeat_info.delay);
resource->sendRepeatInfo(keyboard->repeatRate, keyboard->repeatDelay);
}
void CInputMethodKeyboardGrabV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) {
@ -316,7 +316,7 @@ void CInputMethodV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t loc
}
}
void CInputMethodV2::setKeyboard(wlr_keyboard* keyboard) {
void CInputMethodV2::setKeyboard(SP<IKeyboard> keyboard) {
for (auto& gw : grabs) {
auto g = gw.lock();

View file

@ -11,6 +11,7 @@
class CInputMethodKeyboardGrabV2;
class CInputMethodPopupV2;
class IKeyboard;
class CInputMethodV2 {
public:
@ -58,7 +59,7 @@ class CInputMethodV2 {
bool hasGrab();
void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
void setKeyboard(wlr_keyboard* keyboard);
void setKeyboard(SP<IKeyboard> keyboard);
wl_client* client();
wl_client* grabClient();
@ -90,13 +91,13 @@ class CInputMethodKeyboardGrabV2 {
void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
void sendKeyboardData(wlr_keyboard* keyboard);
void sendKeyboardData(SP<IKeyboard> keyboard);
private:
SP<CZwpInputMethodKeyboardGrabV2> resource;
WP<CInputMethodV2> owner;
wlr_keyboard* pLastKeyboard = nullptr; // READ-ONLY
WP<IKeyboard> pLastKeyboard;
};
class CInputMethodPopupV2 {

View file

@ -6,6 +6,7 @@
#include <sys/mman.h>
#include <xf86drm.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "core/Compositor.hpp"
#include "types/DMABuffer.hpp"
#include "types/WLBuffer.hpp"
@ -22,21 +23,66 @@ static std::optional<dev_t> devIDFromFD(int fd) {
return stat.st_rdev;
}
CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches_) {
CDMABUFFormatTable::CDMABUFFormatTable(SDMABUFTranche _rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches_) :
rendererTranche(_rendererTranche), monitorTranches(tranches_) {
std::vector<SDMABUFFormatTableEntry> formatsVec;
std::set<std::pair<uint32_t, uint64_t>> formats;
for (auto& t : tranches_) {
for (auto& fmt : t.formats) {
for (auto& mod : fmt.mods) {
formats.insert(std::make_pair<>(fmt.format, mod));
// insert formats into vec if they got inserted into set, meaning they're unique
size_t i = 0;
rendererTranche.indicies.clear();
for (auto& fmt : rendererTranche.formats) {
for (auto& mod : fmt.modifiers) {
auto format = std::make_pair<>(fmt.drmFormat, mod);
auto [_, inserted] = formats.insert(format);
if (inserted) {
// if it was inserted into set, then its unique and will have a new index in vec
rendererTranche.indicies.push_back(i++);
formatsVec.push_back(SDMABUFFormatTableEntry{
.fmt = fmt.drmFormat,
.modifier = mod,
});
} else {
// if it wasn't inserted then find its index in vec
auto it =
std::find_if(formatsVec.begin(), formatsVec.end(), [fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; });
rendererTranche.indicies.push_back(it - formatsVec.begin());
}
}
}
tableLen = formats.size() * sizeof(SDMABUFFeedbackTableEntry);
int fds[2] = {0};
allocateSHMFilePair(tableLen, &fds[0], &fds[1]);
for (auto& [monitor, tranche] : monitorTranches) {
tranche.indicies.clear();
for (auto& fmt : tranche.formats) {
for (auto& mod : fmt.modifiers) {
// apparently these can implode on planes, so dont use them
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
continue;
auto format = std::make_pair<>(fmt.drmFormat, mod);
auto [_, inserted] = formats.insert(format);
if (inserted) {
tranche.indicies.push_back(i++);
formatsVec.push_back(SDMABUFFormatTableEntry{
.fmt = fmt.drmFormat,
.modifier = mod,
});
} else {
auto it = std::find_if(formatsVec.begin(), formatsVec.end(),
[fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; });
tranche.indicies.push_back(it - formatsVec.begin());
}
}
}
}
auto arr = (SDMABUFFeedbackTableEntry*)mmap(nullptr, tableLen, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
tableSize = formatsVec.size() * sizeof(SDMABUFFormatTableEntry);
int fds[2] = {0};
allocateSHMFilePair(tableSize, &fds[0], &fds[1]);
auto arr = (SDMABUFFormatTableEntry*)mmap(nullptr, tableSize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
if (arr == MAP_FAILED) {
LOGM(ERR, "mmap failed");
@ -47,33 +93,18 @@ CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMAB
close(fds[0]);
std::vector<std::pair<uint32_t, uint64_t>> formatsVec;
for (auto& f : formats) {
formatsVec.push_back(f);
}
std::copy(formatsVec.begin(), formatsVec.end(), arr);
size_t i = 0;
for (auto& [fmt, mod] : formatsVec) {
arr[i++] = SDMABUFFeedbackTableEntry{
.fmt = fmt,
.modifier = mod,
};
}
munmap(arr, tableSize);
munmap(arr, tableLen);
mainDevice = device;
tableFD = fds[1];
tranches = formatsVec;
// TODO: maybe calculate indices? currently we send all as available which could be wrong? I ain't no kernel dev tho.
tableFD = fds[1];
}
CCompiledDMABUFFeedback::~CCompiledDMABUFFeedback() {
CDMABUFFormatTable::~CDMABUFFormatTable() {
close(tableFD);
}
CLinuxDMABuffer::CLinuxDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs) {
CLinuxDMABuffer::CLinuxDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs) {
buffer = makeShared<CDMABuffer>(id, client, attrs);
buffer->resource->buffer = buffer;
@ -103,7 +134,7 @@ CLinuxDMABBUFParamsResource::CLinuxDMABBUFParamsResource(SP<CZwpLinuxBufferParam
resource->setOnDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); });
resource->setDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); });
attrs = makeShared<SDMABUFAttrs>();
attrs = makeShared<Aquamarine::SDMABUFAttrs>();
attrs->success = true;
@ -190,7 +221,7 @@ void CLinuxDMABBUFParamsResource::create(uint32_t id) {
return;
}
LOGM(LOG, "Creating a dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs->size, attrs->format, attrs->planes);
LOGM(LOG, "Creating a dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs->size, FormatUtils::drmFormatName(attrs->format), attrs->planes);
for (int i = 0; i < attrs->planes; ++i) {
LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs->modifier, attrs->fds[i], attrs->strides[i], attrs->offsets[i]);
}
@ -277,32 +308,9 @@ CLinuxDMABUFFeedbackResource::CLinuxDMABUFFeedbackResource(SP<CZwpLinuxDmabufFee
resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
if (surface)
LOGM(ERR, "FIXME: surface feedback stub");
auto* feedback = PROTO::linuxDma->defaultFeedback.get();
resource->sendFormatTable(feedback->tableFD, feedback->tableLen);
// send default feedback
struct wl_array deviceArr = {
.size = sizeof(feedback->mainDevice),
.data = (void*)&feedback->mainDevice,
};
resource->sendMainDevice(&deviceArr);
resource->sendTrancheTargetDevice(&deviceArr);
resource->sendTrancheFlags((zwpLinuxDmabufFeedbackV1TrancheFlags)0);
wl_array indices;
wl_array_init(&indices);
for (size_t i = 0; i < feedback->tranches.size(); ++i) {
*((uint16_t*)wl_array_add(&indices, sizeof(uint16_t))) = i;
}
resource->sendTrancheFormats(&indices);
wl_array_release(&indices);
resource->sendTrancheDone();
resource->sendDone();
auto& formatTable = PROTO::linuxDma->formatTable;
resource->sendFormatTable(formatTable->tableFD, formatTable->tableSize);
sendDefaultFeedback();
}
CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() {
@ -313,6 +321,41 @@ bool CLinuxDMABUFFeedbackResource::good() {
return resource->resource();
}
void CLinuxDMABUFFeedbackResource::sendTranche(SDMABUFTranche& tranche) {
struct wl_array deviceArr = {
.size = sizeof(tranche.device),
.data = (void*)&tranche.device,
};
resource->sendTrancheTargetDevice(&deviceArr);
resource->sendTrancheFlags((zwpLinuxDmabufFeedbackV1TrancheFlags)tranche.flags);
wl_array indices = {
.size = tranche.indicies.size() * sizeof(tranche.indicies.at(0)),
.data = tranche.indicies.data(),
};
resource->sendTrancheFormats(&indices);
resource->sendTrancheDone();
}
// default tranche is based on renderer (egl)
void CLinuxDMABUFFeedbackResource::sendDefaultFeedback() {
auto mainDevice = PROTO::linuxDma->mainDevice;
auto& formatTable = PROTO::linuxDma->formatTable;
struct wl_array deviceArr = {
.size = sizeof(mainDevice),
.data = (void*)&mainDevice,
};
resource->sendMainDevice(&deviceArr);
sendTranche(formatTable->rendererTranche);
resource->sendDone();
lastFeedbackWasScanout = false;
}
CLinuxDMABUFResource::CLinuxDMABUFResource(SP<CZwpLinuxDmabufV1> resource_) : resource(resource_) {
if (!good())
return;
@ -361,48 +404,81 @@ bool CLinuxDMABUFResource::good() {
}
void CLinuxDMABUFResource::sendMods() {
for (auto& [fmt, mod] : PROTO::linuxDma->defaultFeedback->tranches) {
if (resource->version() < 3) {
if (mod == DRM_FORMAT_MOD_INVALID)
resource->sendFormat(fmt);
continue;
for (auto& fmt : PROTO::linuxDma->formatTable->rendererTranche.formats) {
for (auto& mod : fmt.modifiers) {
if (resource->version() < 3) {
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
resource->sendFormat(fmt.drmFormat);
continue;
}
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
resource->sendModifier(fmt.drmFormat, mod >> 32, mod & 0xFFFFFFFF);
}
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
resource->sendModifier(fmt, mod >> 32, mod & 0xFFFFFFFF);
}
}
CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
static auto P = g_pHookSystem->hookDynamic("ready", [this](void* self, SCallbackInfo& info, std::any d) {
int rendererFD = wlr_renderer_get_drm_fd(g_pCompositor->m_sWLRRenderer);
int rendererFD = g_pCompositor->m_iDRMFD;
auto dev = devIDFromFD(rendererFD);
if (!dev.has_value()) {
LOGM(ERR, "failed to get drm dev");
PROTO::linuxDma.reset();
protoLog(ERR, "failed to get drm dev, disabling linux dmabuf");
removeGlobal();
return;
}
mainDevice = *dev;
auto fmts = g_pHyprOpenGL->getDRMFormats();
SDMABufTranche tranche = {
.device = *dev,
.formats = fmts,
SDMABUFTranche eglTranche = {
.device = mainDevice,
.flags = 0, // renderer isnt for ds so dont set flag.
.formats = g_pHyprOpenGL->getDRMFormats(),
};
std::vector<SDMABufTranche> tches;
tches.push_back(tranche);
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tches;
defaultFeedback = std::make_unique<CCompiledDMABUFFeedback>(*dev, tches);
if (g_pCompositor->m_pAqBackend->hasSession()) {
// this assumes there's only 1 device used for both scanout and rendering
// also that each monitor never changes its primary plane
for (auto& mon : g_pCompositor->m_vMonitors) {
auto tranche = SDMABUFTranche{
.device = mainDevice,
.flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
.formats = mon->output->getRenderFormats(),
};
tches.push_back(std::make_pair<>(mon, tranche));
}
static auto monitorAdded = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
auto pMonitor = std::any_cast<CMonitor*>(param);
auto mon = pMonitor->self.lock();
auto tranche = SDMABUFTranche{
.device = mainDevice,
.flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
.formats = mon->output->getRenderFormats(),
};
formatTable->monitorTranches.push_back(std::make_pair<>(mon, tranche));
resetFormatTable();
});
static auto monitorRemoved = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) {
auto pMonitor = std::any_cast<CMonitor*>(param);
auto mon = pMonitor->self.lock();
std::erase_if(formatTable->monitorTranches, [mon](std::pair<SP<CMonitor>, SDMABUFTranche> pair) { return pair.first == mon; });
resetFormatTable();
});
}
formatTable = std::make_unique<CDMABUFFormatTable>(eglTranche, tches);
drmDevice* device = nullptr;
if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) {
LOGM(ERR, "failed to get drm dev");
PROTO::linuxDma.reset();
protoLog(ERR, "failed to get drm dev, disabling linux dmabuf");
removeGlobal();
return;
}
@ -411,17 +487,51 @@ CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const
mainDeviceFD = open(name, O_RDWR | O_CLOEXEC);
drmFreeDevice(&device);
if (mainDeviceFD < 0) {
LOGM(ERR, "failed to open drm dev");
PROTO::linuxDma.reset();
protoLog(ERR, "failed to open drm dev, disabling linux dmabuf");
removeGlobal();
return;
}
} else {
LOGM(ERR, "DRM device {} has no render node!!", device->nodes[DRM_NODE_PRIMARY]);
protoLog(ERR, "DRM device {} has no render node, disabling linux dmabuf", device->nodes[DRM_NODE_PRIMARY] ? device->nodes[DRM_NODE_PRIMARY] : "null");
drmFreeDevice(&device);
removeGlobal();
}
});
}
void CLinuxDMABufV1Protocol::resetFormatTable() {
if (!formatTable)
return;
LOGM(LOG, "Resetting format table");
// this might be a big copy
auto newFormatTable = std::make_unique<CDMABUFFormatTable>(formatTable->rendererTranche, formatTable->monitorTranches);
for (auto& feedback : m_vFeedbacks) {
feedback->resource->sendFormatTable(newFormatTable->tableFD, newFormatTable->tableSize);
if (feedback->lastFeedbackWasScanout) {
SP<CMonitor> mon;
auto HLSurface = CWLSurface::fromResource(feedback->surface);
if (auto w = HLSurface->getWindow(); w)
if (auto m = g_pCompositor->getMonitorFromID(w->m_iMonitorID); m)
mon = m->self.lock();
if (!mon) {
feedback->sendDefaultFeedback();
return;
}
updateScanoutTranche(feedback->surface, mon);
} else {
feedback->sendDefaultFeedback();
}
}
// delete old table after we sent new one
formatTable = std::move(newFormatTable);
}
CLinuxDMABufV1Protocol::~CLinuxDMABufV1Protocol() {
if (mainDeviceFD >= 0)
close(mainDeviceFD);
@ -452,3 +562,52 @@ void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABBUFParamsResource* resour
void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) {
std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; });
}
void CLinuxDMABufV1Protocol::updateScanoutTranche(SP<CWLSurfaceResource> surface, SP<CMonitor> pMonitor) {
SP<CLinuxDMABUFFeedbackResource> feedbackResource;
for (auto& f : m_vFeedbacks) {
if (f->surface != surface)
continue;
feedbackResource = f;
break;
}
if (!feedbackResource) {
LOGM(LOG, "updateScanoutTranche: surface has no dmabuf_feedback");
return;
}
if (!pMonitor) {
LOGM(LOG, "updateScanoutTranche: resetting feedback");
feedbackResource->sendDefaultFeedback();
return;
}
const auto& monitorTranchePair = std::find_if(formatTable->monitorTranches.begin(), formatTable->monitorTranches.end(),
[pMonitor](std::pair<SP<CMonitor>, SDMABUFTranche> pair) { return pair.first == pMonitor; });
if (monitorTranchePair == formatTable->monitorTranches.end()) {
LOGM(LOG, "updateScanoutTranche: monitor has no tranche");
return;
}
auto& monitorTranche = (*monitorTranchePair).second;
LOGM(LOG, "updateScanoutTranche: sending a scanout tranche");
struct wl_array deviceArr = {
.size = sizeof(mainDevice),
.data = (void*)&mainDevice,
};
feedbackResource->resource->sendMainDevice(&deviceArr);
// prioritize scnaout tranche but have renderer fallback tranche
// also yes formats can be duped here because different tranche flags (ds and no ds)
feedbackResource->sendTranche(monitorTranche);
feedbackResource->sendTranche(formatTable->rendererTranche);
feedbackResource->resource->sendDone();
feedbackResource->lastFeedbackWasScanout = true;
}

View file

@ -7,15 +7,16 @@
#include "wayland.hpp"
#include "linux-dmabuf-v1.hpp"
#include "../helpers/signal/Signal.hpp"
#include "../helpers/Format.hpp"
#include "../helpers/Monitor.hpp"
#include <aquamarine/buffer/Buffer.hpp>
class CDMABuffer;
struct SDRMFormat;
struct SDMABUFAttrs;
class CWLSurfaceResource;
class CLinuxDMABuffer {
public:
CLinuxDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs);
CLinuxDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs);
~CLinuxDMABuffer();
bool good();
@ -31,34 +32,29 @@ class CLinuxDMABuffer {
};
#pragma pack(push, 1)
struct SDMABUFFeedbackTableEntry {
struct SDMABUFFormatTableEntry {
uint32_t fmt = 0;
char pad[4];
uint64_t modifier = 0;
};
#pragma pack(pop)
class SCompiledDMABUFTranche {
dev_t device = 0;
uint32_t flags = 0;
std::vector<uint16_t> indices;
};
struct SDMABufTranche {
struct SDMABUFTranche {
dev_t device = 0;
uint32_t flags = 0;
std::vector<SDRMFormat> formats;
std::vector<uint16_t> indicies;
};
class CCompiledDMABUFFeedback {
class CDMABUFFormatTable {
public:
CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches);
~CCompiledDMABUFFeedback();
CDMABUFFormatTable(SDMABUFTranche rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches);
~CDMABUFFormatTable();
dev_t mainDevice = 0;
int tableFD = -1;
size_t tableLen = 0;
std::vector<std::pair<uint32_t, uint64_t>> tranches;
int tableFD = -1;
size_t tableSize = 0;
SDMABUFTranche rendererTranche;
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> monitorTranches;
};
class CLinuxDMABBUFParamsResource {
@ -66,12 +62,12 @@ class CLinuxDMABBUFParamsResource {
CLinuxDMABBUFParamsResource(SP<CZwpLinuxBufferParamsV1> resource_);
~CLinuxDMABBUFParamsResource();
bool good();
void create(uint32_t id); // 0 means not immed
bool good();
void create(uint32_t id); // 0 means not immed
SP<SDMABUFAttrs> attrs;
WP<CLinuxDMABuffer> createdBuffer;
bool used = false;
SP<Aquamarine::SDMABUFAttrs> attrs;
WP<CLinuxDMABuffer> createdBuffer;
bool used = false;
private:
SP<CZwpLinuxBufferParamsV1> resource;
@ -86,11 +82,16 @@ class CLinuxDMABUFFeedbackResource {
~CLinuxDMABUFFeedbackResource();
bool good();
void sendDefaultFeedback();
void sendTranche(SDMABUFTranche& tranche);
SP<CWLSurfaceResource> surface; // optional, for surface feedbacks
private:
SP<CZwpLinuxDmabufFeedbackV1> resource;
bool lastFeedbackWasScanout = false;
friend class CLinuxDMABufV1Protocol;
};
class CLinuxDMABUFResource {
@ -110,6 +111,7 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
~CLinuxDMABufV1Protocol();
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
void updateScanoutTranche(SP<CWLSurfaceResource> surface, SP<CMonitor> pMonitor);
private:
void destroyResource(CLinuxDMABUFResource* resource);
@ -117,13 +119,15 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
void destroyResource(CLinuxDMABBUFParamsResource* resource);
void destroyResource(CLinuxDMABuffer* resource);
void resetFormatTable();
//
std::vector<SP<CLinuxDMABUFResource>> m_vManagers;
std::vector<SP<CLinuxDMABUFFeedbackResource>> m_vFeedbacks;
std::vector<SP<CLinuxDMABBUFParamsResource>> m_vParams;
std::vector<SP<CLinuxDMABuffer>> m_vBuffers;
UP<CCompiledDMABUFFeedback> defaultFeedback;
UP<CDMABUFFormatTable> formatTable;
dev_t mainDevice;
int mainDeviceFD = -1;

View file

@ -2,12 +2,11 @@
#include <algorithm>
#include <xf86drm.h>
#include "../Compositor.hpp"
#include <wlr/render/drm_format_set.h>
#include "types/WLBuffer.hpp"
#define LOGM PROTO::mesaDRM->protoLog
CMesaDRMBufferResource::CMesaDRMBufferResource(uint32_t id, wl_client* client, SDMABUFAttrs attrs_) {
CMesaDRMBufferResource::CMesaDRMBufferResource(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs_) {
LOGM(LOG, "Creating a Mesa dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs_.size, attrs_.format, attrs_.planes);
for (int i = 0; i < attrs_.planes; ++i) {
LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs_.modifier, attrs_.fds[i], attrs_.strides[i], attrs_.offsets[i]);
@ -60,10 +59,27 @@ CMesaDRMResource::CMesaDRMResource(SP<CWlDrm> resource_) : resource(resource_) {
return;
}
SDMABUFAttrs attrs;
uint64_t mod = DRM_FORMAT_MOD_INVALID;
auto fmts = g_pHyprOpenGL->getDRMFormats();
for (auto& f : fmts) {
if (f.drmFormat != fmt)
continue;
for (auto& m : f.modifiers) {
if (m == DRM_FORMAT_MOD_LINEAR)
continue;
mod = m;
break;
}
break;
}
Aquamarine::SDMABUFAttrs attrs;
attrs.success = true;
attrs.size = {w, h};
attrs.modifier = DRM_FORMAT_MOD_INVALID;
attrs.modifier = mod;
attrs.planes = 1;
attrs.offsets[0] = off0;
attrs.strides[0] = str0;
@ -87,7 +103,7 @@ CMesaDRMResource::CMesaDRMResource(SP<CWlDrm> resource_) : resource(resource_) {
auto fmts = g_pHyprOpenGL->getDRMFormats();
for (auto& fmt : fmts) {
resource->sendFormat(fmt.format);
resource->sendFormat(fmt.drmFormat);
}
}
@ -97,10 +113,10 @@ bool CMesaDRMResource::good() {
CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
drmDevice* dev = nullptr;
int drmFD = wlr_renderer_get_drm_fd(g_pCompositor->m_sWLRRenderer);
int drmFD = g_pCompositor->m_iDRMFD;
if (drmGetDevice2(drmFD, 0, &dev) != 0) {
LOGM(ERR, "Failed to get device");
PROTO::mesaDRM.reset();
protoLog(ERR, "Failed to get device, disabling MesaDRM");
removeGlobal();
return;
}
@ -108,7 +124,15 @@ CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, co
nodeName = dev->nodes[DRM_NODE_RENDER];
} else {
ASSERT(dev->available_nodes & (1 << DRM_NODE_PRIMARY));
LOGM(WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]);
if (!dev->nodes[DRM_NODE_PRIMARY]) {
protoLog(ERR, "No DRM render node available, both render and primary are null, disabling MesaDRM");
drmFreeDevice(&dev);
removeGlobal();
return;
}
protoLog(WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]);
nodeName = dev->nodes[DRM_NODE_PRIMARY];
}
drmFreeDevice(&dev);

View file

@ -10,7 +10,7 @@
class CMesaDRMBufferResource {
public:
CMesaDRMBufferResource(uint32_t id, wl_client* client, SDMABUFAttrs attrs);
CMesaDRMBufferResource(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs);
~CMesaDRMBufferResource();
bool good();

View file

@ -2,6 +2,8 @@
#include <algorithm>
#include "../Compositor.hpp"
using namespace Aquamarine;
#define LOGM PROTO::outputManagement->protoLog
COutputManager::COutputManager(SP<CZwlrOutputManagerV1> resource_) : resource(resource_) {
@ -123,8 +125,8 @@ void COutputHead::sendAllData() {
resource->sendName(pMonitor->szName.c_str());
resource->sendDescription(pMonitor->szDescription.c_str());
if (pMonitor->output->phys_width > 0 && pMonitor->output->phys_height > 0)
resource->sendPhysicalSize(pMonitor->output->phys_width, pMonitor->output->phys_height);
if (pMonitor->output->physicalSize.x > 0 && pMonitor->output->physicalSize.y > 0)
resource->sendPhysicalSize(pMonitor->output->physicalSize.x, pMonitor->output->physicalSize.y);
resource->sendEnabled(pMonitor->m_bEnabled);
if (pMonitor->m_bEnabled) {
@ -133,12 +135,12 @@ void COutputHead::sendAllData() {
resource->sendScale(wl_fixed_from_double(pMonitor->scale));
}
if (pMonitor->output->make && VERSION >= 2)
resource->sendMake(pMonitor->output->make);
if (pMonitor->output->model && VERSION >= 2)
resource->sendModel(pMonitor->output->model);
if (pMonitor->output->serial && VERSION >= 2)
resource->sendSerialNumber(pMonitor->output->serial);
if (!pMonitor->output->make.empty() && VERSION >= 2)
resource->sendMake(pMonitor->output->make.c_str());
if (!pMonitor->output->model.empty() && VERSION >= 2)
resource->sendModel(pMonitor->output->model.c_str());
if (!pMonitor->output->serial.empty() && VERSION >= 2)
resource->sendSerialNumber(pMonitor->output->serial.c_str());
if (VERSION >= 4)
resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED);
@ -146,12 +148,12 @@ void COutputHead::sendAllData() {
// send all available modes
if (modes.empty()) {
if (!wl_list_empty(&pMonitor->output->modes)) {
wlr_output_mode* mode;
wl_list_for_each(mode, &pMonitor->output->modes, link) {
makeAndSendNewMode(mode);
if (!pMonitor->output->modes.empty()) {
for (auto& m : pMonitor->output->modes) {
makeAndSendNewMode(m);
}
} else if (pMonitor->output->state->state().customMode) {
makeAndSendNewMode(pMonitor->output->state->state().customMode);
} else
makeAndSendNewMode(nullptr);
}
@ -164,9 +166,9 @@ void COutputHead::sendAllData() {
if (!m)
continue;
if (m->mode == pMonitor->currentMode) {
if (m->mode == pMonitor->output->state->state().mode) {
if (m->mode)
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh);
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->pixelSize.x, m->mode->pixelSize.y, m->mode->refreshRate);
else
LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName);
resource->sendCurrentMode(m->resource.get());
@ -197,7 +199,7 @@ void COutputHead::updateMode() {
if (m->mode == pMonitor->currentMode) {
if (m->mode)
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh);
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->pixelSize.x, m->mode->pixelSize.y, m->mode->refreshRate);
else
LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName);
resource->sendCurrentMode(m->resource.get());
@ -207,7 +209,7 @@ void COutputHead::updateMode() {
}
}
void COutputHead::makeAndSendNewMode(wlr_output_mode* mode) {
void COutputHead::makeAndSendNewMode(SP<Aquamarine::SOutputMode> mode) {
const auto RESOURCE = PROTO::outputManagement->m_vModes.emplace_back(makeShared<COutputMode>(makeShared<CZwlrOutputModeV1>(resource->client(), resource->version(), 0), mode));
if (!RESOURCE->good()) {
@ -225,7 +227,7 @@ CMonitor* COutputHead::monitor() {
return pMonitor;
}
COutputMode::COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_) : resource(resource_), mode(mode_) {
COutputMode::COutputMode(SP<CZwlrOutputModeV1> resource_, SP<Aquamarine::SOutputMode> mode_) : resource(resource_), mode(mode_) {
if (!good())
return;
@ -237,11 +239,11 @@ void COutputMode::sendAllData() {
if (!mode)
return;
LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", mode->width, mode->height, mode->refresh, mode->preferred);
LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", mode->pixelSize.x, mode->pixelSize.y, mode->refreshRate, mode->preferred);
resource->sendSize(mode->width, mode->height);
if (mode->refresh > 0)
resource->sendRefresh(mode->refresh);
resource->sendSize(mode->pixelSize.x, mode->pixelSize.y);
if (mode->refreshRate > 0)
resource->sendRefresh(mode->refreshRate);
if (mode->preferred)
resource->sendPreferred();
}
@ -250,8 +252,8 @@ bool COutputMode::good() {
return resource->resource();
}
wlr_output_mode* COutputMode::getMode() {
return mode;
SP<Aquamarine::SOutputMode> COutputMode::getMode() {
return mode.lock();
}
COutputConfiguration::COutputConfiguration(SP<CZwlrOutputConfigurationV1> resource_, SP<COutputManager> owner_) : resource(resource_), owner(owner_) {
@ -364,8 +366,8 @@ bool COutputConfiguration::applyTestConfiguration(bool test) {
newRule.disabled = false;
if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) {
newRule.resolution = {head->state.mode->getMode()->width, head->state.mode->getMode()->height};
newRule.refreshRate = head->state.mode->getMode()->refresh / 1000.F;
newRule.resolution = head->state.mode->getMode()->pixelSize;
newRule.refreshRate = head->state.mode->getMode()->refreshRate / 1000.F;
} else if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) {
newRule.resolution = head->state.customMode.size;
newRule.refreshRate = head->state.customMode.refresh / 1000.F;
@ -425,7 +427,7 @@ COutputConfigurationHead::COutputConfigurationHead(SP<CZwlrOutputConfigurationHe
committedProperties |= OUTPUT_HEAD_COMMITTED_MODE;
state.mode = MODE;
LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->width, MODE->getMode()->height, MODE->getMode()->refresh);
LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->pixelSize.x, MODE->getMode()->pixelSize.y, MODE->getMode()->refreshRate);
});
resource->setSetCustomMode([this](CZwlrOutputConfigurationHeadV1* r, int32_t w, int32_t h, int32_t refresh) {

View file

@ -6,6 +6,7 @@
#include "WaylandProtocol.hpp"
#include "wlr-output-management-unstable-v1.hpp"
#include "../helpers/signal/Signal.hpp"
#include <aquamarine/output/Output.hpp>
class CMonitor;
@ -34,15 +35,15 @@ class COutputManager {
class COutputMode {
public:
COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_);
COutputMode(SP<CZwlrOutputModeV1> resource_, SP<Aquamarine::SOutputMode> mode_);
bool good();
wlr_output_mode* getMode();
void sendAllData();
bool good();
SP<Aquamarine::SOutputMode> getMode();
void sendAllData();
private:
SP<CZwlrOutputModeV1> resource;
wlr_output_mode* mode = nullptr;
SP<CZwlrOutputModeV1> resource;
WP<Aquamarine::SOutputMode> mode;
friend class COutputHead;
friend class COutputManagementProtocol;
@ -61,7 +62,7 @@ class COutputHead {
SP<CZwlrOutputHeadV1> resource;
CMonitor* pMonitor = nullptr;
void makeAndSendNewMode(wlr_output_mode* mode);
void makeAndSendNewMode(SP<Aquamarine::SOutputMode> mode);
void sendCurrentMode();
std::vector<WP<COutputMode>> modes;

View file

@ -17,7 +17,7 @@ COutputPower::COutputPower(SP<CZwlrOutputPowerV1> resource_, CMonitor* pMonitor_
pMonitor->dpmsStatus = mode == ZWLR_OUTPUT_POWER_V1_MODE_ON;
wlr_output_state_set_enabled(pMonitor->state.wlr(), pMonitor->dpmsStatus);
pMonitor->output->state->setEnabled(mode == ZWLR_OUTPUT_POWER_V1_MODE_ON);
if (!pMonitor->state.commit())
LOGM(ERR, "Couldn't set dpms to {} for {}", pMonitor->dpmsStatus, pMonitor->szName);

View file

@ -3,6 +3,8 @@
#include "../helpers/Monitor.hpp"
#include "../managers/HookSystemManager.hpp"
#include "core/Compositor.hpp"
#include "core/Output.hpp"
#include <aquamarine/output/Output.hpp>
#define LOGM PROTO::presentation->protoLog
@ -41,13 +43,11 @@ bool CPresentationFeedback::good() {
}
void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) {
auto client = resource->client();
wl_resource* res;
wl_resource_for_each(res, &data->pMonitor->output->resources) {
if (client == wl_resource_get_client(res)) {
resource->sendSyncOutput(res);
break;
}
auto client = resource->client();
if (PROTO::outputs.contains(data->pMonitor->szName)) {
if (auto outputResource = PROTO::outputs.at(data->pMonitor->szName)->outputResourceFrom(client); outputResource)
resource->sendSyncOutput(outputResource->getResource()->resource());
}
uint32_t flags = 0;
@ -55,12 +55,12 @@ void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespe
flags |= WP_PRESENTATION_FEEDBACK_KIND_VSYNC;
if (data->zeroCopy)
flags |= WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
if (reportedFlags & WLR_OUTPUT_PRESENT_HW_CLOCK)
if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK)
flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
if (reportedFlags & WLR_OUTPUT_PRESENT_HW_COMPLETION)
if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION)
flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION;
if (data->wasPresented)
if (data->wasPresented && when)
resource->sendPresented((uint32_t)(when->tv_sec >> 32), (uint32_t)(when->tv_sec & 0xFFFFFFFF), (uint32_t)(when->tv_nsec), untilRefreshNs, (uint32_t)(seq >> 32),
(uint32_t)(seq & 0xFFFFFFFF), (wpPresentationFeedbackKind)flags);
else

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